DEV Community

Cover image for Comment utiliser l'API Etsy : Guide d'intégration complet (2026)
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Comment utiliser l'API Etsy : Guide d'intégration complet (2026)

TL;DR

L'API Etsy permet d'automatiser la gestion des boutiques, produits, commandes et stocks via OAuth 2.0 et des endpoints RESTful, avec une limite de 10 appels/seconde/app. Ce guide propose un parcours complet : authentification, endpoints clés, webhooks, gestion de la production et résolution des problèmes.

Essayez Apidog dès aujourd'hui


Introduction

Etsy gère plus de 13 milliards de dollars de ventes annuelles dans plus de 230 pays. Pour les développeurs d'outils e-commerce, d'inventaire ou d'analytique, intégrer l'API Etsy est essentiel.

Les vendeurs multicanal perdent 15 à 20 heures/semaine en saisie manuelle de données. L'intégration API automatise la synchronisation produits, le traitement des commandes et la gestion des stocks sur toutes les plateformes.

Ce guide détaille chaque étape d'une intégration Etsy robuste : OAuth 2.0, gestion de boutique, gestion des fiches produits, traitement des commandes, webhooks et résolution d'erreurs. À la fin, vous aurez une intégration prête pour la production.

💡 Apidog facilite les tests d'intégration API : testez vos endpoints Etsy, validez l'OAuth, inspectez les payloads webhooks et déboguez l'authentification dans un seul espace de travail. Importez des specs API, simulez des réponses et partagez vos scénarios de test avec l'équipe.


Qu'est-ce que l'API Etsy ?

Etsy fournit une API RESTful pour automatiser :

  • Récupération d'infos boutique/profil
  • Création/mise à jour/gestion stock de fiches produits
  • Traitement des commandes & suivi logistique
  • Accès aux données clients et transactions
  • Profils d'expédition & calculs de taxes
  • Upload d'images et médias

Fonctionnalités clés

Fonctionnalité Description
RESTful Méthodes HTTP standard, réponses JSON
OAuth 2.0 Authentification sécurisée, rafraîchissement des tokens
Webhooks Notifications temps réel (commandes, fiches produits)
Limitation débit 10 requêtes/s/app (rafale autorisée)
Sandbox Environnement de test sans données réelles

Aperçu architecture de l'API

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

Utilisez la version 3 (v3) pour toutes nouvelles intégrations (OAuth 2.0, endpoints simplifiés). Migrez rapidement vos intégrations v2, dépréciées à fin 2026.


Premiers pas : Configuration de l'authentification

Étape 1 : Créer un compte développeur Etsy

  1. Rendez-vous sur le Portail développeurs Etsy
  2. Connectez-vous ou créez un compte Etsy
  3. Accédez à Vos applications dans le dashboard
  4. Cliquez sur Créer une nouvelle application

Étape 2 : Enregistrer votre application

Remplissez le formulaire :

  • Nom : descriptif, visible lors d'OAuth
  • Description : ce que fait l'app, public visé
  • URI de redirection : URL HTTPS de retour OAuth
  • Production/Développement : commencer en développement

Après validation :

  • Key String : identifiant API public
  • Shared Secret : secret privé (gardez-le sécurisé)

Stockez ces infos dans un fichier .env :

ETSY_KEY_STRING="votre_chaine_de_cle_ici"
ETSY_SHARED_SECRET="votre_secret_partage_ici"
ETSY_ACCESS_TOKEN="genere_via_oauth"
ETSY_REFRESH_TOKEN="genere_via_oauth"
Enter fullscreen mode Exit fullscreen mode

Étape 3 : Comprendre le flux OAuth 2.0

Flux standard :

1. L'utilisateur clique "Se connecter avec Etsy"
2. Redirection vers l'URL d'autorisation Etsy
3. Connexion & consentement utilisateur
4. Etsy redirige vers votre app avec le code d'autorisation
5. Votre app échange le code contre un access token
6. Votre app appelle l'API avec ce token
7. Rafraîchissez le token à expiration (1h)
Enter fullscreen mode Exit fullscreen mode

Étape 4 : Implémenter l'autorisation OAuth

Générez l'URL d'autorisation :

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, // Protection CSRF
    response_type: 'code'
  });
  return `${baseUrl}?${params.toString()}`;
};

// Exemple d'utilisation
const authUrl = generateAuthUrl(
  process.env.ETSY_KEY_STRING,
  'https://votre-app.com/callback',
  crypto.randomBytes(16).toString('hex')
);
console.log(`Rediriger l'utilisateur vers : ${authUrl}`);
Enter fullscreen mode Exit fullscreen mode

Portées requises

Demandez uniquement les scopes nécessaires :

Portée Description Cas d'utilisation
listings_r Lire fiches produits Affichage, synchronisation stocks
listings_w Écrire fiches produits Création/màj produits
orders_r Lire commandes Gestion/exécution
orders_w Modifier commandes Statut, ajout suivi
shops_r Lire infos boutique Profil, analytique
transactions_r Lire transactions Rapports financiers
email Accès e-mail acheteur Notifications clients

Étape 5 : Échanger le code contre un access token

