In Etsy’s data model, users and shops are distinct but related entities. A user account can have one shop, and all listings, orders, and shop settings belong to that shop. Understanding this relationship is fundamental to building effective API integrations.
After authentication, you’ll often need to know which user is connected:
# etsy_client/users.py
"""User-related API operations."""
from typing import Dict, Any, Optional
from .client import EtsyClient
class UserOperations:
"""Operations for Etsy users."""
def __init__(self, client: EtsyClient):
self.client = client
def get_current_user(self) -> Dict[str, Any]:
"""
Get the currently authenticated user.
Returns:
User data including user_id, login_name, and shop details
"""
return self.client.get("/application/users/me")
def get_user_by_id(self, user_id: int) -> Dict[str, Any]:
"""
Get a specific user by ID.
Args:
user_id: The Etsy user ID
Returns:
User data
"""
return self.client.get(f"/application/users/{user_id}")
def get_user_shops(self, user_id: int) -> Dict[str, Any]:
"""
Get all shops belonging to a user.
Args:
user_id: The Etsy user ID
Returns:
List of shops
"""
return self.client.get(f"/application/users/{user_id}/shops")
# Example usage
def get_my_shop_id(client: EtsyClient) -> int:
"""
Convenience function to get the current user's shop ID.
Args:
client: Authenticated EtsyClient
Returns:
Shop ID
Raises:
ValueError: If user has no shop
"""
user_ops = UserOperations(client)
user = user_ops.get_current_user()
# Check if user has a shop
if 'shop_id' not in user or not user['shop_id']:
raise ValueError("Authenticated user does not have a shop")
return user['shop_id']
The user endpoint returns data like:
{
"user_id": 123456789,
"login_name": "YourShopName",
"primary_email": "email@example.com",
"create_timestamp": 1609459200,
"shop_id": 987654321,
"is_seller": true
}
# etsy_client/shops.py
"""Shop-related API operations."""
from typing import Dict, Any, Optional, List
from datetime import datetime
from .client import EtsyClient
class ShopOperations:
"""Operations for Etsy shops."""
def __init__(self, client: EtsyClient):
self.client = client
def get_shop(self, shop_id: int) -> Dict[str, Any]:
"""
Get detailed shop information.
Args:
shop_id: The Etsy shop ID
Returns:
Shop data including name, settings, and statistics
"""
return self.client.get(f"/application/shops/{shop_id}")
def get_shop_by_name(self, shop_name: str) -> Dict[str, Any]:
"""
Find a shop by its name.
Args:
shop_name: The shop's display name
Returns:
Shop data
"""
return self.client.get(f"/application/shops/{shop_name}")
def update_shop(
self,
shop_id: int,
title: Optional[str] = None,
announcement: Optional[str] = None,
sale_message: Optional[str] = None,
digital_sale_message: Optional[str] = None,
policy_welcome: Optional[str] = None,
**kwargs
) -> Dict[str, Any]:
"""
Update shop settings.
Args:
shop_id: The shop ID to update
title: Shop title/tagline
announcement: Shop announcement message
sale_message: Message sent after physical product purchase
digital_sale_message: Message sent after digital product purchase
policy_welcome: Welcome message in shop policies
**kwargs: Additional updatable fields
Returns:
Updated shop data
"""
data = {}
if title is not None:
data['title'] = title
if announcement is not None:
data['announcement'] = announcement
if sale_message is not None:
data['sale_message'] = sale_message
if digital_sale_message is not None:
data['digital_sale_message'] = digital_sale_message
if policy_welcome is not None:
data['policy_welcome'] = policy_welcome
data.update(kwargs)
if not data:
raise ValueError("No fields to update")
return self.client.put(f"/application/shops/{shop_id}", json=data)
def get_shop_sections(self, shop_id: int) -> Dict[str, Any]:
"""
Get all sections (categories) in a shop.
Args:
shop_id: The shop ID
Returns:
List of shop sections
"""
return self.client.get(f"/application/shops/{shop_id}/sections")
def create_shop_section(
self,
shop_id: int,
title: str
) -> Dict[str, Any]:
"""
Create a new shop section.
Args:
shop_id: The shop ID
title: Section title
Returns:
Created section data
"""
return self.client.post(
f"/application/shops/{shop_id}/sections",
json={"title": title}
)
def delete_shop_section(
self,
shop_id: int,
section_id: int
) -> Dict[str, Any]:
"""
Delete a shop section.
Args:
shop_id: The shop ID
section_id: The section ID to delete
Returns:
Deletion confirmation
"""
return self.client.delete(
f"/application/shops/{shop_id}/sections/{section_id}"
)
class ShopStatistics:
"""Analyze shop statistics and performance."""
def __init__(self, shop_data: Dict[str, Any]):
"""
Initialize with shop data from API.
Args:
shop_data: Shop data dictionary from get_shop()
"""
self.data = shop_data
@property
def shop_id(self) -> int:
return self.data['shop_id']
@property
def name(self) -> str:
return self.data['shop_name']
@property
def title(self) -> str:
return self.data.get('title', '')
@property
def currency(self) -> str:
return self.data.get('currency_code', 'USD')
@property
def listing_count(self) -> int:
return self.data.get('listing_active_count', 0)
@property
def sale_count(self) -> int:
return self.data.get('transaction_sold_count', 0)
@property
def review_count(self) -> int:
return self.data.get('review_count', 0)
@property
def average_rating(self) -> float:
return self.data.get('review_average', 0.0)
@property
def is_vacation_mode(self) -> bool:
return self.data.get('is_vacation', False)
@property
def created_date(self) -> datetime:
timestamp = self.data.get('create_timestamp', 0)
return datetime.fromtimestamp(timestamp)
def summary(self) -> str:
"""Generate a text summary of shop statistics."""
return f"""
Shop: {self.name}
Title: {self.title}
Currency: {self.currency}
Statistics:
- Active Listings: {self.listing_count}
- Total Sales: {self.sale_count}
- Reviews: {self.review_count}
- Average Rating: {self.average_rating:.2f}/5
Status:
- Vacation Mode: {'Yes' if self.is_vacation_mode else 'No'}
- Created: {self.created_date.strftime('%Y-%m-%d')}
"""
# scripts/shop_info.py
"""Display shop information and statistics."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import Config
from etsy_client import EtsyClient
from etsy_client.shops import ShopOperations, ShopStatistics
from etsy_client.users import get_my_shop_id
def display_shop_info():
"""Fetch and display comprehensive shop information."""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
# Get shop ID
shop_id = get_my_shop_id(client)
print(f"Shop ID: {shop_id}")
# Get shop details
shop_ops = ShopOperations(client)
shop_data = shop_ops.get_shop(shop_id)
# Analyze statistics
stats = ShopStatistics(shop_data)
print(stats.summary())
# Get sections
sections = shop_ops.get_shop_sections(shop_id)
print("\nShop Sections:")
print("-" * 30)
if sections.get('results'):
for section in sections['results']:
count = section.get('active_listing_count', 0)
print(f" - {section['title']} ({count} listings)")
else:
print(" No sections defined")
def update_digital_message(message: str):
"""Update the message sent to customers after digital purchases."""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
shop_ops = ShopOperations(client)
result = shop_ops.update_shop(
shop_id,
digital_sale_message=message
)
print("✓ Digital sale message updated!")
print(f" New message: {result.get('digital_sale_message', '')[:100]}...")
if __name__ == "__main__":
display_shop_info()
Sections help organize your digital products. Here’s how to manage them programmatically:
# scripts/manage_sections.py
"""Manage shop sections for digital product organization."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import Config
from etsy_client import EtsyClient
from etsy_client.shops import ShopOperations
from etsy_client.users import get_my_shop_id
class SectionManager:
"""Manage shop sections for digital products."""
# Common digital product categories
DIGITAL_SECTIONS = [
"Digital Planners",
"Printable Wall Art",
"SVG Cut Files",
"Social Media Templates",
"Resume Templates",
"Canva Templates",
"Digital Stickers",
"Spreadsheet Templates",
"Notion Templates",
"Lightroom Presets"
]
def __init__(self, client: EtsyClient, shop_id: int):
self.client = client
self.shop_id = shop_id
self.shop_ops = ShopOperations(client)
def get_sections(self) -> dict:
"""Get current sections with listing counts."""
return self.shop_ops.get_shop_sections(self.shop_id)
def section_exists(self, title: str) -> bool:
"""Check if a section with given title exists."""
sections = self.get_sections()
for section in sections.get('results', []):
if section['title'].lower() == title.lower():
return True
return False
def create_section(self, title: str) -> dict:
"""Create a section if it doesn't exist."""
if self.section_exists(title):
print(f"Section '{title}' already exists")
return None
result = self.shop_ops.create_shop_section(self.shop_id, title)
print(f"✓ Created section: {title}")
return result
def setup_digital_sections(self, categories: list = None):
"""Set up standard sections for digital products."""
categories = categories or self.DIGITAL_SECTIONS
print("Setting up digital product sections...")
print("-" * 40)
created = 0
for category in categories:
if not self.section_exists(category):
self.create_section(category)
created += 1
else:
print(f" Exists: {category}")
print(f"\nCreated {created} new sections")
def audit_sections(self):
"""Audit sections and report empty ones."""
sections = self.get_sections()
print("\nSection Audit")
print("-" * 40)
empty_sections = []
active_sections = []
for section in sections.get('results', []):
title = section['title']
count = section.get('active_listing_count', 0)
if count == 0:
empty_sections.append(title)
else:
active_sections.append((title, count))
print("\nActive Sections:")
for title, count in sorted(active_sections, key=lambda x: -x[1]):
print(f" {title}: {count} listings")
if empty_sections:
print("\nEmpty Sections (consider removing):")
for title in empty_sections:
print(f" - {title}")
def main():
"""Demo section management."""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
manager = SectionManager(client, shop_id)
# Show current sections
print("Current Sections:")
sections = manager.get_sections()
for section in sections.get('results', []):
print(f" - {section['title']}")
# Audit sections
manager.audit_sections()
if __name__ == "__main__":
main()
Digital product shops have specific settings worth configuring:
# etsy_client/shop_settings.py
"""Shop settings optimized for digital products."""
from typing import Dict, Any, Optional
from .client import EtsyClient
from .shops import ShopOperations
class DigitalShopSettings:
"""Configure shop settings for digital product sales."""
def __init__(self, client: EtsyClient, shop_id: int):
self.client = client
self.shop_id = shop_id
self.shop_ops = ShopOperations(client)
def configure_digital_welcome_message(self) -> Dict[str, Any]:
"""
Set up an optimized welcome message for digital downloads.
Returns:
Updated shop data
"""
message = """Thank you for your purchase! 🎉
📥 HOW TO DOWNLOAD:
1. Go to your Etsy account
2. Click "Purchases and reviews"
3. Click "Download Files" next to your order
💡 TIPS:
- Downloads are available immediately after purchase
- You can download files up to 5 times
- Files never expire from your account
📱 MOBILE USERS:
For the best experience, download on a computer or use a file manager app on mobile.
❓ NEED HELP?
If you have any issues downloading, please message me and I'll help you right away!
Thank you for supporting my shop! ❤️"""
return self.shop_ops.update_shop(
self.shop_id,
digital_sale_message=message
)
def get_policy_settings(self) -> Dict[str, Any]:
"""Get current shop policy settings."""
shop = self.shop_ops.get_shop(self.shop_id)
return {
'policy_welcome': shop.get('policy_welcome', ''),
'policy_payment': shop.get('policy_payment', ''),
'policy_shipping': shop.get('policy_shipping', ''),
'policy_refunds': shop.get('policy_refunds', ''),
'policy_additional': shop.get('policy_additional', ''),
'policy_privacy': shop.get('policy_privacy', ''),
}
def configure_digital_policies(
self,
custom_refund_policy: Optional[str] = None
) -> Dict[str, Any]:
"""
Configure policies appropriate for digital products.
Args:
custom_refund_policy: Override the default refund policy
Returns:
Updated shop data
"""
default_refund = """Due to the digital nature of our products, all sales are final.
We cannot offer refunds once files have been downloaded.
However, we stand behind our products! If you experience technical issues:
1. Contact us with details about the problem
2. Include screenshots if possible
3. We'll work to resolve the issue promptly
If files are corrupted or significantly different from the listing description,
we'll either provide working files or issue a refund at our discretion."""
updates = {
'policy_welcome': """Welcome to our digital products shop!
All items are instant downloads - no physical products will be shipped.""",
'policy_refunds': custom_refund_policy or default_refund,
}
return self.shop_ops.update_shop(self.shop_id, **updates)
def setup_digital_shop(client: EtsyClient, shop_id: int):
"""
One-time setup for a digital products shop.
Args:
client: Authenticated EtsyClient
shop_id: Shop to configure
"""
settings = DigitalShopSettings(client, shop_id)
print("Configuring digital shop settings...")
print("-" * 40)
# Set up welcome message
print("Setting digital sale message...")
settings.configure_digital_welcome_message()
print("✓ Digital sale message configured")
# Configure policies
print("Setting digital product policies...")
settings.configure_digital_policies()
print("✓ Policies configured")
print("\n✓ Shop configured for digital products!")
# etsy_client/shop_health.py
"""Monitor shop health and performance metrics."""
from typing import Dict, Any, List
from datetime import datetime, timedelta
from .client import EtsyClient
from .shops import ShopOperations
class ShopHealthMonitor:
"""Monitor and report on shop health metrics."""
def __init__(self, client: EtsyClient, shop_id: int):
self.client = client
self.shop_id = shop_id
self.shop_ops = ShopOperations(client)
def get_health_report(self) -> Dict[str, Any]:
"""
Generate a comprehensive health report.
Returns:
Dictionary with health metrics and recommendations
"""
shop = self.shop_ops.get_shop(self.shop_id)
# Collect metrics
metrics = {
'shop_name': shop['shop_name'],
'active_listings': shop.get('listing_active_count', 0),
'total_sales': shop.get('transaction_sold_count', 0),
'review_count': shop.get('review_count', 0),
'average_rating': shop.get('review_average', 0),
'favorites': shop.get('num_favorers', 0),
'vacation_mode': shop.get('is_vacation', False),
}
# Calculate health scores
issues = []
recommendations = []
# Check listing count
if metrics['active_listings'] < 10:
issues.append("Low listing count")
recommendations.append(
"Add more listings to increase visibility. "
"Shops with 50+ listings tend to perform better."
)
# Check reviews
if metrics['review_count'] > 0:
if metrics['average_rating'] < 4.5:
issues.append("Below-average rating")
recommendations.append(
"Review recent negative feedback and address common issues."
)
else:
recommendations.append(
"Encourage customers to leave reviews after purchase."
)
# Check vacation mode
if metrics['vacation_mode']:
issues.append("Vacation mode is ON")
recommendations.append(
"Your shop is not visible to buyers while in vacation mode."
)
# Calculate overall health
health_score = 100
health_score -= len(issues) * 10
if metrics['active_listings'] >= 50:
health_score += 10
if metrics['average_rating'] >= 4.8:
health_score += 10
health_score = max(0, min(100, health_score))
return {
'metrics': metrics,
'health_score': health_score,
'issues': issues,
'recommendations': recommendations,
'generated_at': datetime.now().isoformat()
}
def print_report(self):
"""Print a formatted health report."""
report = self.get_health_report()
print("\n" + "=" * 50)
print(f"Shop Health Report: {report['metrics']['shop_name']}")
print("=" * 50)
print(f"\n📊 Health Score: {report['health_score']}/100")
print("\n📈 Metrics:")
metrics = report['metrics']
print(f" Active Listings: {metrics['active_listings']}")
print(f" Total Sales: {metrics['total_sales']}")
print(f" Reviews: {metrics['review_count']}")
print(f" Average Rating: {metrics['average_rating']:.2f}/5")
print(f" Favorites: {metrics['favorites']}")
if report['issues']:
print("\n⚠️ Issues Found:")
for issue in report['issues']:
print(f" - {issue}")
if report['recommendations']:
print("\n💡 Recommendations:")
for i, rec in enumerate(report['recommendations'], 1):
print(f" {i}. {rec}")
print(f"\nGenerated: {report['generated_at']}")
Update the main client to include shop operations:
# etsy_client/client.py - Add to existing EtsyClient class
class EtsyClient:
# ... existing code ...
@property
def shops(self):
"""Access shop operations."""
from .shops import ShopOperations
if not hasattr(self, '_shop_ops'):
self._shop_ops = ShopOperations(self)
return self._shop_ops
@property
def users(self):
"""Access user operations."""
from .users import UserOperations
if not hasattr(self, '_user_ops'):
self._user_ops = UserOperations(self)
return self._user_ops
def get_my_shop(self) -> Dict[str, Any]:
"""Convenience method to get the authenticated user's shop."""
user = self.get_me()
if 'shop_id' not in user:
raise ValueError("User does not have a shop")
return self.shops.get_shop(user['shop_id'])
With shop and user management in place, you’re ready to work with the core of your digital products business: listings. The next chapter covers creating, updating, and managing listings through the API.
| ← Chapter 3: Authentication and Authorization | Table of Contents | Chapter 5: Managing Listings → |