DEV Community

Cover image for How to Use Etsy API: Complete Integration Guide (2026)
Wanda
Wanda

Posted on • Originally published at apidog.com

How to Use Etsy API: Complete Integration Guide (2026)

TL;DR

The Etsy API lets you build applications for Etsy’s marketplace, automating shop, listing, order, and inventory management. It uses OAuth 2.0 authentication, REST endpoints, and enforces a rate limit of 10 calls/sec per app. This guide shows you how to set up authentication, use core endpoints, integrate webhooks, and prepare your integration for production.

Try Apidog today


Introduction

Etsy handles over $13B in annual gross merchandise sales across 230+ countries. If you’re building e-commerce tools, inventory systems, or analytics dashboards, Etsy API integration is critical.

Manual data entry costs multi-channel sellers 15–20 hours a week. Automating your Etsy integration means syncing listings, processing orders, and updating inventory without manual overhead.

This guide covers end-to-end Etsy API integration: OAuth 2.0 authentication, shop/listing/order management, webhook handling, and error troubleshooting. By the end, you’ll be ready for production with a robust Etsy integration.

💡 Apidog streamlines API integration testing. Use it to test Etsy endpoints, validate OAuth flows, inspect webhook payloads, and debug authentication—all in one workspace. Import API specs, mock responses, and share test scenarios with your team.

What Is the Etsy API?

Etsy provides a RESTful API for marketplace data and seller operations, including:

  • Shop/profile info retrieval
  • Listing creation, updates, and inventory management
  • Order processing and fulfillment tracking
  • Customer and transaction data access
  • Shipping profiles and tax calculations
  • Image/media upload management

Key Features

Feature Description
RESTful Design Standard HTTP methods with JSON responses
OAuth 2.0 Secure authentication with token refresh
Webhooks Real-time order/listing event notifications
Rate Limiting 10 requests/sec per app (with burst allowance)
Sandbox Support Dev environment without live data

API Architecture Overview

Etsy uses versioned REST structure:

https://openapi.etsy.com/v3/application/
Enter fullscreen mode Exit fullscreen mode

Version 3 (v3) is the current standard, with improved OAuth 2.0 support and simpler endpoints.

API Versions Compared

Version Status Authentication Use Case
V3 Current OAuth 2.0 All new integrations
V2 Deprecated OAuth 1.0a Legacy apps only
V1 Retired N/A Do not use

Migrate any V2 integrations to V3 ASAP. V2 will be fully retired by late 2026.


Getting Started: Authentication Setup

Step 1: Create Your Etsy Developer Account

  1. Go to the Etsy Developer Portal
  2. Sign in (or create an account)
  3. Navigate to Your Apps in the dashboard
  4. Click Create a new app

Step 2: Register Your Application

Fill the app form:

  • App Name: Clear, descriptive (users see this during OAuth)
  • App Description: What your app does and who uses it
  • Redirect URI: HTTPS endpoint Etsy redirects to after auth
  • Production/Development: Start with development mode

After submission you get:

  • Key String: Public API identifier
  • Shared Secret: Private secret (never expose)

Store credentials in environment variables:

# .env file
ETSY_KEY_STRING="your_key_string_here"
ETSY_SHARED_SECRET="your_shared_secret_here"
ETSY_ACCESS_TOKEN="generated_via_oauth"
ETSY_REFRESH_TOKEN="generated_via_oauth"
Enter fullscreen mode Exit fullscreen mode

Step 3: Understand OAuth 2.0 Flow

Etsy uses OAuth 2.0. The flow is:

1. User clicks "Connect with Etsy" in your app
2. Redirect to Etsy authorization URL
3. User logs in and grants permissions
4. Etsy redirects back with authorization code
5. Exchange code for access token
6. Use access token for API calls
7. Refresh token when access token expires (1 hour)
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement OAuth Authorization

Generate the authorization URL in Node.js:

const generateAuthUrl = (clientId, redirectUri, state) => {
  const baseUrl = 'https://www.etsy.com/oauth/connect';
  const params = new URLSearchParams({
    client_id: clientId,
    redirect_uri: redirectUri,
    scope: 'listings_r listings_w orders_r orders_w shops_r',
    state: state, // CSRF protection
    response_type: 'code'
  });

  return `${baseUrl}?${params.toString()}`;
};

// Usage
const authUrl = generateAuthUrl(
  process.env.ETSY_KEY_STRING,
  'https://your-app.com/callback',
  crypto.randomBytes(16).toString('hex')
);

