DEV Community

Cover image for Etsy API nutzen: Vollständige Integrationsanleitung (2026)
Emre Demir
Emre Demir

Posted on • Originally published at apidog.com

Etsy API nutzen: Vollständige Integrationsanleitung (2026)

TL;DR

Die Etsy API ermöglicht es Entwicklern, Anwendungen zu bauen, die direkt mit dem Etsy-Marktplatz interagieren. Sie verwendet OAuth 2.0, bietet RESTful-Endpunkte für Shops, Listings, Bestellungen und Inventar, und limitiert Anfragen auf 10 pro Sekunde pro App. In diesem Leitfaden findest du praxisorientierte Schritte zur Authentifizierung, Nutzung zentraler Endpunkte, Webhook-Integration und Produktions-Bereitstellung.

Teste Apidog noch heute

Einleitung

Etsy verarbeitet jährlich Bruttowarenverkäufe von über 13 Milliarden US-Dollar in 230+ Ländern. Für Entwickler von E-Commerce-Tools, Inventarsystemen oder Analyseplattformen ist die Etsy API-Integration unverzichtbar.

Viele Verkäufer verlieren 15–20 Stunden pro Woche durch manuelle Dateneingabe bei Multichannel-Vertrieb. Eine robuste Etsy API-Integration automatisiert Angebotssynchronisierung, Bestellabwicklung und Inventar-Updates.

In diesem Leitfaden implementierst du Schritt für Schritt eine produktionsreife Etsy API-Integration: von OAuth 2.0 über Shop-/Listing-Management und Bestellverarbeitung bis zu Webhook-Handling, Fehlerbehebung und Deployment-Strategien.

💡 Apidog vereinfacht das Testen deiner API-Integrationen: Teste Etsy-Endpunkte, prüfe OAuth-Flows, inspiziere Webhook-Payloads und debugge Auth-Probleme zentral. Importiere API-Spezifikationen, modelliere Antworten und teile Testszenarien mit deinem Team.

Was ist die Etsy API?

Etsy stellt eine RESTful API bereit, um auf Marktplatzdaten zuzugreifen und Verkäuferoperationen zu steuern:

  • Shop- und Profilinformationen abrufen
  • Angebote erstellen/aktualisieren, Bestandsverwaltung
  • Auftragsabwicklung & Fulfillment
  • Kunden- und Transaktionsdaten abrufen
  • Versandprofile, Steuerberechnung
  • Bild-/Medien-Uploads verwalten

Hauptmerkmale

Merkmal Beschreibung
RESTful Design HTTP-Methoden, JSON-Responses
OAuth 2.0 Sichere Authentifizierung, Token-Refresh
Webhooks Echtzeitbenachrichtigungen zu Orders/Listings
Ratenbegrenzung 10 Requests/s/App (mit Burst-Kontingent)
Sandbox Entwickeln & testen ohne Live-Daten

API-Architektur

API-Basis (aktuell v3):

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

Nutze v3 für alle neuen Projekte. Etsy stellt v2 zum 2026 ein.

Version Status Auth Anwendungsfall
V3 Aktuell OAuth 2.0 Neue Integrationen
V2 Veraltet OAuth 1.0a Legacy
V1 Eingestellt N/A Nicht verwenden

Wichtig: Migriere bestehende V2-Integrationen sofort auf v3.

Erste Schritte: Authentifizierung

1. Etsy-Entwicklerkonto anlegen

  1. Etsy Developer Portal öffnen
  2. Mit Etsy-Account einloggen (oder neuen erstellen)
  3. Im Dashboard zu Ihre Apps navigieren
  4. Neue App erstellen anklicken

2. Anwendung registrieren

  • App-Name: Klar und verständlich (wird im OAuth-Dialog angezeigt)
  • Beschreibung: Was macht die App? Wer nutzt sie?
  • Redirect-URI: Ziel-URL nach Auth (HTTPS erforderlich)
  • Modus: Mit Entwicklungsmodus starten

