Troubleshooting

Overview

This final chapter covers common problems you’ll encounter and how to solve them:

Authentication Problems

“Invalid API Key”

Symptoms: 401 error mentioning invalid API key

Solutions:

  1. Verify your API key in .env matches the developer portal
  2. Check for trailing spaces or quotes in your key
  3. Ensure your app is approved (not pending)
# Debug: Print sanitized key
print(f"API Key (first 8 chars): {Config.API_KEY[:8]}...")
print(f"API Key length: {len(Config.API_KEY)}")

“Token Expired”

Symptoms: 401 error after working previously

Solutions:

  1. Implement automatic token refresh (see Chapter 2)
  2. Delete tokens.json and re-authenticate
  3. Check if refresh token is also expired
def force_reauthenticate():
    """Delete tokens and start fresh."""
    from pathlib import Path
    token_file = Path("tokens.json")
    if token_file.exists():
        token_file.unlink()
    print("Tokens cleared. Run authentication again.")

“Invalid Scope”

Symptoms: 403 error for certain operations

Solutions:

  1. Check your requested scopes during authentication
  2. Some operations require specific scopes:
    • listings_w for creating/updating listings
    • listings_d for deleting listings
    • transactions_r for reading orders
# Verify your token's scopes
import jwt
token = "your_access_token"
decoded = jwt.decode(token, options={"verify_signature": False})
print(f"Token scopes: {decoded.get('scope', 'No scopes found')}")

API Errors

“Listing Not Found” (404)

Possible causes:

  1. Listing was deleted
  2. Wrong listing ID
  3. Listing belongs to different shop

Debug approach:

def verify_listing_ownership(client, shop_id, listing_id):
    """Check if listing belongs to your shop."""
    try:
        listings = get_all_listings(client, shop_id)
        ids = [l["listing_id"] for l in listings]
        
        if listing_id in ids:
            print(f"✅ Listing {listing_id} found in shop")
        else:
            print(f"❌ Listing {listing_id} NOT in shop")
            print(f"   Shop has {len(ids)} listings")
    except Exception as e:
        print(f"Error: {e}")

“Bad Request” (400)

Common causes:

  1. Invalid field values
  2. Missing required fields
  3. Field length exceeded

Debug approach:

def validate_listing_data(data):
    """Validate listing data before submission."""
    errors = []
    
    # Title length
    if "title" in data and len(data["title"]) > 140:
        errors.append(f"Title too long: {len(data['title'])}/140 chars")
    
    # Tags count
    if "tags" in data and len(data["tags"]) > 13:
        errors.append(f"Too many tags: {len(data['tags'])}/13")
    
    # Price validation
    if "price" in data:
        if not isinstance(data["price"], int):
            errors.append("Price must be integer (cents)")
        elif data["price"] < 20:  # $0.20 minimum
            errors.append("Price below minimum ($0.20)")
    
    return errors

Rate Limiting (429)

Symptoms: Requests fail with 429 after many calls

Solutions:

  1. Add delays between requests
  2. Implement exponential backoff
  3. Cache responses when possible
def adaptive_rate_limiter(response):
    """Adjust delay based on rate limit headers."""
    remaining = int(response.headers.get("X-RateLimit-Remaining", 100))
    limit = int(response.headers.get("X-RateLimit-Limit", 100))
    
    usage_percent = (limit - remaining) / limit * 100
    
    if usage_percent > 80:
        return 2.0  # Slow down
    elif usage_percent > 50:
        return 0.5
    else:
        return 0.1  # Normal speed

Digital Product Issues

Files Not Appearing After Upload

Causes:

  1. Upload failed silently
  2. Listing not marked as digital
  3. File too large

Debug:

def diagnose_file_issue(client, shop_id, listing_id):
    """Diagnose file upload issues."""
    # Check listing is digital
    listing = client.get(f"/application/listings/{listing_id}")
    
    if not listing.get("is_digital"):
        print("❌ Listing is not marked as digital!")
        print("   Set is_digital=true when creating listing")
        return
    
    # Check files
    files = list_digital_files(client, shop_id, listing_id)
    
    if not files:
        print("❌ No files attached to listing")
    else:
        print(f"{len(files)} files attached:")
        for f in files:
            print(f"   - {f['filename']} ({f['filesize']})")

Customer Can’t Download

Not an API issue - but common question. Direct customers to:

  1. Etsy account > Purchases and reviews
  2. Click “Download Files” next to the order
  3. Files don’t come via email

Wrong File Delivered

If you updated a file but customer got old version:

Data Consistency Issues

Listing Count Mismatch

Symptom: API returns different count than Etsy dashboard

Causes:

  1. Caching delays (data can be minutes behind)
  2. Different listing states being counted
  3. Pagination not complete
