Skip to content

robomello/etsy-browser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

etsy-browser

Manage Etsy shops programmatically via the internal API and Playwright browser automation. No official API keys needed - uses cookie-based authentication from Chrome DevTools.

10-20x faster than Playwright-only approaches - the internal API handles most operations in under 1 second.

How It Works

Etsy's web interface uses an internal REST API at /api/v3/ajax/shop/{shopId}/listings. This tool authenticates using cookies exported from your browser and a CSRF nonce extracted from the dashboard page.

Two modes:

  • API mode (httpx): Fast path for all CRUD operations (~0.5-1s per operation)
  • Browser mode (Playwright): Fallback for complex form filling and order scraping

All operations automatically try API first and fall back to Playwright if needed.

Features

  • Full listing CRUD (create, read, update, delete)
  • Batch update multiple listings at once
  • Image upload via multipart API
  • Order monitoring (Playwright)
  • Multi-shop support
  • Cookie session management with import CLI
  • Anti-detection (stealth JS, human-like delays)
  • Rate limiting (configurable)

Installation

# API mode only (recommended - covers most use cases)
pip install etsy-browser

# With Playwright browser fallback
pip install 'etsy-browser[browser]'
playwright install chromium

Setup

1. Create config

cp config.example.toml config.toml

Edit config.toml with your shop details:

[defaults]
shop = "my_shop"

[shops.my_shop]
name = "my-etsy-shop-name"

The name is your Etsy shop URL name (e.g., if your shop is at etsy.com/shop/CoolMugs, the name is CoolMugs).

2. Export cookies from Chrome

  1. Log into your Etsy shop in Chrome
  2. Open DevTools (F12) > Application > Cookies > https://www.etsy.com
  3. Right-click > "Copy all as JSON" (or use the Cookie-Editor extension)
  4. Import into etsy-browser:
etsy-browser import-cookies my_shop
# Paste the JSON, then Ctrl+D

3. Verify

etsy-browser check-sessions
#   + my_shop: ACTIVE

Usage

CLI

# Check all sessions
etsy-browser check-sessions

# List configured shops
etsy-browser list-shops

# Test API access
etsy-browser api-test my_shop

# Test with a specific listing
etsy-browser api-test my_shop 1889058316

# Refresh cookies (hit dashboard to keep sessions alive)
etsy-browser refresh-cookies

Python API

from etsy_browser import load_config, EtsyAPI

# Load config from config.toml
config = load_config()

# Create API client for a shop
api = EtsyAPI(config.get_shop("my_shop"))

# Get all active listings
result = api.get_listings(state="active")
print(f"Found {result['count']} active listings")

# Get a specific listing (127 fields)
listing = api.get_listing("1889058316")
print(listing["listing"]["title"])

# Create a draft listing
result = api.create_listing(
    title="Mountain Coffee Mug Gift For Him Nature Ceramic Cup",
    description="Beautiful mountain scene on premium ceramic...",
    price=14.99,
    tags=["coffee mug", "gift for him", "mountain mug"],
    taxonomy_id=6048,  # Mugs
    state="draft",
)
print(f"Created: {result['listing_id']}")

# Update a listing
api.update_listing("1889058316", price=16.99, quantity=50)

# Upload an image
api.upload_image("1889058316", "/path/to/image.jpg", rank=1)

# Batch update
api.batch_update(
    ["1889058316", "1889058317", "1889058318"],
    quantity=99,
)

# Get shop sections
sections = api.get_sections()

# Delete a listing (permanent!)
api.delete_listing("1889058316")

# Deactivate (reversible)
api.deactivate_listing("1889058316")

# Always close when done
api.close()

Convenience Functions

For quick scripts, use the module-level functions:

from etsy_browser import (
    load_config, get_listing, get_listings, create_listing,
    update_listing, delete_listing, batch_update, list_shops,
)

load_config()

# These auto-select API vs browser
listings = get_listings(state="active")
listing = get_listing(listing_id="1889058316")
update_listing(listing_id="1889058316", price=16.99)