Nach Registrierung erhältst du:

  • Key String (öffentlich)
  • Shared Secret (privat, nie öffentlich!)

Speichere Credentials sicher:

# .env
ETSY_KEY_STRING="dein_key"
ETSY_SHARED_SECRET="dein_secret"
ETSY_ACCESS_TOKEN="via_oauth_gen"
ETSY_REFRESH_TOKEN="via_oauth_gen"
Enter fullscreen mode Exit fullscreen mode

3. OAuth 2.0 Flow verstehen

  1. User klickt „Mit Etsy verbinden“
  2. App leitet zu Etsy-Auth-URL weiter
  3. User loggt sich ein, gibt Rechte frei
  4. Etsy schickt Auth-Code an Redirect-URI
  5. App tauscht Code gegen Access Token
  6. Mit Access Token API-Calls durchführen
  7. Token regelmäßig mit Refresh Token erneuern

4. OAuth-Autorisierung implementieren

Generiere die OAuth-Autorisierungs-URL:

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, // Zufallswert für CSRF-Schutz
    response_type: 'code'
  });

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

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

console.log(`Benutzer weiterleiten zu: ${authUrl}`);
Enter fullscreen mode Exit fullscreen mode

Scopes

Fordere nur die Rechte an, die du wirklich brauchst:

Scope Beschreibung Use Case
listings_r Listings lesen Produkte anzeigen
listings_w Listings schreiben Produkte erstellen
orders_r Bestellungen lesen Order-Management
orders_w Bestellungen schreiben Status, Tracking setzen
shops_r Shopinfo lesen Profil, Analytics
transactions_r Transaktionen lesen Finanzberichte
email Käufer-Mail lesen Bestellkommunikation

5. Code gegen Access Token eintauschen

Verarbeite den OAuth-Callback:

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();

  // Sicher speichern!
  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;
  if (state !== req.session.oauthState) {
    return res.status(400).send('Ungültiger State');
  }

  try {
    const tokens = await exchangeCodeForToken(code, 'https://deine-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-Austausch fehlgeschlagen:', error);
    res.status(500).send('Authentifizierung fehlgeschlagen');
  }
});
Enter fullscreen mode Exit fullscreen mode

6. Token-Refresh implementieren

Access Tokens laufen nach 1 Stunde ab – automatisiere das Refreshing:

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();

  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in
  };
};

// Vor API-Call Token prüfen
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

