Before writing code, understanding how Etsy’s API is structured helps you navigate its capabilities effectively. The Etsy API v3 follows RESTful principles, organizing resources hierarchically around your shop’s data model.
Resources: Everything in Etsy is a resource—shops, listings, users, orders, receipts. Each resource has a unique identifier and can be accessed, created, updated, or deleted through API endpoints.
Endpoints: URLs that represent specific resources or collections. For example:
/v3/application/shops/{shop_id} - A specific shop/v3/application/shops/{shop_id}/listings - All listings in a shop/v3/application/listings/{listing_id} - A specific listingHTTP Methods: Standard HTTP verbs indicate actions:
GET - Retrieve dataPOST - Create new resourcesPUT/PATCH - Update existing resourcesDELETE - Remove resourcesJSON Payloads: All data exchanges use JSON format, making parsing straightforward in Python.
Understanding the relationship between resources helps you construct effective API calls:
Application (Your API Key)
├── User (Etsy Account)
│ └── Shop (Seller Store)
│ ├── Listings
│ │ ├── Images
│ │ ├── Videos
│ │ ├── Inventory
│ │ ├── Variations
│ │ └── Digital Files
│ ├── Receipts (Orders)
│ │ └── Transactions
│ ├── Reviews
│ └── Shipping Profiles
└── Taxonomy (Categories)
A shop is the central resource for sellers. Each Etsy account can have one shop, containing all listings, orders, and settings. Key shop properties include:
Listings represent products for sale. For digital products, listings have special considerations:
When customers purchase, Etsy creates:
For digital products, understanding this distinction helps you track sales per product.
The Etsy API uses OAuth scopes to control access. When users authorize your application, they grant specific permissions:
| Scope | Description |
|---|---|
address_r |
Read user address information |
address_w |
Write user address information |
billing_r |
Read billing information |
cart_r |
Read shopping cart |
cart_w |
Write shopping cart |
email_r |
Read email address |
favorites_r |
Read favorites |
favorites_w |
Write favorites |
feedback_r |
Read feedback/reviews |
listings_d |
Delete listings |
listings_r |
Read listings |
listings_w |
Write listings |
profile_r |
Read profile information |
profile_w |
Write profile information |
recommend_r |
Read recommendations |
recommend_w |
Write recommendations |
shops_r |
Read shop information |
shops_w |
Write shop information |
transactions_r |
Read transactions/orders |
transactions_w |
Write transactions |
For a digital products business, you’ll typically need:
REQUIRED_SCOPES = [
"listings_r", # Read your listings
"listings_w", # Create and update listings
"listings_d", # Delete listings if needed
"shops_r", # Read shop information
"transactions_r", # Read orders and sales data
"profile_r", # Basic profile access
]
Etsy implements rate limiting to ensure fair API usage. Understanding these limits prevents your applications from being throttled:
Every API response includes headers indicating your current status:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 8
X-RateLimit-Reset: 1640000000
import time
import requests
def make_api_request(url, headers):
"""Make an API request with rate limit handling."""
response = requests.get(url, headers=headers)
# Check remaining requests
remaining = int(response.headers.get('X-RateLimit-Remaining', 10))
if remaining < 2:
# Approaching limit, pause briefly
reset_time = int(response.headers.get('X-RateLimit-Reset', 0))
wait_time = max(reset_time - time.time(), 1)
print(f"Rate limit approaching, waiting {wait_time} seconds")
time.sleep(wait_time)
if response.status_code == 429:
# Rate limited, wait and retry
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {retry_after} seconds")
time.sleep(retry_after)
return make_api_request(url, headers)
return response
All Etsy API v3 requests use the base URL:
https://openapi.etsy.com/v3/
The version is embedded in the URL path, ensuring your code targets the correct API version.
Etsy API responses follow consistent patterns:
{
"count": 1,
"results": [
{
"listing_id": 123456789,
"title": "Digital Planner Template",
"description": "A comprehensive digital planner...",
"price": {
"amount": 999,
"divisor": 100,
"currency_code": "USD"
},
"quantity": 999,
"state": "active"
}
]
}
{
"error": "Invalid API key",
"error_description": "The API key provided is not valid"
}
Large result sets are paginated:
{
"count": 500,
"results": [...],
"pagination": {
"effective_limit": 100,
"effective_offset": 0,
"next_offset": 100,
"next_page": 2
}
}
Etsy represents prices as integers to avoid floating-point issues:
def parse_etsy_price(price_obj):
"""Convert Etsy price object to decimal value."""
amount = price_obj['amount']
divisor = price_obj['divisor']
return amount / divisor
def create_etsy_price(value, currency='USD'):
"""Create Etsy price object from decimal value."""
return {
'amount': int(value * 100),
'divisor': 100,
'currency_code': currency
}
# Example usage
etsy_price = {'amount': 1999, 'divisor': 100, 'currency_code': 'USD'}
actual_price = parse_etsy_price(etsy_price) # 19.99
For digital products, several endpoints are particularly important:
Upload and manage digital download files:
POST /v3/application/shops/{shop_id}/listings/{listing_id}/files
GET /v3/application/shops/{shop_id}/listings/{listing_id}/files
DELETE /v3/application/shops/{shop_id}/listings/{listing_id}/files/{listing_file_id}
Manage variations and stock:
GET /v3/application/listings/{listing_id}/inventory
PUT /v3/application/listings/{listing_id}/inventory
Handle variations (format, size, color):
GET /v3/application/shops/{shop_id}/listings/{listing_id}/properties
PUT /v3/application/shops/{shop_id}/listings/{listing_id}/properties/{property_id}
Unlike some APIs, Etsy doesn’t provide a sandbox environment. Testing must be done carefully:
class EtsyClient:
def __init__(self, api_key, access_token, dry_run=False):
self.api_key = api_key
self.access_token = access_token
self.dry_run = dry_run
def update_listing(self, listing_id, **kwargs):
"""Update a listing with optional dry-run mode."""
if self.dry_run:
print(f"[DRY RUN] Would update listing {listing_id} with: {kwargs}")
return {"dry_run": True, "would_update": kwargs}
# Actual API call
return self._make_request(
'PUT',
f'/v3/application/listings/{listing_id}',
json=kwargs
)
Etsy provides comprehensive API documentation at:
https://developer.etsy.com/documentation/
Key sections to bookmark:
Before coding, explore the API interactively:
Import Etsy’s Postman collection to test endpoints without writing code:
import requests
import json
BASE_URL = "https://openapi.etsy.com/v3"
def explore_endpoint(endpoint, access_token):
"""Explore an API endpoint interactively."""
headers = {
"Authorization": f"Bearer {access_token}",
"x-api-key": "your_api_key"
}
response = requests.get(f"{BASE_URL}{endpoint}", headers=headers)
print(f"Status: {response.status_code}")
print(f"Headers: {dict(response.headers)}")
print(f"Body: {json.dumps(response.json(), indent=2)}")
return response.json()
# Example: Explore your shop
# shop_data = explore_endpoint("/application/users/me", access_token)
With a solid understanding of the Etsy API architecture, you’re ready to set up your development environment. The next chapter guides you through installing Python dependencies, creating your Etsy developer application, and preparing your workspace for API development.
| ← Introduction | Table of Contents | Chapter 2: Setting Up Your Development Environment → |