console.log(`Redirect user to: ${authUrl}`);
Enter fullscreen mode Exit fullscreen mode

Required Scopes

Request only what you need:

Scope Description Use Case
listings_r Read listings Display/sync inventory
listings_w Write listings Create/update products
orders_r Read orders Order management
orders_w Write orders Update status
shops_r Read shop info Shop profile/analytics
transactions_r Read transactions Financial reporting
email Buyer email Order comms

Step 5: Exchange Code for Access Token

Handle the OAuth callback to get tokens:

const exchangeCodeForToken = async (code, redirectUri) => {
  const response = await fetch('https://api.etsy.com/v3/public/oauth/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: process.env.ETSY_KEY_STRING,
      client_secret: process.env.ETSY_SHARED_SECRET,
      redirect_uri: redirectUri,
      code: code
    })
  });

  const data = await response.json();

  // Store tokens securely
  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in,
    user_id: data.user_id,
    scope: data.scope
  };
};

// Express callback route
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;

  // Verify CSRF state
  if (state !== req.session.oauthState) {
    return res.status(400).send('Invalid state parameter');
  }

  try {
    const tokens = await exchangeCodeForToken(code, 'https://your-app.com/callback');
    await db.users.update(req.session.userId, {
      etsy_access_token: tokens.access_token,
      etsy_refresh_token: tokens.refresh_token,
      etsy_token_expires: Date.now() + (tokens.expires_in * 1000),
      etsy_user_id: tokens.user_id
    });

    res.redirect('/dashboard');
  } catch (error) {
    console.error('Token exchange failed:', error);
    res.status(500).send('Authentication failed');
  }
});
Enter fullscreen mode Exit fullscreen mode

Step 6: Implement Token Refresh

Tokens expire after 1 hour. Refresh automatically:

const refreshAccessToken = async (refreshToken) => {
  const response = await fetch('https://api.etsy.com/v3/public/oauth/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      client_id: process.env.ETSY_KEY_STRING,
      client_secret: process.env.ETSY_SHARED_SECRET,
      refresh_token: refreshToken
    })
  });

  const data = await response.json();

  // Always save the new refresh token!
  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in
  };
};

// Middleware to auto-refresh
const ensureValidToken = async (userId) => {
  const user = await db.users.findById(userId);

  if (user.etsy_token_expires < Date.now() + 300000) {
    const newTokens = await refreshAccessToken(user.etsy_refresh_token);

    await db.users.update(userId, {
      etsy_access_token: newTokens.access_token,
      etsy_refresh_token: newTokens.refresh_token,
      etsy_token_expires: Date.now() + (newTokens.expires_in * 1000)
    });

    return newTokens.access_token;
  }

  return user.etsy_access_token;
};
Enter fullscreen mode Exit fullscreen mode

Step 7: Make Authenticated API Calls

Send your access token with every request:

const makeEtsyRequest = async (endpoint, options = {}) => {
  const accessToken = await ensureValidToken(options.userId);

  const response = await fetch(`https://openapi.etsy.com/v3/application${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'x-api-key': process.env.ETSY_KEY_STRING,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Etsy API Error: ${error.message}`);
  }

  return response.json();
};
Enter fullscreen mode Exit fullscreen mode

Shop Management Endpoints

Retrieving Shop Information

Fetch shop details:

const getShopInfo = async (shopId) => {
  const response = await makeEtsyRequest(`/shops/${shopId}`, {
    method: 'GET'
  });
  return response;
};

// Example usage
const shop = await getShopInfo(12345678);
console.log(`Shop: ${shop.title}`);
console.log(`Currency: ${shop.currency_code}`);
console.log(`Listings: ${shop.num_listings_active}`);
Enter fullscreen mode Exit fullscreen mode

Example response:

{
  "shop_id": 12345678,
  "shop_name": "MyHandmadeShop",
  "title": "Handmade Jewelry & Accessories",
  "announcement": "Welcome! Free shipping on orders over $50",
  "currency_code": "USD",
  "is_vacation": false,
  "num_listings_active": 127,
  "num_listings_sold": 1543,
  "url": "https://www.etsy.com/shop/MyHandmadeShop"
}
Enter fullscreen mode Exit fullscreen mode

Retrieving Shop Sections

Organize listings by section:

const getShopSections = async (shopId) => {
  const response = await makeEtsyRequest(`/shops/${shopId}/sections`, {
    method: 'GET'
  });
  return response;
};