7. Authentifizierte API-Requests

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 Fehler: ${error.message}`);
  }

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

Shop-Management Endpunkte

Shop-Informationen abrufen

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

// Beispiel
const shop = await getShopInfo(12345678);
console.log(`Shop: ${shop.title}`);
console.log(`Währung: ${shop.currency_code}`);
console.log(`Aktive Angebote: ${shop.num_listings_active}`);
Enter fullscreen mode Exit fullscreen mode

Shop-Kategorien abrufen

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

Angebotsverwaltung

Neues Angebot erstellen

const createListing = async (shopId, listingData) => {
  const payload = {
    title: listingData.title,
    description: listingData.description,
    price: listingData.price.toString(),
    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;
};

// Beispiel
const listing = await createListing(12345678, {
  title: 'Halskette mit Mondphasen aus Sterlingsilber',
  description: 'Handgefertigte Halskette aus Sterlingsilber mit Mondphasen...',
  price: 89.99,
  quantity: 15,
  sku: ['MOON-NECKLACE-001'],
  tags: ['Mondkette', 'Sterlingsilber', 'Mondphase', 'himmlischer Schmuck'],
  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

Angebotsbilder hochladen

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: 'Handgefertigte Halskette mit Mondphasen aus Sterlingsilber'
  };

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

  return response;
};
Enter fullscreen mode Exit fullscreen mode

Bestellverwaltung

Bestellungen abrufen

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);
  }
  if (options.minLastModified) {
    params.append('min_last_modified', options.minLastModified);
  }

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

  return response;
};
Enter fullscreen mode Exit fullscreen mode

Bestellstatus aktualisieren

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;
};
Enter fullscreen mode Exit fullscreen mode

Ratenbegrenzung & Quotas

Ratenbegrenzungen umsetzen

  • Standard: 10 Requests/s/App
  • Quota: 10.000/h/App (stündlich zurückgesetzt)
  • Überschreitung → HTTP 429

Exponentielles Backoff implementieren:

const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await makeEtsyRequest(endpoint, options);
      // ... Header prüfen ...
      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Webhook-Integration

Webhooks konfigurieren

  1. Im Developer Dashboard unter Ihre Apps die App auswählen
  2. Webhook hinzufügen
  3. HTTPS-Endpoint eingeben
  4. Zu abonnierende Events auswählen

Webhook-Handler (Node.js/Express)

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;

  const isValid = verifyWebhookSignature(payload, signature, process.env.ETSY_WEBHOOK_SECRET);
  if (!isValid) {
    return res.status(401).send('Nicht autorisiert');
  }

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

  switch (event.type) {
    case 'v3/shops/*/orders/create':
      await handleNewOrder(event.data);
      break;
    // Weitere Events ...
    default:
      console.log('Unbehandelter Ereignistyp:', event.type);
  }
  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

Best Practices:

  • Signatur prüfen
  • Immer schnell mit 200 OK antworten
  • Events asynchron verarbeiten
  • Idempotenz sicherstellen
  • Audit-Log der Events führen

Fehlerbehebung

Problem: OAuth-Token-Austausch schlägt fehl

  • 401/403 Fehler: Prüfe Redirect-URI (genauer String inkl. https://), client_id/client_secret, Gültigkeit des Codes, App-Modus
  • Fehlerausgabe prüfen:
  const error = await response.json();
  console.error('OAuth-Fehler:', error);
Enter fullscreen mode Exit fullscreen mode

Problem: Ratenlimit überschritten

  • Anfragewarteschlange & Rate Limiter nutzen, Backoff bei HTTP 429, Quota-Header überwachen
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;
  }
}
const etsyRateLimiter = new RateLimiter(9);
const result = await etsyRateLimiter.add(() => makeEtsyRequest('/shops/12345/listings'));
Enter fullscreen mode Exit fullscreen mode

Problem: Angebotserstellung scheitert mit Validierungsfehlern

  • Prüfe category_id, Preis als String, max. 13 Tags, Pflichtfelder
  • Beispiel-Validierung:
  const validateListing = (data) => {
    const errors = [];
    if (!data.title || data.title.length < 5) errors.push('Titel zu kurz');
    if (typeof data.price !== 'string') errors.push('Preis muss String sein');
    if (data.tags && data.tags.length > 13) errors.push('Max 13 Tags');
    if (!['i_did', 'someone_else', 'collective'].includes(data.whoMade))
      errors.push('Ungültiger Wert für who_made');
    return errors;
  };
Enter fullscreen mode Exit fullscreen mode

Problem: Webhooks kommen nicht an

  • Prüfe Webhook-Logs im Dashboard
  • Endpoint muss 200 OK in <5s liefern
  • SSL & Firewall prüfen, Signatur-Logik kontrollieren

Problem: Bilder-Upload schlägt fehl

  • Format: JPEG, PNG, GIF
  • Max. 20 MB/Bild
  • Base64-Kodierung prüfen
  • Existenz des Listings sicherstellen
  • Bilder nacheinander hochladen

Produktions-Checkliste

  • [ ] Produktionsmodus für App aktivieren
  • [ ] Redirect-URIs auf Live-URLs setzen
  • [ ] Tokens verschlüsselt speichern
  • [ ] Token-Refresh automatisieren
  • [ ] Ratenbegrenzung & Queue implementieren
  • [ ] HTTPS für Webhooks erzwingen
  • [ ] Fehlerbehandlung umsetzen
  • [ ] Logging aller API-Calls aktivieren
  • [ ] Quota-Nutzung überwachen
  • [ ] Troubleshooting-Doku erstellen
  • [ ] Mit mehreren Shops testen
  • [ ] OAuth-Flow für User dokumentieren

Überwachung/Alerting

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 }
};

const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;
if (failureRate > 0.05) {
  sendAlert('Etsy API Fehlerquote > 5%');
}
if (metrics.quotaUsage.current < 500) {
  sendAlert('Etsy API Quota < 500');
}
Enter fullscreen mode Exit fullscreen mode

Praktische Anwendungsfälle

Mehrkanal-Bestandssynchronisierung

  • Problem: Manuelle Updates führen zu Überverkäufen
  • Lösung: Zentrales Inventar per Webhook-Trigger, Etsy API und weitere Plattformen synchronisieren
  • Ablauf: Webhook → Bestand reduzieren → Mengen via API aktualisieren → Bestätigung loggen

Automatisierte Auftragsabwicklung

  • Problem: 50+ Bestellungen/Tag, viel manuelle Eingabe
  • Lösung: API-Integration mit Fulfillment-Anbieter & Webhook
  • Ablauf: Webhook orders/create → Daten an Fulfillment-API → Trackingnummer via Etsy API → Kunde erhält Versandbenachrichtigung

Analyse-Dashboard

  • Problem: Keine zentrale Auswertung für Multishop-Verkäufer
  • Lösung: Datenaggregation via OAuth-Flow, Echtzeit-Dashboard
  • Datenquellen: Shop-Stats, Bestellhistorie, Listings, Bewertungen

Fazit

Mit der Etsy API automatisierst und skalierst du Shop-Integrationen effizient:

  • Implementiere zuverlässigen OAuth 2.0 Flow & Token-Refresh
  • Beachte Ratenbegrenzung (10/s, 10.000/h) und setze Request-Queues ein
  • Nutze Webhooks für Echtzeit-Synchronisierung
  • Fehlerbehandlung und Monitoring sind Pflicht für Produktion
  • Apidog beschleunigt Integrationstests und Zusammenarbeit im Team

FAQ-Bereich

Wofür wird die Etsy API verwendet?

Für Multichannel-Bestandssynchronisierung, automatisierte Auftragsabwicklung, Analyse-Dashboards, Angebotserstellung und CRM-Lösungen.

Wie erhalte ich einen Etsy API-Schlüssel?

Im Etsy Developer Portal App anlegen. Key String und Shared Secret sicher speichern (z.B. in .env).

Ist die Etsy API kostenlos nutzbar?

Ja, aber mit Limits: 10/s und 10.000/h pro App. Höhere Limits nur nach Antrag.

Welche Authentifizierung verwendet Etsy?

OAuth 2.0 – User autorisieren deine App, du erhältst Access- und Refresh-Token.

Wie gehe ich mit Etsy API-Ratenlimits um?

Warteschlange & Rate Limiter nutzen. Quota-Header (x-etsy-quota-remaining) monitoren. Exponentielles Backoff bei 429-Fehlern.

Kann ich die API ohne Live-Shop testen?

Ja, mit Entwicklungsmodus und Testshops.

Wie funktionieren Webhooks?

Etsy sendet POSTs an deinen HTTPS-Webhook für relevante Events (Order/Listing). Endpoint muss 200 OK in <5s liefern, Signatur prüfen.

Was passiert, wenn ein OAuth Token abläuft?

Tokens laufen nach 1h ab. Vor Ablauf automatisch mit Refresh Token erneuern.

Kann ich Angebotsbilder via API hochladen?

Ja, separat als Base64-String, max. 20 MB, JPEG/PNG/GIF.

Wie migriere ich von V2 auf V3?

OAuth 2.0 anstatt 1.0a, Endpunkte auf /v3/application/ umstellen, Auth-Flow und Tests anpassen. Bis Ende 2026 migrieren.

Top comments (0)