API de administración de Shopify para desarrolladores de WooCommerce (2026)
Cómo los desarrolladores de WooCommerce utilizan la API de administración de Shopify: GraphQL vs REST, mutaciones de productos, gestión de inventario, consultas de pedidos, webhooks y portabilidad de la lógica de la API REST de WooCommerce a Shopify.
Los desarrolladores de WooCommerce que creaban integraciones personalizadas usaban la API REST de WooCommerce v3 — una interfaz REST para productos, pedidos, clientes y cupones. Shopify ofrece dos APIs de administración paralelas: GraphQL (recomendada) y REST (heredada). Esta guía conecta los patrones de la API REST de WooCommerce con sus equivalentes en la API de administración de Shopify, cubriendo las mutaciones y consultas que más utilizarás al crear migraciones, integraciones y herramientas de backend.
API REST de WooCommerce vs API de administración de Shopify
| Operación | REST de WooCommerce v3 | API de administración de Shopify (GraphQL) |
|---|---|---|
| Listar productos | GET /wp-json/wc/v3/products | query { products(first: 50) } |
| Crear producto | POST /wp-json/wc/v3/products | mutation productCreate |
| Actualizar producto | PUT /wp-json/wc/v3/products/{id} | mutation productUpdate |
| Eliminar producto | DELETE /wp-json/wc/v3/products/{id} | mutation productDelete |
| Listar pedidos | GET /wp-json/wc/v3/orders | query { orders(first: 50) } |
| Crear pedido | POST /wp-json/wc/v3/orders | mutation orderCreate |
| Actualizar inventario | PUT /products/{id} stock_quantity | mutation inventorySetQuantities |
| Listar clientes | GET /wp-json/wc/v3/customers | query { customers(first: 50) } |
| Establecer metafields | POST /wp-json/wc/v3/products/{id} meta_data | mutation metafieldsSet |
| Webhooks | Gestor de webhooks de WooCommerce | mutation webhookSubscriptionCreate |
Autenticación
La API de administración requiere un token de acceso de app privada (no el token de acceso de la Storefront API):
- Crear en: Admin → Apps → Desarrollar apps → crear app personalizada → Permisos de la API de administración.
- Conceder solo los alcances necesarios:
write_products,read_orders, etc. - Token de acceso:
shpat_xxxx(token de acceso a la API de administración — mantener solo en el servidor, nunca exponer en el cliente).
const SHOP_DOMAIN = 'tutienda.myshopify.com';
const ACCESS_TOKEN = process.env.SHOPIFY_ADMIN_ACCESS_TOKEN;
const endpoint = 'https://' + SHOP_DOMAIN + '/admin/api/2024-01/graphql.json';
async function adminQuery(query, variables = {}) {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': ACCESS_TOKEN,
},
body: JSON.stringify({ query, variables }),
});
const data = await response.json();
if (data.errors) throw new Error(JSON.stringify(data.errors));
return data;
}
Mutaciones de productos
Crear un producto (productCreate)
const CREATE_PRODUCT = `
mutation productCreate($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
variants(first: 10) {
edges { node { id sku price } }
}
}
userErrors { field message }
}
}
`;
const result = await adminQuery(CREATE_PRODUCT, {
input: {
title: "Cartera de cuero clásica",
bodyHtml: "<p>Cuero de grano completo, 6 ranuras para tarjetas</p>",
vendor: "Artisan Co",
productType: "Accesorios",
tags: ["cuero", "cartera", "accesorios"],
status: "ACTIVE",
variants: [{
price: "49.99",
sku: "CARTERA-NGR-001",
inventoryManagement: "SHOPIFY",
inventoryQuantities: [{
availableQuantity: 50,
locationId: "gid://shopify/Location/12345"
}]
}]
}
});
Actualizar un producto
const UPDATE_PRODUCT = `
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product { id title }
userErrors { field message }
}
}
`;
await adminQuery(UPDATE_PRODUCT, {
input: {
id: "gid://shopify/Product/12345",
title: "Título actualizado",
tags: ["cuero", "cartera", "nueva-llegada"]
}
});
Productos variables: crear con opciones y variantes
const CREATE_VARIABLE_PRODUCT = `
mutation productCreate($input: ProductInput!) {
productCreate(input: $input) {
product { id title variants(first: 50) { edges { node { id title sku } } } }
userErrors { field message }
}
}
`;
await adminQuery(CREATE_VARIABLE_PRODUCT, {
input: {
title: "Camiseta clásica",
options: ["Color", "Talla"],
variants: [
{ options: ["Negro", "S"], price: "29.99", sku: "CAMISETA-NGR-S", inventoryQuantities: [{ availableQuantity: 10, locationId: "gid://shopify/Location/12345" }] },
{ options: ["Negro", "M"], price: "29.99", sku: "CAMISETA-NGR-M", inventoryQuantities: [{ availableQuantity: 15, locationId: "gid://shopify/Location/12345" }] },
{ options: ["Blanco", "S"], price: "29.99", sku: "CAMISETA-BLC-S", inventoryQuantities: [{ availableQuantity: 8, locationId: "gid://shopify/Location/12345" }] },
{ options: ["Blanco", "M"], price: "29.99", sku: "CAMISETA-BLC-M", inventoryQuantities: [{ availableQuantity: 12, locationId: "gid://shopify/Location/12345" }] }
]
}
});
Metafields (reemplaza meta_data de WooCommerce)
WooCommerce almacenaba datos personalizados como arrays de meta_data en los productos. Shopify usa metafields estructurados con espacio de nombres + clave + tipo:
const SET_METAFIELDS = `
mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields { namespace key value type }
userErrors { field message }
}
}
`;
await adminQuery(SET_METAFIELDS, {
metafields: [
{
ownerId: "gid://shopify/Product/12345",
namespace: "product_info",
key: "material",
value: "Cuero de grano completo",
type: "single_line_text_field"
},
{
ownerId: "gid://shopify/Product/12345",
namespace: "product_info",
key: "weight_g",
value: "85",
type: "number_integer"
}
]
});
Gestión de inventario
WooCommerce almacena stock_quantity en el producto. Shopify separa el inventario en InventoryItem (el artículo) e InventoryLevel (cantidad por ubicación):
// Obtener primero el ID de ubicación
const LOCATIONS_QUERY = `
query { locations(first: 10) { edges { node { id name } } } }
`;
// Establecer la cantidad de inventario
const SET_INVENTORY = `
mutation inventorySetQuantities($input: InventorySetQuantitiesInput!) {
inventorySetQuantities(input: $input) {
inventoryAdjustmentGroup { id }
userErrors { field message }
}
}
`;
await adminQuery(SET_INVENTORY, {
input: {
name: "available",
reason: "correction",
quantities: [{
inventoryItemId: "gid://shopify/InventoryItem/67890",
locationId: "gid://shopify/Location/12345",
quantity: 25
}]
}
});
Consultas de pedidos
const ORDERS_QUERY = `
query getOrders($first: Int!, $after: String) {
orders(first: $first, after: $after, sortKey: CREATED_AT, reverse: true) {
pageInfo { hasNextPage endCursor }
edges {
node {
id
name
createdAt
totalPriceSet { shopMoney { amount currencyCode } }
displayFinancialStatus
displayFulfillmentStatus
customer { id email firstName lastName }
lineItems(first: 20) {
edges {
node {
id title quantity
variant { sku price }
}
}
}
}
}
}
}
`;
let allOrders = [];
let cursor = null;
let hasMore = true;
while (hasMore) {
const result = await adminQuery(ORDERS_QUERY, { first: 50, after: cursor });
const { orders } = result.data;
allOrders = allOrders.concat(orders.edges.map(e => e.node));
hasMore = orders.pageInfo.hasNextPage;
cursor = orders.pageInfo.endCursor;
}
Webhooks (reemplaza los hooks de WooCommerce)
WooCommerce usaba hooks de acción (woocommerce_order_status_changed, woocommerce_new_order). Shopify tiene webhooks HTTP:
const CREATE_WEBHOOK = `
mutation webhookSubscriptionCreate($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) {
webhookSubscription { id callbackUrl topic }
userErrors { field message }
}
}
`;
// Suscribirse a nuevos pedidos
await adminQuery(CREATE_WEBHOOK, {
topic: "ORDERS_CREATE",
webhookSubscription: {
callbackUrl: "https://tuapp.com/webhooks/shopify/orders",
format: "JSON"
}
});
// Temas disponibles: PRODUCTS_CREATE, PRODUCTS_UPDATE, ORDERS_PAID,
// ORDERS_FULFILLED, CUSTOMERS_CREATE, INVENTORY_LEVELS_UPDATE, etc.
Límites de velocidad
| API | Tipo de límite | Límite | Tasa de restauración |
|---|---|---|---|
| API de administración GraphQL | Throttling basado en coste | 1000 puntos/cubo | 50 puntos/segundo |
| API de administración REST | Cubo con fugas | 40 solicitudes | 2 solicitudes/segundo |
| Storefront API | Throttling basado en coste | 1000 puntos/cubo | 50 puntos/segundo |
La respuesta de GraphQL incluye información de coste en las extensiones:
// Comprobar el coste en la respuesta
const result = await adminQuery(PRODUCTS_QUERY, { first: 50 });
const { actualQueryCost, throttleStatus } = result.extensions.cost;
// throttleStatus: { maximumAvailable, currentlyAvailable, restoreRate }
// Si currentlyAvailable es bajo, añadir un retardo: await new Promise(r => setTimeout(r, 1000))
Operaciones masivas (migraciones de catálogos grandes)
Para migrar catálogos con más de 1.000 productos, usar las Bulk Operations de Shopify — operaciones asíncronas que devuelven un archivo JSONL:
// 1. Iniciar la operación masiva
const BULK_QUERY = `
mutation {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id title handle
variants(first: 100) { edges { node { id sku price inventoryQuantity } } }
}
}
}
}
"""
) {
bulkOperation { id status }
userErrors { field message }
}
}
`;
// 2. Sondear hasta completar
const POLL_BULK = `
query {
currentBulkOperation {
id status errorCode
createdAt completedAt
objectCount url
}
}
`;
// 3. Cuando status === "COMPLETED", descargar el JSONL desde url
// Cada línea es un nodo de producto como JSON
Lista de verificación para la migración del desarrollador
- Crear el token de acceso a la API de administración con solo los alcances necesarios.
- No exponer nunca el token de la API de administración en el cliente — solo en el servidor (a diferencia del token de Storefront).
- Portar el CRUD de productos de la API REST de WooCommerce a las mutaciones productCreate / productUpdate de Shopify.
- Portar los campos meta_data personalizados a metafieldsSet con el tipo correcto (single_line_text_field, number_integer, boolean, etc.).
- Obtener el ID de ubicación predeterminado para inventorySetQuantities.
- Implementar la gestión de límite de velocidad basado en coste para GraphQL (comprobar throttleStatus en la respuesta).
- Reemplazar los hooks de acción de WooCommerce con suscripciones a webhooks de Shopify (webhookSubscriptionCreate).
- Para catálogos grandes: usar la Bulk Operations API en lugar de consultas paginadas.
- Gestionar userErrors en cada respuesta de mutación — Shopify devuelve 200 incluso para mutaciones fallidas.
- Probar todas las mutaciones en un Shopify Development Store antes de producción.
El error más habitual que cometen los desarrolladores de WooCommerce con la API de administración de Shopify es ignorar userErrors en las respuestas de mutación. A diferencia de la API REST de WooCommerce, que devuelve códigos de estado HTTP 4xx para los fallos de validación, las mutaciones GraphQL de Shopify siempre devuelven HTTP 200 — los errores de validación están en el array userErrors del resultado de la mutación. Un producto que no se pudo crear sigue devolviendo 200 con un producto vacío y userErrors rellenos. Comprobar siempre userErrors en cada mutación o perderás productos silenciosamente durante la migración.
Migra tu tienda con k-sync
Conecta tu tienda WooCommerce, valida tus productos y publícalos en Shopify en minutos. Gratis hasta 50 productos.
Empezar gratisLectura relacionada
Migrating a luggage and travel accessories store from WooCommerce to Shopify (2026)
How to migrate a luggage, travel bags, or travel accessories WooCommerce store to Shopify — luggage specifications, airline compliance, TSA lock, warranty and durability claims, and luggage retail Shopify setup.
Migrating a motorcycle accessories store from WooCommerce to Shopify (2026)
How to migrate a motorcycle accessories, biker gear, or motorbike parts WooCommerce store to Shopify — helmet safety standards, CE-rated protective clothing, type approval for parts, fitment compatibility, and motorcycle retail Shopify setup.