// Example response:
{
  "count": 5,
  "results": [
    {
      "shop_section_id": 12345,
      "title": "Necklaces",
      "rank": 1,
      "num_listings": 23
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Listing Management

Creating a New Listing

Create a product listing:

const createListing = async (shopId, listingData) => {
  const payload = {
    title: listingData.title,
    description: listingData.description,
    price: listingData.price.toString(), // Must be string
    quantity: listingData.quantity,
    sku: listingData.sku || [],
    tags: listingData.tags.slice(0, 13),
    category_id: listingData.categoryId,
    shop_section_id: listingData.sectionId,
    state: listingData.state || 'active',
    who_made: listingData.whoMade,
    when_made: listingData.whenMade,
    is_supply: listingData.isSupply,
    item_weight: listingData.weight || null,
    item_weight_unit: listingData.weightUnit || 'g',
    item_length: listingData.length || null,
    item_width: listingData.width || null,
    item_height: listingData.height || null,
    item_dimensions_unit: listingData.dimensionsUnit || 'mm',
    is_private: listingData.isPrivate || false,
    recipient: listingData.recipient || null,
    occasion: listingData.occasion || null,
    style: listingData.style || []
  };

  const response = await makeEtsyRequest(`/shops/${shopId}/listings`, {
    method: 'POST',
    body: JSON.stringify(payload)
  });
  return response;
};

// Usage
const listing = await createListing(12345678, {
  title: 'Sterling Silver Moon Phase Necklace',
  description: 'Handcrafted sterling silver necklace featuring moon phases...',
  price: 89.99,
  quantity: 15,
  sku: ['MOON-NECKLACE-001'],
  tags: ['moon necklace', 'sterling silver', 'moon phase', 'celestial jewelry'],
  categoryId: 10623,
  sectionId: 12345,
  state: 'active',
  whoMade: 'i_did',
  whenMade: 'made_to_order',
  isSupply: false,
  weight: 25,
  weightUnit: 'g'
});
Enter fullscreen mode Exit fullscreen mode

Uploading Listing Images

Images are uploaded after listing creation (see Etsy documentation):

const uploadListingImage = async (listingId, imagePath, imagePosition = 1) => {
  const fs = require('fs');
  const imageBuffer = fs.readFileSync(imagePath);
  const base64Image = imageBuffer.toString('base64');

  const payload = {
    image: base64Image,
    listing_image_id: null,
    position: imagePosition,
    is_watermarked: false,
    alt_text: 'Handcrafted sterling silver moon phase necklace'
  };

  const response = await makeEtsyRequest(`/listings/${listingId}/images`, {
    method: 'POST',
    body: JSON.stringify(payload)
  });

  return response;
};

// Upload multiple images
const uploadListingImages = async (listingId, imagePaths) => {
  const results = [];
  for (let i = 0; i < imagePaths.length; i++) {
    const result = await uploadListingImage(listingId, imagePaths[i], i + 1);
    results.push(result);
  }
  return results;
};
Enter fullscreen mode Exit fullscreen mode

Updating Listing Inventory

Update inventory for an existing listing:

const updateListingInventory = async (shopId, listingId, inventory) => {
  const payload = {
    products: inventory.products.map(product => ({
      sku: product.sku,
      quantity: product.quantity
    })),
    is_over_selling: inventory.isOverSelling || false,
    on_property: inventory.onProperty || []
  };

  const response = await makeEtsyRequest(
    `/shops/${shopId}/listings/${listingId}/inventory`,
    {
      method: 'PUT',
      body: JSON.stringify(payload)
    }
  );

  return response;
};

// Usage
await updateListingInventory(12345678, 987654321, {
  products: [
    { sku: 'MOON-NECKLACE-001', quantity: 10 },
    { sku: 'MOON-NECKLACE-002', quantity: 5 }
  ],
  isOverSelling: false
});
Enter fullscreen mode Exit fullscreen mode

Retrieving Listings

Fetch all listings or filter by status:

const getListings = async (shopId, options = {}) => {
  const params = new URLSearchParams({
    limit: options.limit || 25,
    offset: options.offset || 0
  });

  if (options.state) {
    params.append('state', options.state); // active, inactive, draft, sold_out
  }

  const response = await makeEtsyRequest(
    `/shops/${shopId}/listings?${params.toString()}`,
    { method: 'GET' }
  );

  return response;
};

// Get single listing
const getListing = async (listingId) => {
  const response = await makeEtsyRequest(`/listings/${listingId}`, {
    method: 'GET'
  });
  return response;
};
Enter fullscreen mode Exit fullscreen mode

Deleting a Listing

Remove a listing:

const deleteListing = async (listingId) => {
  const response = await makeEtsyRequest(`/listings/${listingId}`, {
    method: 'DELETE'
  });
  return response;
};
Enter fullscreen mode Exit fullscreen mode

Order Management

Retrieving Orders

Fetch orders with filters:

const getOrders = async (shopId, options = {}) => {
  const params = new URLSearchParams({
    limit: options.limit || 25,
    offset: options.offset || 0
  });

  if (options.status) {
    params.append('status', options.status); // open, completed, cancelled
  }

  if (options.minLastModified) {
    params.append('min_last_modified', options.minLastModified);
  }

  const response = await makeEtsyRequest(
    `/shops/${shopId}/orders?${params.toString()}`,
    { method: 'GET' }
  );

  return response;
};

// Get one order
const getOrder = async (shopId, orderId) => {
  const response = await makeEtsyRequest(`/shops/${shopId}/orders/${orderId}`, {
    method: 'GET'
  });
  return response;
};
Enter fullscreen mode Exit fullscreen mode

Order response:

{
  "order_id": 1234567890,
  "user_id": 98765432,
  "shop_id": 12345678,
  "state": "complete",
  "name": "Jane Doe",
  "email": "jane.doe@email.com",
  "total_price": "89.99",
  "total_shipping_cost": "5.95",
  "grand_total": "103.59",
  "currency_code": "USD",
  "shipping_address": {
    "name": "Jane Doe",
    "address_line1": "123 Main Street",
    "city": "New York",
    "state": "NY",
    "zip": "10001",
    "country": "US",
    "phone": "+1-555-0123"
  },
  "listings": [
    {
      "listing_id": 987654321,
      "title": "Sterling Silver Moon Phase Necklace",
      "sku": ["MOON-NECKLACE-001"],
      "quantity": 1,
      "price": "89.99"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Updating Order Status

Mark orders as shipped/add tracking:

const updateOrderStatus = async (shopId, orderId, trackingData) => {
  const payload = {
    carrier_id: trackingData.carrierId,
    tracking_code: trackingData.trackingCode,
    should_send_bcc_to_buyer: trackingData.notifyBuyer || true
  };

  const response = await makeEtsyRequest(
    `/shops/${shopId}/orders/${orderId}/shipping`,
    {
      method: 'POST',
      body: JSON.stringify(payload)
    }
  );

  return response;
};

// Usage
await updateOrderStatus(12345678, 1234567890, {
  carrierId: 'usps',
  trackingCode: '9400111899223456789012',
  notifyBuyer: true
});
Enter fullscreen mode Exit fullscreen mode

Common Carrier IDs:

Carrier Carrier ID
USPS usps
FedEx fedex
UPS ups
DHL dhl_express
Canada Post canada_post
Royal Mail royal_mail
Australia Post australia_post

Rate Limiting and Quotas

Understanding Rate Limits

Etsy enforces:

  • Standard: 10 requests/sec per app
  • Burst: Up to 50 requests in one second (short bursts)
  • Quota: 10,000 calls/hour/app

Exceeding limits returns HTTP 429.

Implementing Rate Limit Handling

Use exponential backoff for retries:

const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await makeEtsyRequest(endpoint, options);

      // Check rate limit headers
      const remaining = response.headers.get('x-etsy-quota-remaining');
      const resetTime = response.headers.get('x-etsy-quota-reset');

      if (remaining < 100) {
        console.warn(`Low quota remaining: ${remaining}, resets at ${resetTime}`);
      }

      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Rate limit headers:

Header Description
x-etsy-quota-remaining Remaining calls this hour
x-etsy-quota-reset When quota resets (unix time)
x-etsy-limit-remaining Remaining calls this second
x-etsy-limit-reset When per-second resets (unix)

Always log these headers for monitoring.


Webhook Integration

Configuring Webhooks

  1. Go to Your Apps in the developer dashboard
  2. Select your app
  3. Click Add Webhook
  4. Enter your HTTPS endpoint
  5. Select events to subscribe

Available Webhook Events

Event Type Trigger Use Case
v3/shops/{shop_id}/orders/create New order placed Confirmation/fulfillment
v3/shops/{shop_id}/orders/update Order status changed Status sync
v3/shops/{shop_id}/listings/create New listing created Inventory sync
v3/shops/{shop_id}/listings/update Listing modified Product sync
v3/shops/{shop_id}/listings/delete Listing removed Remove from systems

Creating Webhook Handler

Example Express handler with signature verification:

const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/etsy', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-etsy-signature'];
  const payload = req.body;

  // Verify webhook signature
  const isValid = verifyWebhookSignature(payload, signature, process.env.ETSY_WEBHOOK_SECRET);

  if (!isValid) {
    console.error('Invalid webhook signature');
    return res.status(401).send('Unauthorized');
  }

  const event = JSON.parse(payload.toString());

  // Route to appropriate handler
  switch (event.type) {
    case 'v3/shops/*/orders/create':
      await handleNewOrder(event.data);
      break;
    case 'v3/shops/*/orders/update':
      await handleOrderUpdate(event.data);
      break;
    case 'v3/shops/*/listings/create':
      await handleListingCreated(event.data);
      break;
    case 'v3/shops/*/listings/update':
      await handleListingUpdated(event.data);
      break;
    case 'v3/shops/*/listings/delete':
      await handleListingDeleted(event.data);
      break;
    default:
      console.log('Unhandled event type:', event.type);
  }

  // Respond within 5 seconds
  res.status(200).send('OK');
});

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}
Enter fullscreen mode Exit fullscreen mode

Webhook Best Practices

  1. Verify signatures — Prevent spoofed webhooks
  2. Return 200 OK quickly — Etsy retries if not acknowledged in 5s
  3. Process asynchronously — Queue events for background processing
  4. Implement idempotency — Handle duplicate deliveries
  5. Log all events — Timestamped audit trail

Troubleshooting Common Issues

Issue: OAuth Token Exchange Fails

Symptoms: 401/403 during authentication.

Diagnosis:

const error = await response.json();
console.error('OAuth error:', error);
Enter fullscreen mode Exit fullscreen mode

Solutions:

  1. Redirect URI must match exactly (incl. https://, trailing slash)
  2. Confirm client_id and client_secret
  3. Authorization code not expired (expires after 1 use or 5 mins)
  4. App in correct mode (prod/dev)

Issue: Rate Limit Exceeded

Symptoms: HTTP 429 responses.

Solutions:

  1. Implement request queuing + rate limiting
  2. Use exponential backoff retries
  3. Batch requests (fetch multiple listings at once)
  4. Monitor quota headers

Simple rate limiter:

class RateLimiter {
  constructor(requestsPerSecond = 9) {
    this.queue = [];
    this.interval = 1000 / requestsPerSecond;
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift();

      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }

      if (this.queue.length > 0) {
        await new Promise(r => setTimeout(r, this.interval));
      }
    }

    this.processing = false;
  }
}

// Usage
const etsyRateLimiter = new RateLimiter(9);
const result = await etsyRateLimiter.add(() => makeEtsyRequest('/shops/12345/listings'));
Enter fullscreen mode Exit fullscreen mode

Issue: Listing Creation Fails with Validation Errors

Symptoms: 400 Bad Request.

Common Causes:

  1. Invalid category_id (use categories API)
  2. Price must be a string
  3. Max 13 tags per listing
  4. Required fields missing

Validate before request:

const validateListing = (data) => {
  const errors = [];

  if (!data.title || data.title.length < 5) {
    errors.push('Title must be at least 5 characters');
  }

  if (typeof data.price !== 'string') {
    errors.push('Price must be a string');
  }

  if (data.tags && data.tags.length > 13) {
    errors.push('Maximum 13 tags allowed');
  }

  if (!['i_did', 'someone_else', 'collective'].includes(data.whoMade)) {
    errors.push('Invalid who_made value');
  }

  return errors;
};
Enter fullscreen mode Exit fullscreen mode

Issue: Webhooks Not Arriving

Symptoms: Webhook endpoint receives nothing.

Diagnosis:

  1. Check delivery logs in dashboard
  2. Endpoint returns 200 OK within 5s?
  3. Test with curl

Solutions:

  1. Ensure HTTPS with valid cert
  2. Whitelist Etsy webhook IPs
  3. Check signature verification
  4. Use webhook testing tools

Issue: Images Fail to Upload

Symptoms: Listing created, images error.

Solutions:

  1. Valid image format (JPEG, PNG, GIF)
  2. File size <= 20MB
  3. Correct base64 encoding
  4. Listing exists before upload
  5. Upload images sequentially, not in parallel

Production Deployment Checklist

Before go-live, ensure:

  • [ ] Switch from development to production app mode
  • [ ] Update all redirect URIs to production
  • [ ] Secure token storage (encrypted DB)
  • [ ] Automatic token refresh logic
  • [ ] Set up rate limiting and request queuing
  • [ ] Webhook endpoint uses HTTPS
  • [ ] Comprehensive error handling
  • [ ] API call logging
  • [ ] Quota monitoring
  • [ ] Runbook for common issues
  • [ ] Test with multiple shop accounts
  • [ ] Document OAuth flow for onboarding

Monitoring and Alerting

Track these metrics:

const metrics = {
  apiCalls: {
    total: 0,
    successful: 0,
    failed: 0,
    rateLimited: 0
  },
  quotaUsage: {
    current: 0,
    limit: 10000,
    resetTime: null
  },
  oauthTokens: {
    active: 0,
    expiring_soon: 0,
    refresh_failures: 0
  },
  webhooks: {
    received: 0,
    processed: 0,
    failed: 0
  }
};

// Alert on high failure rate
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;

if (failureRate > 0.05) {
  sendAlert('Etsy API failure rate above 5%');
}

// Alert on low quota
if (metrics.quotaUsage.current < 500) {
  sendAlert('Etsy API quota below 500 calls remaining');
}
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

Multi-Channel Inventory Sync

Scenario: Home decor seller syncs inventory across Etsy, Shopify, and Amazon.

  • Problem: Manual updates led to overselling
  • Solution: Central inventory system + Etsy API webhooks
  • Result: 0 overselling, 12 hours/week saved

Flow:

  1. Etsy webhook triggers on order creation
  2. Central system decrements inventory
  3. API updates Shopify & Amazon
  4. Log confirmation

Automated Order Fulfillment

Scenario: Print-on-demand automates processing.

  • Problem: 50+ manual orders/day
  • Solution: Etsy API + fulfillment provider integration
  • Result: Orders auto-routed to production in <5 minutes

Key Points:

  • Webhook listens for orders/create
  • Send order details to print provider
  • Update tracking via Etsy API
  • Auto shipping notification to customer

Analytics Dashboard

Scenario: Seller analytics tool for multiple shops.

  • Problem: No unified reporting
  • Solution: OAuth-based multi-shop aggregation
  • Result: Real-time dashboard: sales, traffic, conversions

Data via API:

  • Shop stats (listings, sales, revenue)
  • Order history/trends
  • Listing performance
  • Customer reviews

Conclusion

The Etsy API gives you comprehensive access to marketplace features. Key reminders:

  • OAuth 2.0 authentication: manage/refresh tokens securely
  • Rate limiting (10 req/s, 10K/hr): monitor & queue requests
  • Webhooks: real-time order/inventory sync
  • Error handling & retries: critical for reliability
  • Apidog streamlines API testing and collaboration for Etsy integrations

FAQ Section

What is the Etsy API used for?

The Etsy API lets developers build apps that interact with Etsy’s marketplace. Common uses: inventory management across channels, automated order fulfillment, analytics dashboards, listing tools, and CRM systems.

How do I get an Etsy API key?

Register at the Etsy Developer Portal, go to Your Apps, and click Create a new app. After registration, you get a Key String (public ID) and Shared Secret (private key). Store both in environment variables.

Is the Etsy API free to use?

Yes, the Etsy API is free. Rate limits apply: 10 requests/second and 10,000 calls/hour per app. Higher limits require Etsy approval.

What authentication does Etsy API use?

OAuth 2.0. Users authorize your app via the Etsy auth page; you get an access token (1 hour) and a refresh token (for new tokens).

How do I handle Etsy API rate limits?

Queue requests to stay under 10/sec. Track x-etsy-quota-remaining for hourly usage. Use exponential backoff on HTTP 429 responses.

Can I test Etsy API without a live shop?

Yes—dev mode apps connect to test shops. Create a test Etsy account and use it for OAuth with your dev app.

How do webhooks work with Etsy API?

Etsy webhooks send POSTs to your HTTPS endpoint on events (orders, listings). Set up in the app dashboard, verify signatures, and respond with 200 OK within 5s.

What happens when Etsy OAuth token expires?

Access tokens expire after 1 hour. Use the refresh token to get a new access token before expiry. Automate token refresh in your middleware.

Can I upload listing images via the API?

Yes—upload images as base64-encoded strings in a separate API call after creating the listing. Max 20MB, JPEG/PNG/GIF only.

How do I migrate from Etsy API V2 to V3?

V3 uses OAuth 2.0 (not OAuth 1.0a) and new endpoints. Update your auth flow, change endpoint paths from /v2/ to /v3/application/, and test before V2 retirement (late 2026).

Top comments (0)