
Si quiero conectar Shopify con mi sistema sin depender de CSV, GraphQL Admin API es el camino más directo.
Yo lo resumiría así: para leer productos, pedidos, clientes e inventario en Shopify, primero creo una custom app, doy solo los scopes justos, guardo el token en el backend y uso un endpoint fijo como 2026-01. Después hago POST con query y variables, controlo errors y userErrors, y escalo con cursores, Bulk Operations y webhooks.
En pocas líneas, esto es lo que importa:
En otras palabras: si yo dejo bien armado acceso, permisos, consultas simples y control de volumen desde el arranque, la integración queda lista para mover precios, stock y órdenes con menos fallas y menos trabajo manual.
A partir de ahí, el resto pasa por una idea simple: pedir solo lo que uso, guardar estado entre corridas y separar tareas chicas de procesos masivos.
Cómo Integrar GraphQL API con Shopify: Flujo Completo
Antes de usar la GraphQL Admin API, tenés que crear una custom app, darle solo los scopes que hagan falta y guardar el token en el backend. Si querés sincronizar productos, pedidos e inventario sin meter mano todo el tiempo, este paso va primero.
Andá a Configuración > Apps y canales de venta > Desarrollar apps, creá la app y activá el acceso a la Admin API. Después, pedí solo los scopes que tu integración use de verdad.
| Recurso | Scopes recomendados |
|---|---|
| Productos | read_products, write_products |
| Pedidos | read_orders, write_orders |
| Clientes | read_customers, write_customers |
| Inventario | read_inventory, write_inventory |
Si tu integración solo lee productos y pedidos, no hace falta pedir write_customers ni write_orders. Menos scopes significa menos riesgo si algo se complica. Es la lógica de “solo lo justo y necesario”.
Después de instalar la app, Shopify muestra el token una sola vez. Copialo en el momento y guardalo en SHOPIFY_ADMIN_TOKEN, ya sea en una variable de entorno o en un gestor de secretos.
El endpoint de la Admin GraphQL API sigue este patrón:
https://{tu-tienda}.myshopify.com/admin/api/2026-01/graphql.json
Usá una versión fija de API, como 2026-01, para evitar sorpresas cuando Shopify haga cambios en la plataforma. También conviene guardar el dominio de la tienda en una variable de entorno, por ejemplo SHOPIFY_STORE_DOMAIN, así podés cambiar de entorno sin tocar el código.
Usá X-Shopify-Access-Token solo desde el backend. No lo expongas en el frontend, en repos públicos ni en logs. Ese token es la llave de entrada a la tienda, así que tratarlo con cuidado no es opcional.
Si sospechás que el token se expuso, revocalo de inmediato desde Configuración > Apps y canales de venta > Desarrollar apps y generá uno nuevo. No hace falta esperar una confirmación. Rotarlo cuesta poco; una filtración de datos puede salir mucho más cara.
Con esto listo, ya podés mandar tu primera solicitud GraphQL.
Con el token guardado y el endpoint definido, ahora sí toca hacer la primera llamada de verdad. La mecánica no cambia: enviás un POST, sumás dos headers obligatorios y mandás un body JSON con query y variables.
La primera llamada usa POST, Content-Type: application/json, X-Shopify-Access-Token y un body con query y, si hace falta, variables.
| Elemento | Valor |
|---|---|
| Método HTTP | POST |
| URL | https://{tu-tienda}.myshopify.com/admin/api/2026-01/graphql.json |
| Header de autenticación | X-Shopify-Access-Token: {tu_token} |
| Header de contenido | Content-Type: application/json |
Body: query | String con la operación GraphQL |
Body: variables | Objeto JSON con los parámetros (opcional) |
Para chequear que la conexión responde, la query más simple es { shop { name } }. En curl, se ve así:
curl -X POST \
https://mi-tienda.myshopify.com/admin/api/2026-01/graphql.json \
-H "Content-Type: application/json" \
-H "X-Shopify-Access-Token: $SHOPIFY_ADMIN_TOKEN" \
-d '{"query": "query { shop { name } }"}'
Si el token es válido, Shopify devuelve un JSON con el nombre de la tienda. Si en la respuesta aparece errors, lo primero para mirar es el header de autenticación: tiene que ser exactamente X-Shopify-Access-Token, no Authorization: Bearer.
Una vez que eso funciona, ya podés empezar a leer datos de la tienda sin vueltas. Para escalar estas operaciones, podés usar un Commerce Brain con IA que gestione la lógica de negocio automáticamente.
Para arrancar, usá pedidos. Productos y clientes siguen la misma lógica.
query GetOrders($first: Int!) {
orders(first: $first) {
edges {
node {
id
name
createdAt
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
customer {
id
displayName
email
}
}
}
}
}
Hay dos detalles que conviene tener presentes:
Cuando ya no alcanza con leer y necesitás cambiar datos, pasás a las mutations.
Las mutations usan el mismo esquema, pero con mutation.
mutation CreateProductWithOptions($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
{
"input": {
"title": "Zapatillas deportivas",
"productOptions": [
{ "name": "Color", "values": [{ "name": "Rojo" }, { "name": "Verde" }] },
{ "name": "Talle", "values": [{ "name": "38" }, { "name": "40" }] }
]
}
}
Acá hay una trampa bastante común: no alcanza con mirar el código HTTP. Shopify puede responder con HTTP 200 y, aun así, incluir fallos de validación en userErrors. Puede pasar, por ejemplo, si el precio tiene que ser un número positivo o si falta un campo requerido.
Por eso, revisá siempre tanto errors como userErrors.
Si el volumen de datos empieza a crecer, el paso que sigue es paginar o usar bulk operations.
Cuando ya podés leer y mutar datos, el paso que sigue es operar a escala sin perder estado ni velocidad. En Shopify, hay tres piezas que cubren casi todo: cursores, Bulk Operations y webhooks.
Shopify usa conexiones Relay. Eso quiere decir que cada lista devuelve nodes y pageInfo; dentro de pageInfo vienen hasNextPage y endCursor. Después usás endCursor como after en la consulta siguiente.
{
orders(first: 100, after: "cursorDeLaPaginaAnterior") {
nodes {
id
name
createdAt
totalPriceSet {
shopMoney { amount currencyCode }
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
El ciclo es simple: consultás una página, guardás el cursor y seguís hasta que hasNextPage dé false. La mayoría de los recursos acepta hasta 250 nodos por solicitud.
En producción, conviene guardar el endCursor de cada lote en tu base de datos junto con el timestamp del sync. Si una tarea se corta a mitad de camino, podés retomarla desde ese punto en vez de volver a leer todo desde cero. Eso, en la práctica, te ahorra tiempo y llamadas.
Usá nodes salvo que necesites el cursor por ítem.
Si el volumen ya queda grande para paginación común, pasá a Bulk Operations.
Cuando necesitás exportar todo el catálogo, traer el historial completo de órdenes o sincronizar mucho volumen con un ERP, la paginación sincrónica empieza a quedarse corta. Ahí entran las Bulk Operations.
El flujo es bastante directo:
bulkOperationRunQuerycurrentBulkOperation hasta que el estado sea COMPLETEDEl dato más importante es este: las bulk operations no consumen puntos del rate limit estándar. Por eso son la opción correcta para tareas nocturnas, backfills históricos o sincronizaciones masivas de precios.
Hay un límite a tener en cuenta: solo puede correr una bulk por tipo y por tienda al mismo tiempo.
Cuando la tienda ya se mueve por API, los webhooks te evitan hacer polling al pedo. El webhook te avisa que pasó algo; GraphQL te deja consultar o actualizar el recurso que disparó ese evento. Juntas, estas dos cosas forman la base de una integración basada en eventos que funcione bien en producción.
El patrón suele ser siempre el mismo: recibís el webhook, validás la firma, encolás el trabajo y después llamás a GraphQL para traer el estado actualizado del recurso o para aplicar cambios. Por ejemplo, si entra un orders/create, el payload del webhook trae el ID de la orden, pero no todos los datos. Con ese ID hacés una query a Admin GraphQL para traer ítems, totales y datos del cliente, y recién ahí corrés tu lógica de negocio.
| Evento webhook | Acción GraphQL típica | Para qué sirve |
|---|---|---|
orders/create | Query order con ítems y cliente | Alimentar analítica, disparar notificaciones post-compra (como automatizar mensajes por WhatsApp) |
products/update | Query del producto actualizado con variantes | Sincronizar precios y stock |
customers/create | Query del cliente recién creado o mutation para etiquetarlo | Etiquetar clientes para segmentación o flujos de bienvenida |
orders/updated | Query fulfillmentOrder | Actualizar sistemas de seguimiento o logística |
Hay un punto que no conviene pasar por alto: desacoplar la recepción del webhook del procesamiento pesado. Respondé rápido y mandá el trabajo más caro a un worker en segundo plano.
Con paginación, bulk y webhooks ya cubiertos, falta cerrar la integración con tres temas que pegan directo en el día a día: costo, versionado y despliegue.
Shopify no limita por cantidad de llamadas, sino por costo calculado. O sea: no alcanza con “llamar poco”. También hay que pedir solo lo que hace falta.
Por eso conviene:
Dicho mal y pronto, si metés todo en una sola query “porque es más cómodo”, después el costo te pasa factura.
Con el costo bajo control, el paso siguiente es sostener la integración sin sobresaltos con el correr del tiempo.
Shopify publica una nueva versión de API cada tres meses, y cada versión estable se mantiene por al menos 12 meses. Eso obliga a seguir de cerca los cambios, no solo a mirar si “todavía funciona”.
Un punto clave: monitoreá deprecaciones de forma activa con el header X-Shopify-API-Deprecated-Reason y registrá ese valor en logs y alertas.
Para el flujo de trabajo, conviene ir por un camino simple y prolijo:
Con eso, el paso a producción deja de ser un salto de fe y pasa a ser algo mucho más controlado.
Las decisiones que más mueven la aguja son pocas y bastante concretas: elegir la API correcta para cada caso, dejar solo los scopes necesarios, arrancar con queries simples y bien testeadas antes de sumar complejidad, y escalar con paginación por cursor, Bulk Operations y webhooks cuando el volumen lo pida.
Si hacés bien esa base - API correcta, scopes mínimos y despliegue gradual - la integración queda lista para operar con menos errores y menor costo.
Elegí solo los scopes que tu app necesita de verdad. Cuantos menos permisos des, mejor cuidás los datos de la tienda.
Estos permisos se configuran cuando creás la app personalizada en Shopify. Entre los más usados están los de productos (read_products, write_products), pedidos (read_orders, write_orders y administrar información de pedidos), clientes (read_customers, write_customers), existencias (read_inventory, write_inventory) y metaobjects (read_metaobjects, write_metaobjects).
Conviene usar la Bulk Operations API cuando necesitás mover o leer muchos datos de tu tienda Shopify de una sola vez. Por ejemplo: cargas históricas, migraciones o reconciliaciones nocturnas.
Funciona muy bien si tu prioridad es el rendimiento y la eficiencia, no la inmediatez. Pensalo así: si necesitás hacer un backfill o bajar una base completa de productos o pedidos, esta API te deja hacerlo sin empujar de más los límites de tasa.
Ahora bien, hay una contra clara: tiene más latencia. O sea, no está pensada para cambios al instante ni para actualizaciones en tiempo real.
Si la sincronización entre Shopify y tu sistema se corta, retomala con un enfoque resistente para evitar pérdidas o duplicados.
Dicho simple: si algo se cae en el medio, no conviene “seguir como si nada”. Primero hay que comparar datos, volver a procesar lo que quedó pendiente y asegurarse de que un mismo evento no rompa todo si entra dos veces.
Es el tipo de detalle que parece menor al principio, pero después te ahorra dolores de cabeza cuando hay cortes, demoras o picos de tráfico.