def accurate_listing_count(client, shop_id):
    """Get accurate count by fetching all listings."""
    all_listings = get_all_listings(client, shop_id)
    
    by_state = {}
    for listing in all_listings:
        state = listing["state"]
        by_state[state] = by_state.get(state, 0) + 1
    
    print(f"Total listings: {len(all_listings)}")
    for state, count in by_state.items():
        print(f"  {state}: {count}")

Price Showing Wrong

Causes:

  1. Currency conversion display
  2. Price field is in smallest currency unit (cents)
  3. Sale pricing active
def parse_price(price_obj):
    """Correctly parse Etsy price object."""
    amount = price_obj["amount"]
    divisor = price_obj["divisor"]
    currency = price_obj["currency_code"]
    
    actual_price = amount / divisor
    return f"{currency} {actual_price:.2f}"

Performance Issues

Scripts Running Slowly

Causes:

  1. No pagination optimization
  2. Too many sequential requests
  3. No caching

Solutions:

# Bad: Fetching one at a time
for listing_id in listing_ids:
    listing = client.get(f"/application/listings/{listing_id}")

# Better: Fetch in batches where possible
listings = get_all_listings(client, shop_id)
listings_dict = {l["listing_id"]: l for l in listings}

Memory Issues with Large Shops

For shops with thousands of listings:

def process_listings_in_batches(client, shop_id, processor, batch_size=100):
    """Process listings in memory-efficient batches."""
    offset = 0
    
    while True:
        response = client.get(
            f"/application/shops/{shop_id}/listings",
            params={"limit": batch_size, "offset": offset}
        )
        
        batch = response["results"]
        if not batch:
            break
        
        for listing in batch:
            processor(listing)
        
        offset += batch_size
        
        # Optional: Clear memory
        del batch

Debugging Tools

Request/Response Logger

class DebugClient:
    """Client wrapper that logs all requests."""
    
    def __init__(self, client):
        self.client = client
        self.log = []
    
    def get(self, endpoint, **kwargs):
        start = time.time()
        try:
            response = self.client.get(endpoint, **kwargs)
            self._log_request("GET", endpoint, time.time() - start, "success")
            return response
        except Exception as e:
            self._log_request("GET", endpoint, time.time() - start, str(e))
            raise
    
    def _log_request(self, method, endpoint, duration, status):
        self.log.append({
            "timestamp": datetime.now().isoformat(),
            "method": method,
            "endpoint": endpoint,
            "duration_ms": int(duration * 1000),
            "status": status
        })
    
    def print_log(self):
        for entry in self.log:
            print(f"{entry['timestamp']} | {entry['method']} {entry['endpoint']} | "
                  f"{entry['duration_ms']}ms | {entry['status']}")

API Health Check

def api_health_check(client, shop_id):
    """Run diagnostic checks on API connectivity."""
    checks = []
    
    # 1. Basic connectivity
    try:
        client.get("/application/openapi-ping")
        checks.append(("API Connectivity", "✅ OK"))
    except Exception as e:
        checks.append(("API Connectivity", f"{e}"))
    
    # 2. Authentication
    try:
        client.get("/application/shops")
        checks.append(("Authentication", "✅ OK"))
    except Exception as e:
        checks.append(("Authentication", f"{e}"))
    
    # 3. Shop access
    try:
        client.get(f"/application/shops/{shop_id}")
        checks.append(("Shop Access", "✅ OK"))
    except Exception as e:
        checks.append(("Shop Access", f"{e}"))
    
    # 4. Listings access
    try:
        response = client.get(f"/application/shops/{shop_id}/listings", 
                             params={"limit": 1})
        checks.append(("Listings Access", f"✅ OK ({response.get('count', 0)} total)"))
    except Exception as e:
        checks.append(("Listings Access", f"{e}"))
    
    # Print results
    print("\n🔍 API Health Check")
    print("=" * 40)
    for check, status in checks:
        print(f"{check}: {status}")

Getting Help

Etsy Developer Resources

  1. Developer Portal: https://www.etsy.com/developers
  2. API Documentation: https://developers.etsy.com/documentation
  3. Developer Forums: Search for similar issues
  4. API Status: Check if Etsy is having issues

Community Resources

  1. Stack Overflow: Tag questions with etsy-api
  2. Reddit: r/Etsy and r/EtsySellers
  3. GitHub: Search for Etsy API wrappers and examples

When to Contact Etsy

Contact Etsy developer support when:

Summary

Most issues fall into these categories:

  1. Authentication: Usually token expiration or scope issues
  2. Request errors: Typically validation or missing data
  3. Rate limits: Add delays and caching
  4. Data issues: Check currency handling and state filtering

The key to troubleshooting is good logging. Always log:


← Previous: Automation Workflows

← Back to Table of Contents