Gérez le callback OAuth :

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();
  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in,
    user_id: data.user_id,
    scope: data.scope
  };
};

// Route de callback
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  if (state !== req.session.oauthState) {
    return res.status(400).send('Paramètre d\'état invalide');
  }
  try {
    const tokens = await exchangeCodeForToken(code, 'https://votre-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('/tableau-de-bord');
  } catch (error) {
    console.error('Échange de jetons échoué :', error);
    res.status(500).send('Authentification échouée');
  }
});
Enter fullscreen mode Exit fullscreen mode

Étape 6 : Rafraîchir automatiquement les tokens

Les tokens expirent après 1h. Implémentez le rafraîchissement :

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

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

Étape 7 : Effectuer des appels authentifiés

Incluez le token dans chaque requête Etsy :

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(`Erreur API Etsy : ${error.message}`);
  }
  return response.json();
};
Enter fullscreen mode Exit fullscreen mode

Points d'accès pour la gestion de la boutique

Récupérer les informations boutique

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

Réponse :

{
  "shop_id": 12345678,
  "shop_name": "MyHandmadeShop",
  "title": "Bijoux et accessoires faits main",
  ...
}
Enter fullscreen mode Exit fullscreen mode

Récupérer les sections de la boutique

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

Gestion des fiches produits

Créer une fiche produit

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

Upload d'images pour fiche produit

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: 'Collier phase de lune en argent sterling fait main'
  };
  const response = await makeEtsyRequest(`/listings/${listingId}/images`, {
    method: 'POST',
    body: JSON.stringify(payload)
  });
  return response;
};
Enter fullscreen mode Exit fullscreen mode

Mettre à jour l'inventaire

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

Lire, supprimer, filtrer les fiches produits

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);
  const response = await makeEtsyRequest(
    `/shops/${shopId}/listings?${params.toString()}`,
    { method: 'GET' }
  );
  return response;
};

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

Gestion des commandes

Récupérer et filtrer les commandes

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

Mettre à jour le statut de la commande

const updateOrderStatus = async (shopId, orderId, trackingData) => {
  const payload = {
    carrier_id: trackingData.carrierId,
    tracking_code: trackingData.trackingCode,
    should_send_bcc_to_buyer: trackingData.notifyBuyer || true
  };
  return await makeEtsyRequest(
    `/shops/${shopId}/orders/${orderId}/shipping`,
    {
      method: 'POST',
      body: JSON.stringify(payload)
    }
  );
};
Enter fullscreen mode Exit fullscreen mode

ID transporteurs courants

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

Limitation de débit et quotas

Limites Etsy

  • Standard : 10 requêtes/seconde/app
  • Rafale : jusqu'à 50 en une seconde
  • Quota : 10 000 appels/heure/app

HTTP 429 si dépassement.

Gestion de la limitation

Attente exponentielle :

const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await makeEtsyRequest(endpoint, options);
      // Surveillez les headers de quota pour adapter le rythme
      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

Surveillez les headers :

En-tête Description
x-etsy-quota-remaining Appels restants (heure)
x-etsy-quota-reset Réinitialisation quota (epoch)
x-etsy-limit-remaining Appels restants (seconde)
x-etsy-limit-reset Réinitialisation limite

Intégration des webhooks

Configuration

  1. Allez dans Vos applications sur le dashboard Etsy
  2. Sélectionnez l'application
  3. Cliquez Ajouter un Webhook
  4. Renseignez votre endpoint HTTPS
  5. Choisissez les événements souhaités
Événement Déclencheur Cas d'utilisation
v3/shops/{shop_id}/orders/create Nouvelle commande Confirmation, exécution
v3/shops/{shop_id}/orders/update Statut changé Sync statut
v3/shops/{shop_id}/listings/create Nouvelle fiche Sync inventaire
v3/shops/{shop_id}/listings/update Fiche modifiée Sync produit
v3/shops/{shop_id}/listings/delete Fiche supprimée Nettoyage externe

