En resumen
La API de Etsy permite a los desarrolladores crear aplicaciones que interactúan con el marketplace de Etsy. Utiliza autenticación OAuth 2.0, puntos finales RESTful para tiendas, listados, pedidos y gestión de inventario, con límites de tasa de 10 llamadas por segundo por aplicación. Esta guía cubre la configuración de autenticación, los puntos finales principales, la integración de webhooks y las estrategias de despliegue en producción.
Introducción
Etsy procesa más de $13 mil millones en ventas brutas anuales de mercancías en más de 230 países. Para los desarrolladores que crean herramientas de comercio electrónico, sistemas de gestión de inventario o plataformas de análisis, la integración de la API de Etsy no es opcional, es esencial.
Esta es la realidad: los vendedores que gestionan múltiples canales de venta pierden entre 15 y 20 horas semanales en la entrada manual de datos. Una sólida integración con la API de Etsy automatiza la sincronización de listados, el procesamiento de pedidos y las actualizaciones de inventario en todas las plataformas.
Esta guía le guiará a través de todo el proceso de integración de la API de Etsy. Aprenderá sobre la autenticación OAuth 2.0, la gestión de tiendas y listados, el procesamiento de pedidos, el manejo de webhooks y la resolución de problemas de errores. Al final, tendrá una integración de Etsy lista para producción.
💡 Apidog simplifica las pruebas de integración de API. Pruebe sus puntos finales de Etsy, valide flujos de OAuth, inspeccione cargas de webhook y depure problemas de autenticación en un solo espacio de trabajo. Importe especificaciones de API, respuestas simuladas y comparta escenarios de prueba con su equipo.
¿Qué es la API de Etsy?
Etsy proporciona una API RESTful para acceder a los datos del marketplace y gestionar las operaciones del vendedor. La API maneja:
- Recuperación de información de la tienda y del perfil
- Creación, actualización y gestión de inventario de listados
- Procesamiento de pedidos y seguimiento de envíos
- Acceso a datos de clientes y transacciones
- Perfiles de envío y cálculos de impuestos
- Gestión de carga de imágenes y medios
Características clave
| Característica | Descripción |
|---|---|
| Diseño RESTful | Métodos HTTP estándar con respuestas JSON |
| OAuth 2.0 | Autenticación segura con actualización de token de acceso |
| Webhooks | Notificaciones en tiempo real para eventos de pedidos y listados |
| Limitación de Tasa | 10 solicitudes por segundo por aplicación (con asignación de ráfaga) |
| Soporte de Sandbox | Entorno de prueba para desarrollo sin datos reales |
Visión general de la arquitectura de la API
Etsy utiliza una estructura de API REST versionada:
https://openapi.etsy.com/v3/application/
La Versión 3 (v3) es el estándar actual, ofreciendo un soporte OAuth 2.0 mejorado y estructuras de puntos finales simplificadas en comparación con la v2.
Comparación de versiones de API
| Versión | Estado | Autenticación | Caso de Uso |
|---|---|---|---|
| V3 | Actual | OAuth 2.0 | Todas las nuevas integraciones |
| V2 | Obsoleta | OAuth 1.0a | Solo aplicaciones heredadas |
| V1 | Retirada | N/A | No usar |
Migre cualquier integración de V2 a V3 inmediatamente. Etsy anunció la obsolescencia de V2 con su retirada completa programada para finales de 2026.
Primeros pasos: Configuración de la autenticación
Paso 1: Cree su cuenta de desarrollador de Etsy
- Visite el Portal de Desarrolladores de Etsy
- Inicie sesión con su cuenta de Etsy (o cree una)
- Navegue a Sus Aplicaciones en el panel de control del desarrollador
- Haga clic en Crear una nueva aplicación
Paso 2: Registre su aplicación
Complete el formulario de registro de la aplicación:
- Nombre de la Aplicación: Nombre claro y descriptivo (visible para los usuarios durante OAuth)
- Descripción de la Aplicación: Explique qué hace su aplicación y quién la usa
- URI de Redirección: Donde Etsy envía a los usuarios después de la autenticación (debe usar HTTPS)
- Producción/Desarrollo: Comience con el modo de desarrollo para las pruebas
Después de la presentación, recibirá:
- Cadena de Clave (Key String): Su identificador de API público
- Secreto Compartido (Shared Secret): Su secreto de API privado (nunca lo exponga)
Nota de seguridad: Almacene las credenciales en variables de entorno, nunca en el código:
# .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"
Paso 3: Comprenda el flujo de OAuth 2.0
Etsy utiliza OAuth 2.0 para la autenticación. Aquí está el flujo completo:
1. El usuario hace clic en "Conectar con Etsy" en su aplicación
2. Su aplicación redirige a la URL de autorización de Etsy
3. El usuario inicia sesión y otorga permisos
4. Etsy redirige de vuelta con el código de autorización
5. Su aplicación intercambia el código por un token de acceso
6. Su aplicación usa el token de acceso para las llamadas a la API
7. Actualice el token cuando el token de acceso expire (1 hora)
Paso 4: Implementar la autorización OAuth
Genere la URL de autorización:
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, // Random string for CSRF protection
response_type: 'code'
});
return `${baseUrl}?${params.toString()}`;
};
// Uso
const authUrl = generateAuthUrl(
process.env.ETSY_KEY_STRING,
'https://your-app.com/callback',
crypto.randomBytes(16).toString('hex')
);
console.log(`Redirige al usuario a: ${authUrl}`);
Ámbitos requeridos
Solicite solo los permisos que su aplicación necesita:
| Ámbito | Descripción | Caso de Uso |
|---|---|---|
listings_r |
Leer listados | Mostrar productos, sincronizar inventario |
listings_w |
Escribir listados | Crear/actualizar productos |
orders_r |
Leer pedidos | Gestión de pedidos, cumplimiento |
orders_w |
Escribir pedidos | Actualizar estado de pedido, añadir seguimiento |
shops_r |
Leer información de la tienda | Mostrar perfil de la tienda, análisis |
transactions_r |
Leer transacciones | Informes financieros |
email |
Acceder al correo electrónico del comprador | Comunicación de pedidos |
Paso 5: Intercambiar Código por Token de Acceso
Maneje la devolución de llamada de OAuth e intercambie el código de autorización:
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();
// Almacene estos datos de forma segura en su base de datos
return {
access_token: data.access_token,
refresh_token: data.refresh_token,
expires_in: data.expires_in, // Normalmente 3600 segundos (1 hora)
user_id: data.user_id,
scope: data.scope
};
};
// Manejo de la ruta de callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Verifique que el state coincida (protección CSRF)
if (state !== req.session.oauthState) {
return res.status(400).send('Parámetro state inválido');
}
try {
const tokens = await exchangeCodeForToken(code, 'https://your-app.com/callback');
// Almacene los tokens en la base de datos asociados al usuario
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('Intercambio de token fallido:', error);
res.status(500).send('Fallo en la autenticación');
}
});
Paso 6: Implementar la actualización del token
Los tokens de acceso expiran después de 1 hora. Implemente la actualización automática:
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();
// Actualice los tokens almacenados
return {
access_token: data.access_token,
refresh_token: data.refresh_token, // Guarde siempre el nuevo refresh token
expires_in: data.expires_in
};
};
// Middleware para asegurar token válido antes de llamadas API
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
// ¿El token expira en menos de 5 minutos?
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;
};
Paso 7: Realizar llamadas a la API autenticadas
Incluya el token de acceso en cada solicitud:
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();
};
Puntos finales de gestión de la tienda
Recuperación de información de la tienda
Obtenga los detalles de la tienda, políticas y configuraciones:
const getShopInfo = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}`, {
method: 'GET'
});
return response;
};
// Uso
const shop = await getShopInfo(12345678);
console.log(`Shop: ${shop.title}`);
console.log(`Currency: ${shop.currency_code}`);
console.log(`Listings count: ${shop.num_listings_active}`);
Respuesta esperada de la tienda
{
"shop_id": 12345678,
"shop_name": "MyHandmadeShop",
"title": "Handmade Jewelry & Accessories",
"announcement": "Welcome! Free shipping on orders over $50",
"currency_code": "USD",
"is_vacation": false,
"vacation_message": null,
"sale_message": "Thank you for supporting small businesses!",
"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"
}
Recuperación de secciones de la tienda
Organice los listados por secciones:
const getShopSections = async (shopId) => {
const response = await makeEtsyRequest(`/shops/${shopId}/sections`, {
method: 'GET'
});
return response;
};
// Ejemplo de respuesta
{
"count": 5,
"results": [
{
"shop_section_id": 12345,
"title": "Necklaces",
"rank": 1,
"num_listings": 23
},
{
"shop_section_id": 12346,
"title": "Earrings",
"rank": 2,
"num_listings": 45
}
]
}
Gestión de listados
Creación de un nuevo listado
Cree un listado de producto con imágenes y variaciones:
const createListing = async (shopId, listingData) => {
const payload = {
title: listingData.title,
description: listingData.description,
price: listingData.price.toString(), // Debe ser string
quantity: listingData.quantity,
sku: listingData.sku || [],
tags: listingData.tags.slice(0, 13), // Máx 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
when_made: listingData.whenMade, // 2020_2026, 2010_2019, etc.
is_supply: listingData.isSupply, // true para insumos de manualidades
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.
occasion: listingData.occasion || null, // birthday, wedding, etc.
style: listingData.style || [] // Máx 2 estilos
};
const response = await makeEtsyRequest(`/shops/${shopId}/listings`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// Ejemplo de uso
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, // Jewelry > Necklaces
sectionId: 12345,
state: 'active',
whoMade: 'i_did',
whenMade: 'made_to_order',
isSupply: false,
weight: 25,
weightUnit: 'g'
});
Subir imágenes de listado
Las imágenes se cargan por separado después de la creación del listado:
const uploadListingImage = async (listingId, imagePath, imagePosition = 1) => {
// Leer archivo de imagen como 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: 'Handcrafted sterling silver moon phase necklace' // Para accesibilidad
};
const response = await makeEtsyRequest(`/listings/${listingId}/images`, {
method: 'POST',
body: JSON.stringify(payload)
});
return response;
};
// Subir múltiples imágenes
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;
};
Top comments (0)