TL;DR
L'API Etsy permet aux développeurs de créer des applications qui interagissent avec la marketplace d'Etsy. Elle utilise l'authentification OAuth 2.0, des points d'accès RESTful pour les boutiques, les fiches produits, les commandes et la gestion des stocks, avec des limites de débit de 10 appels par seconde et par application. Ce guide couvre la configuration de l'authentification, les points d'accès principaux, l'intégration de webhooks et les stratégies de déploiement en production.
Introduction
Etsy traite plus de 13 milliards de dollars de ventes brutes de marchandises annuelles dans plus de 230 pays. Pour les développeurs qui créent des outils de commerce électronique, des systèmes de gestion des stocks ou des plateformes d'analyse, l'intégration de l'API Etsy n'est pas une option, c'est une nécessité.
Voici la réalité : les vendeurs gérant plusieurs canaux de vente perdent 15 à 20 heures par semaine en saisie manuelle de données. Une solide intégration de l'API Etsy automatise la synchronisation des fiches produits, le traitement des commandes et les mises à jour des stocks sur toutes les plateformes.
Ce guide vous accompagne tout au long du processus complet d'intégration de l'API Etsy. Vous apprendrez l'authentification OAuth 2.0, la gestion des boutiques et des fiches produits, le traitement des commandes, la gestion des webhooks et le dépannage des erreurs. À la fin, vous disposerez d'une intégration Etsy prête pour la production.
Qu'est-ce que l'API Etsy ?
Etsy fournit une API RESTful pour accéder aux données du marché et gérer les opérations des vendeurs. L'API gère :
- La récupération des informations sur la boutique et le profil
- La création, les mises à jour et la gestion des stocks des fiches produits
- Le traitement des commandes et le suivi de l'exécution
- L'accès aux données des clients et des transactions
- Les profils d'expédition et les calculs de taxes
- La gestion du téléchargement d'images et de médias
Fonctionnalités clés
| Fonctionnalité | Description |
|---|---|
| Conception RESTful | Méthodes HTTP standard avec réponses JSON |
| OAuth 2.0 | Authentification sécurisée avec rafraîchissement des jetons d'accès |
| Webhooks | Notifications en temps réel pour les événements de commande et de fiche produit |
| Limitation de débit | 10 requêtes par seconde et par application (avec une allocation de rafale) |
| Support Sandbox | Environnement de test pour le développement sans données réelles |
Aperçu de l'architecture de l'API
Etsy utilise une structure d'API REST versionnée :
https://openapi.etsy.com/v3/application/
La version 3 (v3) est la norme actuelle, offrant un support OAuth 2.0 amélioré et des structures de points d'accès simplifiées par rapport à la v2.
Comparaison des versions de l'API
| Version | Statut | Authentification | Cas d'utilisation |
|---|---|---|---|
| V3 | Actuelle | OAuth 2.0 | Toutes les nouvelles intégrations |
| V2 | Dépréciée | OAuth 1.0a | Applications héritées uniquement |
| V1 | Retirée | N/A | Ne pas utiliser |
Migrez immédiatement toutes les intégrations V2 vers la V3. Etsy a annoncé la dépréciation de la V2 avec un retrait complet prévu pour fin 2026.
Premiers pas : Configuration de l'authentification
Étape 1 : Créez votre compte développeur Etsy
Avant d'accéder à l'API, vous avez besoin d'un compte développeur :
- Visitez le Portail des développeurs Etsy
- Connectez-vous avec votre compte Etsy (ou créez-en un)
- Accédez à Vos applications dans le tableau de bord des développeurs
- Cliquez sur Créer une nouvelle application
Étape 2 : Enregistrez votre application
Remplissez le formulaire d'enregistrement de l'application :
- Nom de l'application : Nom clair et descriptif (visible par les utilisateurs lors de l'OAuth)
- Description de l'application : Expliquez ce que fait votre application et qui l'utilise
- URI de redirection : Où Etsy renvoie les utilisateurs après l'authentification (doit utiliser HTTPS)
- Production/Développement : Commencez en mode développement pour les tests
Après soumission, vous recevrez :
- Chaîne de clé (Key String) : Votre identifiant API public
- Secret partagé (Shared Secret) : Votre secret API privé (ne le divulguez jamais)
Note de sécurité : Stockez les informations d'identification dans des variables d'environnement, jamais dans le code :
# 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"
Étape 3 : Comprendre le flux OAuth 2.0
Etsy utilise OAuth 2.0 pour l'authentification. Voici le flux complet :
1. L'utilisateur clique sur "Se connecter avec Etsy" dans votre application
2. Votre application redirige vers l'URL d'autorisation Etsy
3. L'utilisateur se connecte et accorde les permissions
4. Etsy redirige vers votre application avec le code d'autorisation
5. Votre application échange le code contre un jeton d'accès
6. Votre application utilise le jeton d'accès pour les appels API
7. Rafraîchissez le jeton lorsque le jeton d'accès expire (1 heure)
É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, // Chaîne aléatoire pour la protection CSRF
response_type: 'code'
});
return `${baseUrl}?${params.toString()}`;
};
// 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}`);
Portées requises
Demandez uniquement les autorisations dont votre application a besoin :
| Portée | Description | Cas d'utilisation |
|---|---|---|
listings_r |
Lire les fiches produits | Afficher les produits, synchroniser les stocks |
listings_w |
Écrire les fiches produits | Créer/mettre à jour les produits |
orders_r |
Lire les commandes | Gestion des commandes, exécution |
orders_w |
Écrire les commandes | Mettre à jour le statut des commandes, ajouter le suivi |
shops_r |
Lire les infos de la boutique | Afficher le profil de la boutique, analyses |
transactions_r |
Lire les transactions | Rapports financiers |
email |
Accéder à l'e-mail de l'acheteur | Communication de commande |
Étape 5 : Échanger le code contre un jeton d'accès
Gérez le rappel OAuth et échangez le code d'autorisation :
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();
// Stockez-les en toute sécurité dans votre base de données
return {
access_token: data.access_token,
refresh_token: data.refresh_token,
expires_in: data.expires_in, // Généralement 3600 secondes (1 heure)
user_id: data.user_id,
scope: data.scope
};
};
// Gérer la route de rappel
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Vérifiez que l'état correspond à ce que vous avez envoyé (protection CSRF)
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');
// Stocker les jetons dans la base de données associée à l'utilisateur
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');
}
});
Étape 6 : Implémenter le rafraîchissement des jetons
Les jetons d'accès expirent après 1 heure. Implémentez un rafraîchissement automatique :
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();
// Mettre à jour les jetons stockés
return {
access_token: data.access_token,
refresh_token: data.refresh_token, // Toujours enregistrer le nouveau jeton de rafraîchissement
expires_in: data.expires_in
};
};
// Middleware pour s'assurer d'un jeton valide avant les appels API
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
// Vérifier si le jeton expire dans les 5 minutes
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;
};
Étape 7 : Effectuer des appels API authentifiés
Incluez le jeton d'accès dans chaque requête :
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();
};
Points d'accès pour la gestion de la boutique
Récupération des informations sur la boutique
Récupérez les détails de la boutique, les politiques et les paramètres :
const getShopInfo = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}`, {
method: 'GET'
});
return response;
};
// Utilisation
const shop = await getShopInfo(12345678);
console.log(`Boutique : ${shop.title}`);
console.log(`Devise : ${shop.currency_code}`);
console.log(`Nombre d'annonces : ${shop.num_listings_active}`);
Réponse attendue de la boutique
{
"shop_id": 12345678,
"shop_name": "MyHandmadeShop",
"title": "Bijoux et accessoires faits main",
"announcement": "Bienvenue ! Livraison gratuite pour les commandes de plus de 50 $",
"currency_code": "USD",
"is_vacation": false,
"vacation_message": null,
"sale_message": "Merci de soutenir les petites entreprises !",
"digital_sale_message": null,
"created_timestamp": 1609459200,
"updated_timestamp": 1710950400,
"num_listings_active": 127,
"num_listings_sold": 1543,
"gaussian_alphas": {
"overall": 4.8,
"last_30_days": 4.9
},
"vacation_autoreply": null,
"url": "https://www.etsy.com/shop/MyHandmadeShop",
"image_url_760x100": "https://i.etsystatic.com/.../banner_760x100.jpg"
}
Récupération des sections de la boutique
Organisez les fiches produits par sections :
const getShopSections = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}/sections`, {
method: 'GET'
});
return response;
};
// Exemple de réponse
{
"count": 5,
"results": [
{
"shop_section_id": 12345,
"title": "Colliers",
"rank": 1,
"num_listings": 23
},
{
"shop_section_id": 12346,
"title": "Boucles d'oreilles",
"rank": 2,
"num_listings": 45
}
]
}
Gestion des fiches produits
Création d'une nouvelle fiche produit
Créez une fiche produit avec des images et des variations :
const createListing = async (shopId, listingData) => {
const payload = {
title: listingData.title,
description: listingData.description,
price: listingData.price.toString(), // Doit être une chaîne
quantity: listingData.quantity,
sku: listingData.sku || [],
tags: listingData.tags.slice(0, 13), // Max 13 tags
category_id: listingData.categoryId,
shop_section_id: listingData.sectionId,
state: listingData.state || 'active', // active, inactive, draft
who_made: listingData.whoMade, // i_did, someone_else, collective (je l'ai fait, quelqu'un d'autre, collectif)
when_made: listingData.whenMade, // 2020_2026, 2010_2019, etc. (2020_2026, 2010_2019, etc.)
is_supply: listingData.isSupply, // true pour les fournitures d'artisanat
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, // men, women, unisex, etc. (hommes, femmes, unisexe, etc.)
occasion: listingData.occasion || null, // birthday, wedding, etc. (anniversaire, mariage, etc.)
style: listingData.style || [] // Max 2 styles
};
const response = await makeEtsyRequest(`/shops/${shopId}/listings`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// Exemple d'utilisation
const listing = await createListing(12345678, {
title: 'Collier Phase de Lune en Argent Sterling',
description: 'Collier en argent sterling fait à la main représentant les phases de la lune...',
price: 89.99,
quantity: 15,
sku: ['LUNE-COLLIER-001'],
tags: ['collier lune', 'argent sterling', 'phase de lune', 'bijoux célestes'],
categoryId: 10623, // Bijoux > Colliers
sectionId: 12345,
state: 'active',
whoMade: 'i_did',
whenMade: 'made_to_order', // fait sur commande
isSupply: false,
weight: 25,
weightUnit: 'g'
});
Téléchargement d'images de fiches produits
Les images sont téléchargées séparément après la création de la fiche produit :
const uploadListingImage = async (listingId, imagePath, imagePosition = 1) => {
// Lire le fichier image en base64
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' // Pour l'accessibilité
};
const response = await makeEtsyRequest(`/listings/${listingId}/images`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// Télécharger plusieurs 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;
};
Mise à jour de l'inventaire des fiches produits
Mettez à jour la quantité pour les fiches produits existantes :
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, // est_en_survente
on_property: inventory.onProperty || []
};
const response = await makeEtsyRequest(
`/shops/${shopId}/listings/${listingId}/inventory`,
{
method: 'PUT',
body: JSON.stringify(payload)
}
);
return response;
};
// Utilisation
await updateListingInventory(12345678, 987654321, {
products: [
{ sku: 'LUNE-COLLIER-001', quantity: 10 },
{ sku: 'LUNE-COLLIER-002', quantity: 5 }
],
isOverSelling: false
});
Récupération des fiches produits
Récupérez toutes les fiches produits ou filtrez par statut :
const getListings = async (shopId, options = {}) => {
const params = new URLSearchParams({
limit: options.limit || 25, // Max 100
offset: options.offset || 0
});
if (options.state) {
params.append('state', options.state); // active, inactive, draft, sold_out (actif, inactif, brouillon, épuisé)
}
const response = await makeEtsyRequest(
`/shops/${shopId}/listings?${params.toString()}`,
{ method: 'GET' }
);
return response;
};
// Obtenir une seule fiche produit
const getListing = async (listingId) => {
const response = await makeEtsyRequest(`/listings/${listingId}`, {
method: 'GET'
});
return response;
};
Suppression d'une fiche produit
Supprimez une fiche produit de votre boutique :
const deleteListing = async (listingId) => {
const response = await makeEtsyRequest(`/listings/${listingId}`, {
method: 'DELETE'
});
return response;
};
Gestion des commandes
Récupération des commandes
Récupérez les commandes avec des options de filtrage :
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 (ouvert, terminé, annulé)
}
if (options.minLastModified) {
params.append('min_last_modified', options.minLastModified);
}
const response = await makeEtsyRequest(
`/shops/${shopId}/orders?${params.toString()}`,
{ method: 'GET' }
);
return response;
};
// Obtenir une seule commande
const getOrder = async (shopId, orderId) => {
const response = await makeEtsyRequest(`/shops/${shopId}/orders/${orderId}`, {
method: 'GET'
});
return response;
};
Structure de la réponse de la commande
{
"order_id": 1234567890,
"user_id": 98765432,
"shop_id": 12345678,
"buyer_user_id": 11223344,
"creation_timestamp": 1710864000,
"last_modified_timestamp": 1710950400,
"completed_timestamp": 1710950400,
"state": "complete", // état : terminé
"user_id_fob": null,
"is_guest": false, // est_invité : faux
"name": "Jeanne Dupont",
"email": "jeanne.dupont@email.com",
"buyer_phone_number": "+33-1-23-45-67-89",
"total_price": "89.99",
"total_shipping_cost": "5.95",
"total_tax": "7.65",
"grand_total": "103.59",
"currency_code": "USD",
"payment_method": "credit_card", // méthode_de_paiement : carte_de_crédit
"shipping_address": { // adresse_de_livraison
"name": "Jeanne Dupont",
"address_line1": "123 Rue Principale",
"address_line2": "Appt 4B",
"city": "Paris",
"state": "Île-de-France",
"zip": "75001",
"country": "FR",
"phone": "+33-1-23-45-67-89"
},
"listings": [
{
"listing_id": 987654321,
"title": "Collier Phase de Lune en Argent Sterling",
"sku": ["LUNE-COLLIER-001"],
"quantity": 1,
"price": "89.99"
}
]
}
Mise à jour du statut de la commande
Marquez les commandes comme terminées et ajoutez le suivi :
const updateOrderStatus = async (shopId, orderId, trackingData) => {
const payload = {
carrier_id: trackingData.carrierId, // usps, fedex, ups, etc. (transporteur : la poste, fedex, ups, etc.)
tracking_code: trackingData.trackingCode, // code_de_suivi
should_send_bcc_to_buyer: trackingData.notifyBuyer || true // doit_envoyer_cc_a_l'acheteur
};
const response = await makeEtsyRequest(
`/shops/${shopId}/orders/${orderId}/shipping`,
{
method: 'POST',
body: JSON.stringify(payload)
}
);
return response;
};
// Utilisation
await updateOrderStatus(12345678, 1234567890, {
carrierId: 'laposte', // La Poste
trackingCode: '9400111899223456789012',
notifyBuyer: true // notifier l'acheteur
});
ID de transporteurs courants
| Transporteur | ID Transporteur |
|---|---|
| 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
Comprendre les limites de débit
Etsy applique des limites de débit pour protéger la stabilité de l'API :
- Limite standard : 10 requêtes par seconde et par application
- Allocation de rafale : Jusqu'à 50 requêtes en une seule seconde (courtes rafales)
- Système de quota : 10 000 appels par heure et par application (réinitialisation horaire)
Dépasser les limites entraîne des réponses HTTP 429 (Too Many Requests).
Implémentation de la gestion des limites de débit
Utilisez l'attente exponentielle pour les nouvelles tentatives :
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await makeEtsyRequest(endpoint, options);
// Vérifier les en-têtes de limite de débit
const remaining = response.headers.get('x-etsy-quota-remaining'); // quota restant
const resetTime = response.headers.get('x-etsy-quota-reset'); // temps de réinitialisation
if (remaining < 100) {
console.warn(`Quota faible restant : ${remaining}, réinitialisation à ${resetTime}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
// Attente exponentielle : 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
console.log(`Limite de débit atteinte. Nouvelle tentative dans ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
En-têtes de limite de débit
Etsy inclut ces en-têtes dans chaque réponse :
| En-tête | Description |
|---|---|
x-etsy-quota-remaining |
Appels restants dans l'heure actuelle |
x-etsy-quota-reset |
Horodatage Unix de réinitialisation du quota |
x-etsy-limit-remaining |
Appels restants dans la seconde actuelle |
x-etsy-limit-reset |
Horodatage Unix de réinitialisation de la limite par seconde |
Enregistrez toujours ces en-têtes pour la surveillance et le débogage.
Intégration des webhooks
Configuration des webhooks
Etsy prend en charge les webhooks pour les notifications d'événements en temps réel :
- Accédez à Vos applications dans le tableau de bord des développeurs
- Sélectionnez votre application
- Cliquez sur Ajouter un Webhook
- Entrez l'URL de votre point d'accès HTTPS
- Sélectionnez les événements auxquels vous souhaitez vous abonner
Événements de webhook disponibles
| Type d'événement | Déclencheur | Cas d'utilisation |
|---|---|---|
v3/shops/{shop_id}/orders/create |
Nouvelle commande passée | Envoyer une confirmation, commencer l'exécution |
v3/shops/{shop_id}/orders/update |
Statut de la commande modifié | Synchroniser le statut de la commande |
v3/shops/{shop_id}/listings/create |
Nouvelle fiche produit créée | Mettre à jour l'inventaire externe |
v3/shops/{shop_id}/listings/update |
Fiche produit modifiée | Synchroniser les données du produit |
v3/shops/{shop_id}/listings/delete |
Fiche produit supprimée | Supprimer des systèmes externes |
Création du gestionnaire de webhooks
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;
// Vérifier la signature du webhook
const isValid = verifyWebhookSignature(payload, signature, process.env.ETSY_WEBHOOK_SECRET);
if (!isValid) {
console.error('Signature de webhook invalide');
return res.status(401).send('Non autorisé');
}
const event = JSON.parse(payload.toString());
// Acheminer vers le gestionnaire approprié
switch (event.type) {
case 'v3/shops/*/orders/create':
await handleNewOrder(event.data); // gérer la nouvelle commande
break;
case 'v3/shops/*/orders/update':
await handleOrderUpdate(event.data); // gérer la mise à jour de la commande
break;
case 'v3/shops/*/listings/create':
await handleListingCreated(event.data); // gérer la création de la fiche produit
break;
case 'v3/shops/*/listings/update':
await handleListingUpdated(event.data); // gérer la mise à jour de la fiche produit
break;
case 'v3/shops/*/listings/delete':
await handleListingDeleted(event.data); // gérer la suppression de la fiche produit
break;
default:
console.log('Type d\'événement non géré :', event.type);
}
// Accuser réception dans les 5 secondes
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')
);
}
Bonnes pratiques pour les webhooks
- Vérifier les signatures - Empêche les webhooks falsifiés
- Retourner rapidement un 200 OK - Etsy réessaie sur les réponses non-200 dans les 5 secondes
- Traiter de manière asynchrone - Mettre en file d'attente les événements pour un traitement en arrière-plan
- Implémenter l'idempotence - Gérer les livraisons de webhooks en double
- Enregistrer tous les événements - Déboguer les problèmes avec une piste d'audit horodatée
Dépannage des problèmes courants
Problème : Échange de jetons OAuth échoue
Symptômes : Obtention d'erreurs 401 ou 403 lors de l'authentification.
Diagnostic :
// Vérifier la réponse d'erreur
const error = await response.json();
console.error('Erreur OAuth :', error);
Solutions :
- Vérifiez que l'URI de redirection correspond exactement (y compris https:// et la barre oblique finale)
- Confirmez que client_id et client_secret sont corrects
- Assurez-vous que le code d'autorisation n'a pas expiré (les codes expirent après 1 utilisation ou 5 minutes)
- Vérifiez que l'application est en mode production (les applications de développement ne peuvent accéder qu'aux comptes de test)
Problème : Limite de débit dépassée
Symptômes : Réception de réponses HTTP 429.
Solutions :
- Implémentez une file d'attente de requêtes avec limitation de débit
- Utilisez l'attente exponentielle pour les nouvelles tentatives
- Regroupez les requêtes lorsque c'est possible (par exemple, récupérer plusieurs fiches produits en un seul appel)
- Surveillez les en-têtes de quota et ralentissez de manière proactive
// Limiteur de débit simple
class RateLimiter {
constructor(requestsPerSecond = 9) { // Rester sous la limite de 10/s
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'));
Problème : La création de fiches produits échoue avec des erreurs de validation
Symptômes : 400 Bad Request avec des messages d'erreur de validation.
Causes courantes :
- `category_id` invalide : Utilisez l'API des catégories d'Etsy pour obtenir des ID valides
- Format du prix : Doit être une chaîne, pas un nombre
- Limite de tags : Maximum 13 tags par fiche produit
- Champs obligatoires manquants : title, description, price, quantity, who_made, when_made
Solution :
// Valider avant d'envoyer
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;
};
Problème : Les webhooks n'arrivent pas
Symptômes : Les commandes sont traitées mais le point d'accès du webhook ne reçoit rien.
Diagnostic :
- Vérifiez les journaux de livraison des webhooks dans le tableau de bord du développeur
- Vérifiez que le point d'accès renvoie 200 OK dans les 5 secondes
- Testez le point d'accès manuellement avec curl
Solutions :
- Assurez-vous d'utiliser HTTPS avec un certificat SSL valide
- Ajoutez les adresses IP des webhooks Etsy à votre liste blanche dans le pare-feu
- Vérifiez la logique de vérification de signature
- Utilisez des outils de test de webhook pendant le développement
Problème : Le téléchargement d'images échoue
Symptômes : La fiche produit est créée mais les images renvoient des erreurs.
Solutions :
- Vérifiez que l'image est d'un format valide (JPEG, PNG, GIF)
- Vérifiez la taille du fichier (max 20 Mo par image)
- Assurez-vous que l'encodage base64 est correct
- Confirmez que la fiche produit existe avant de télécharger les images
- Téléchargez les images séquentiellement, pas en parallèle
Liste de contrôle pour le déploiement en production
Avant de passer en ligne :
- [ ] Basculez du mode application de développement au mode production
- [ ] Mettez à jour toutes les URI de redirection vers les URL de production
- [ ] Implémentez un stockage sécurisé des jetons (base de données cryptée)
- [ ] Ajoutez une logique de rafraîchissement automatique des jetons
- [ ] Configurez la limitation de débit et la mise en file d'attente des requêtes
- [ ] Configurez le point d'accès du webhook avec HTTPS
- [ ] Implémentez une gestion complète des erreurs
- [ ] Ajoutez la journalisation pour tous les appels API
- [ ] Mettez en place la surveillance de l'utilisation du quota
- [ ] Créez un manuel d'exploitation pour les problèmes courants
- [ ] Testez avec plusieurs comptes de boutique
- [ ] Documentez le flux OAuth pour l'intégration des utilisateurs
Surveillance et alertes
Suivez ces métriques :
// Exemple de métriques à suivre
const metrics = {
apiCalls: { // appels API
total: 0,
successful: 0, // réussis
failed: 0, // échoués
rateLimited: 0 // limités en débit
},
quotaUsage: { // utilisation du quota
current: 0, // actuel
limit: 10000,
resetTime: null // temps de réinitialisation
},
oauthTokens: { // jetons OAuth
active: 0, // actifs
expiring_soon: 0, // expirant bientôt
refresh_failures: 0 // échecs de rafraîchissement
},
webhooks: {
received: 0, // reçus
processed: 0, // traités
failed: 0 // échoués
}
};
// Alerte en cas de taux d'échec élevé
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;
if (failureRate > 0.05) { // Plus de 5% de taux d'échec
sendAlert('Taux d\'échec de l\'API Etsy supérieur à 5%');
}
// Alerte en cas de quota faible
if (metrics.quotaUsage.current < 500) {
sendAlert('Quota API Etsy inférieur à 500 appels restants');
}
Cas d'utilisation réels
Synchronisation des stocks multicanal
Un vendeur de décoration intérieure utilise l'API Etsy pour synchroniser les stocks entre Etsy, Shopify et Amazon :
- Défi : Les mises à jour manuelles des stocks entraînaient des surventes
- Solution : Système d'inventaire centralisé avec webhooks API Etsy
- Résultat : Zéro incident de survente, 12 heures/semaine économisées
Flux d'implémentation :
- Le webhook Etsy se déclenche à la création d'une commande
- Le système central décrémente l'inventaire
- Les appels API mettent à jour les quantités sur Shopify et Amazon
- La confirmation est enregistrée dans la base de données
Exécution automatisée des commandes
Une entreprise d'impression à la demande automatise le traitement des commandes :
- Défi : Plus de 50 commandes quotidiennes nécessitaient une saisie manuelle
- Solution : Intégration de l'API Etsy avec un fournisseur d'exécution
- Résultat : Commandes acheminées automatiquement vers la production en moins de 5 minutes
Points d'intégration clés :
- Le webhook écoute les événements `orders/create`
- Les détails de la commande sont envoyés à l'API du fournisseur d'impression
- Le numéro de suivi est retourné et mis à jour via l'API Etsy
- Le client reçoit une notification d'expédition automatique
Tableau de bord analytique
Un outil d'analyse pour vendeurs agrège les données de plusieurs boutiques :
- Défi : Les vendeurs avec plusieurs boutiques manquaient de rapports unifiés
- Solution : Agrégation de données multi-boutiques basée sur OAuth
- Résultat : Tableau de bord en temps réel avec les métriques de ventes, de trafic et de conversion
Données collectées via l'API :
- Statistiques de la boutique (fiches produits, ventes, revenus)
- Historique et tendances des commandes
- Métriques de performance des fiches produits
- Données des avis clients
Conclusion
L'API Etsy offre un accès complet aux fonctionnalités du marché. Points clés à retenir :
- L'authentification OAuth 2.0 nécessite une gestion attentive des jetons et un rafraîchissement automatique
- La limitation de débit (10 requêtes/s, 10K/heure) exige une surveillance proactive et une mise en file d'attente
- Les webhooks permettent une synchronisation en temps réel des commandes et des stocks
- Une gestion appropriée des erreurs et une logique de nouvelle tentative sont essentielles pour la fiabilité en production
- Apidog simplifie les tests API et la collaboration d'équipe pour les intégrations Etsy
Section FAQ
À quoi sert l'API Etsy ?
L'API Etsy permet aux développeurs de créer des applications qui interagissent avec la marketplace d'Etsy. Les cas d'utilisation courants incluent la gestion des stocks sur plusieurs canaux de vente, l'exécution automatisée des commandes, les tableaux de bord analytiques, les outils de création de fiches produits et les systèmes de gestion de la relation client.
Comment obtenir une clé API Etsy ?
Créez un compte sur le Portail des développeurs Etsy, accédez à Vos applications et cliquez sur Créer une nouvelle application. Après l'enregistrement, vous recevrez une chaîne de clé (identifiant public) et un secret partagé (clé privée). Stockez les deux en toute sécurité à l'aide de variables d'environnement.
L'API Etsy est-elle gratuite ?
Oui, l'API Etsy est gratuite pour les développeurs. Cependant, des limites de débit s'appliquent : 10 requêtes par seconde et 10 000 appels par heure et par application. Des limites plus élevées nécessitent l'approbation d'Etsy pour des cas d'utilisation spécifiques.
Quelle authentification l'API Etsy utilise-t-elle ?
Etsy utilise OAuth 2.0 pour l'authentification. Les utilisateurs autorisent votre application via la page d'autorisation d'Etsy, et votre application reçoit un jeton d'accès (valide pendant 1 heure) et un jeton de rafraîchissement (pour obtenir de nouveaux jetons d'accès).
Comment gérer les limites de débit de l'API Etsy ?
Implémentez une file d'attente de requêtes pour rester en dessous de 10 requêtes par seconde. Surveillez l'en-tête x-etsy-quota-remaining pour suivre l'utilisation horaire. Utilisez l'attente exponentielle lors de la réception de réponses HTTP 429 (Too Many Requests).
Puis-je tester l'API Etsy sans une boutique réelle ?
Oui. Les applications en mode développement peuvent se connecter à des boutiques de test pour l'intégration. Créez un compte Etsy de test et utilisez-le pour authentifier votre application de développement sans affecter les données réelles.
Comment fonctionnent les webhooks avec l'API Etsy ?
Les webhooks Etsy envoient des requêtes POST à votre point d'accès HTTPS lorsque des événements se produisent (nouvelles commandes, mises à jour des fiches produits). Configurez les webhooks dans le tableau de bord de votre application, implémentez la vérification de signature et renvoyez un 200 OK dans les 5 secondes.
Que se passe-t-il lorsque le jeton OAuth Etsy expire ?
Les jetons d'accès expirent après 1 heure. Utilisez le jeton de rafraîchissement pour obtenir un nouveau jeton d'accès avant l'expiration. Implémentez un rafraîchissement automatique des jetons dans votre middleware pour éviter les échecs d'authentification lors des appels API.
Puis-je télécharger des images de fiches produits via l'API ?
Oui. Les images sont téléchargées sous forme de chaînes encodées en base64 dans un appel API distinct après la création de la fiche produit. Chaque image peut peser jusqu'à 20 Mo et doit être au format JPEG, PNG ou GIF.
Comment migrer de l'API Etsy V2 vers la V3 ?
La V3 utilise OAuth 2.0 au lieu d'OAuth 1.0a et possède une structure de point d'accès différente. Mettez à jour le flux d'authentification, modifiez les chemins des points d'accès de /v2/ à /v3/application/, et testez minutieusement avant le retrait de la V2 fin 2026.