Implémenter un handler webhook

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('Non autorisé');
  }
  const event = JSON.parse(payload.toString());
  switch (event.type) {
    case 'v3/shops/*/orders/create':
      await handleNewOrder(event.data);
      break;
    // autres cas...
    default:
      console.log('Type d\'événement non géré :', 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

Bonnes pratiques webhooks

  1. Vérifiez la signature systématiquement
  2. Répondez 200 OK sous 5s (sinon Etsy retente)
  3. Traitez en async (file d'attente)
  4. Implémentez l'idempotence (événements en double)
  5. Logguez tous les événements pour debug

Dépannage des problèmes courants

Échec échange de jetons OAuth (401/403)

  • Vérifiez URI de redirection exacte (HTTPS, slash final)
  • Vérifiez client_id et client_secret
  • Le code d'autorisation expire après 1 utilisation/5min
  • App en mode développement : accès limité

Limite de débit dépassée (429)

  • Implémentez file d'attente + rate limiting
  • Attente exponentielle sur nouvelle tentative
  • Groupez les requêtes si possible
  • Surveillez les headers de quota

Rate limiter simple :

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

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

Erreur validation fiche produit (400)

  • category_id invalide : vérifiez via l'API catégories
  • price doit être une chaîne
  • Max 13 tags/fiches
  • Champs obligatoires : title, description, price, quantity, who_made, when_made
const validateListing = (data) => {
  const errors = [];
  if (!data.title || data.title.length < 5) errors.push('Le titre doit contenir au moins 5 caractères');
  if (typeof data.price !== 'string') errors.push('Le prix doit être une chaîne');
  if (data.tags && data.tags.length > 13) errors.push('Maximum 13 tags autorisés');
  if (!['i_did', 'someone_else', 'collective'].includes(data.whoMade)) errors.push('Valeur who_made invalide');
  return errors;
};
Enter fullscreen mode Exit fullscreen mode

Webhooks non reçus

  • Vérifiez les logs de livraison webhooks Etsy
  • Vérifiez que le endpoint répond 200 OK < 5s
  • Testez manuellement avec curl
  • Assurez HTTPS + certificat SSL valide
  • Liste blanche des IPs Etsy dans le firewall
  • Vérifiez la logique de signature

Problème upload images

  • Format valide (JPEG, PNG, GIF)
  • Taille < 20 Mo/image
  • Encodage base64 correct
  • Fiche produit existante
  • Uploader les images séquentiellement

Liste de contrôle de production

  • [ ] Passez en mode production
  • [ ] Mettez à jour les URI de redirection
  • [ ] Stockage sécurisé des tokens (DB chiffrée)
  • [ ] Rafraîchissement automatique des tokens
  • [ ] Rate limiting & file d'attente des requêtes
  • [ ] Webhook HTTPS configuré
  • [ ] Gestion complète des erreurs
  • [ ] Journalisation des appels API
  • [ ] Surveillance de quota
  • [ ] Manuel d'exploitation
  • [ ] Tests multi-comptes boutiques
  • [ ] Documentation du flow OAuth

Surveillance & alertes

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('Taux d\'échec de l\'API Etsy supérieur à 5%'); }
if (metrics.quotaUsage.current < 500) { sendAlert('Quota API Etsy inférieur à 500 appels restants'); }
Enter fullscreen mode Exit fullscreen mode

Cas d'utilisation réels

Synchronisation des stocks multicanal

  • Défi : Stocks manuels, surventes
  • Solution : Système centralisé + webhooks Etsy
  • Résultat : Zéro survente, 12h/semaine gagnées

Implémentation :

  1. Webhook Etsy déclenche à la création commande
  2. Inventaire central décrémente produit
  3. Màj stocks Shopify/Amazon via API
  4. Confirmation stockée

Exécution automatisée des commandes

  • Défi : 50+ commandes/jour, saisie manuelle
  • Solution : Intégration API Etsy + fournisseur d'exécution
  • Résultat : Commandes transmises en <5min

Tableau de bord analytique

  • Défi : Rapports éclatés multi-boutiques
  • Solution : Agrégation des données via OAuth
  • Résultat : Dashboard temps réel (ventes, trafic, conversion)

Conclusion

  • OAuth 2.0 demande une gestion attentive des tokens et leur rafraîchissement automatique
  • Le rate limiting (10/s, 10K/h) impose file d'attente & surveillance proactive
  • Les webhooks permettent la synchronisation temps réel
  • La gestion d'erreur et retry est cruciale en production
  • Apidog simplifie les tests API et la collaboration sur les intégrations Etsy

Section FAQ

À quoi sert l'API Etsy ?

L'API Etsy permet de développer des applications pour la gestion multi-canal des stocks, l'automatisation des commandes, la création de fiches produits, l'analytique et le CRM.

Comment obtenir une clé API Etsy ?

Créez un compte sur le portail développeurs Etsy, puis dans Vos applications, cliquez sur Créer une nouvelle application. Stockez la clé publique et le secret en variables d'environnement.

L'API Etsy est-elle gratuite ?

Oui, dans la limite de 10 requêtes/s et 10 000 appels/h/app. Plus de quota ? Demander une extension à Etsy.

Quelle authentification utilise l’API Etsy ?

OAuth 2.0. L'utilisateur autorise l'app, qui reçoit un access token (1h) + refresh token.

Comment gérer les limites de débit Etsy ?

File d'attente de requêtes, respecter les headers x-etsy-quota-remaining. Attente exponentielle sur 429.

Peut-on tester l’API sans boutique réelle ?

Oui, en mode développement sur une boutique de test.

Comment fonctionnent les webhooks Etsy ?

Etsy POST des événements à votre endpoint HTTPS à chaque action (commande, fiche produit). Vérifiez la signature, répondez 200 OK sous 5 s.

Que se passe-t-il quand le token OAuth expire ?

Il expire après 1h. Utilisez le refresh token pour en obtenir un nouveau et rafraîchissez automatiquement côté serveur.

Peut-on uploader des images via l’API ?

Oui, après la création de la fiche produit, via POST base64 (max 20 Mo, JPEG/PNG/GIF).

Comment migrer de l’API Etsy V2 vers V3 ?

Passez à OAuth 2.0, adaptez les endpoints (/v3/application/), testez à fond avant la fin de V2 (2026).

Top comments (0)