Runtime Configuration (No config.toml)

from etsy_browser import add_shop, load_config, EtsyAPI, EtsyShopConfig

# Option 1: add_shop() for quick setup
add_shop("my_shop", "my-etsy-shop-name")

# Option 2: environment variables
# ETSY_SHOPS="my_shop=my-etsy-shop-name"
# ETSY_DEFAULT_SHOP="my_shop"
# ETSY_COOKIES_DIR="~/.etsy-browser/cookies"

# Then use normally
config = EtsyShopConfig.from_alias("my_shop")
api = EtsyAPI(config)

API Reference

Internal Etsy API Endpoints

Method Endpoint Description
GET /api/v3/ajax/shop/{shopId}/listings List all listings (paginated, 1000/page)
GET /api/v3/ajax/shop/{shopId}/listings/{id} Get single listing (127 fields)
POST /api/v3/ajax/shop/{shopId}/listings Create listing
PATCH /api/v3/ajax/shop/{shopId}/listings/{id} Update listing fields
DELETE /api/v3/ajax/shop/{shopId}/listings/{id} Delete listing (204)
POST /api/v3/ajax/shop/{shopId}/listings/images Upload image (multipart)
GET /api/v3/ajax/shop/{shopId}/sections Get shop sections
GET /api/v3/ajax/shop/{shopId}/listings/{id}/inventory Get SKUs/pricing

Listing States

Value Meaning
0 Active (is_available=true)
1 Inactive
3 Draft
4 Removed
5 Expired

Auth

  • Cookies: Injected from Chrome DevTools JSON export
  • CSRF: x-csrf-token header from <meta name="csrf_nonce"> tag on dashboard (cached 10 min)
  • Shop ID: Extracted from dashboard page JSON

Important Notes

  • Price format: API returns {"amount": 1499, "divisor": 100} for $14.99. Send "14.99" (string) in create/update.
  • Tags: Max 13 tags, max 20 characters each.
  • Title: Max 140 characters.
  • Images: The images field in listing lists is URL strings. The listing_images field has dicts with image_id.
  • Pagination: API returns max 1000 listings per page. The library handles pagination automatically.
  • Cookie expiry: Cookies expire after ~30 days of inactivity. Use etsy-browser refresh-cookies periodically (or set up a cron job).
  • Rate limits: Self-imposed: max 1 listing creation per 60s, 10 per hour. Configurable in config.toml.

Cookie Refresh (Keep Sessions Alive)

To prevent cookie expiry, hit the dashboard periodically:

# Manual
etsy-browser refresh-cookies

# Cron job (every 4 hours)
0 */4 * * * /usr/local/bin/etsy-browser refresh-cookies >> ~/logs/etsy-refresh.log 2>&1

# systemd timer (Linux)
# See examples/etsy-cookie-refresh.service and .timer

Configuration Reference

config.toml

[paths]
cookies_dir = "~/.etsy-browser/cookies"
profiles_dir = "~/.etsy-browser/profiles"
screenshots_dir = "/tmp/etsy_screenshots"

[rate_limits]
max_creates_per_hour = 10
min_create_interval_sec = 60

[defaults]
shop = "my_shop"

[shops.my_shop]
name = "my-etsy-shop-name"

[shops.second_shop]
name = "another-shop-name"

Environment Variables

Variable Description
ETSY_SHOPS Comma-separated alias=name pairs
ETSY_DEFAULT_SHOP Default shop alias
ETSY_COOKIES_DIR Cookie directory path
ETSY_PROFILES_DIR Browser profiles directory path
ETSY_BROWSER_CONFIG Path to config.toml

Environment variables override config.toml values.

Disclaimer

This tool uses Etsy's internal API endpoints which are not officially documented or supported. Use at your own risk. Etsy may change these endpoints at any time. This project is not affiliated with Etsy.

License

MIT

About

Manage Etsy shops via internal API + Playwright. No API keys needed.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages