Tony Wang7 min de lecturaCómo hacer scraping de tiendas Shopify en 2026 (API y Python)
Tres formas de hacer scraping de productos y colecciones Shopify en 2026 — DIY Python, no-code o una API estructurada, y las bases legales.
La forma más rápida de hacer scraping de tiendas Shopify en 2026 es llamar a una API estructurada que devuelve JSON normalizado — productos, variantes, precios, colecciones y metadatos de la tienda — en lugar de rastrear cada storefront y parsearlo tú mismo. El DIY en Python es posible porque la mayoría de las tiendas Shopify exponen un feed público de productos, pero el manejo de variantes, la paginación y las particularidades de cada tienda hacen que un endpoint mantenido sea el camino más simple a escala.
Las Admin y Storefront APIs propias de Shopify requieren las credenciales del dueño de la tienda — están pensadas para tiendas que tú controlas. Para investigar storefronts que no son tuyos, recopilas la superficie pública de productos, que es justo lo que una API de scraping estructurada normaliza por ti.
¿Es legal hacer scraping de tiendas Shopify?
Las páginas de producto del storefront son públicas, y recopilar datos públicos suele tratarse de forma distinta al acceso a cuentas privadas — con las condiciones habituales:
- Recopila solo datos públicos del storefront — sin acceso al admin ni al checkout.
- Respeta los términos y las directivas robots de cada tienda, además de tu ley local.
- No reutilices imágenes ni textos de producto más allá de lo que tu caso de uso y la ley permitan.
- Eres responsable del uso lícito y de buena fe de lo que recopiles.
No es asesoría legal — consulta ¿Es legal el web scraping en 2026? para el panorama completo.
Opción 1: DIY en Python (y por qué se rompe)
Muchas tiendas Shopify exponen un feed público products.json, así que el primer intento parece sencillo:
import csv, requests
resp = requests.get(
"https://www.allbirds.com/products.json",
params={"limit": 250, "page": 1}, # walk page=1,2,... until the list is empty
headers={"User-Agent": "Mozilla/5.0"},
)
rows = []
for p in resp.json()["products"]:
for v in p["variants"]: # flatten nested variants into rows
rows.append({"title": p["title"], "handle": p["handle"],
"variant": v["title"], "price": v["price"], "available": v["available"]})
with open("shopify.csv", "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=rows[0].keys()); w.writeheader(); w.writerows(rows)
# ...then handle HTTP 430 rate limits and stores that disable /products.json
Dónde se vuelve costoso:
- Exposición inconsistente — algunas tiendas desactivan el feed público, y Shopify devuelve HTTP
430cuando alcanzas su límite de tasa, así que necesitas backoff, proxies y fallbacks. - Aplanar variantes — cada producto tiene variantes, opciones e imágenes anidadas que hay que normalizar en filas.
- Paginación —
/products.jsontiene un tope de 250 por página; los catálogos grandes abarcan muchas páginas que recorrer y deduplicar, y/collections.jsones un segundo rastreo. - Diferencias entre tiendas — las divisas, la disponibilidad y los metafields varían según el theme.
Opción 2: herramientas no-code y ya hechas
Los exportadores de apuntar y hacer clic pueden volcar una tienda, pero el monitoreo de catálogo y precios implica revisar muchas tiendas con un calendario y guardar el histórico — un pipeline al que una API sirve mejor que una herramienta manual.
Opción 3: una API estructurada de Shopify
La API de Shopify de Crawlora envuelve los endpoints de producto, colección y tienda detrás de una sola API key, devolviendo JSON normalizado. Apúntala a una URL de tienda:
curl -G "https://api.crawlora.net/api/v1/shopify/products" \
-H "x-api-key: $CRAWLORA_API_KEY" \
--data-urlencode "url=https://www.allbirds.com" \
--data-urlencode "limit=50"
import requests
resp = requests.get(
"https://api.crawlora.net/api/v1/shopify/products",
headers={"x-api-key": "YOUR_API_KEY"},
params={"url": "https://www.allbirds.com", "limit": 50},
)
for product in resp.json()["data"]["products"]:
print(product["title"], product["price"], product["handle"])
Una respuesta es JSON normalizado que puedes guardar directamente (los campos son ilustrativos — confirma el esquema en los docs):
{
"code": 200,
"msg": "OK",
"data": {
"products": [
{
"handle": "wool-runner",
"title": "Wool Runner",
"price": 98.0,
"currency": "USD",
"available": true,
"variants": [{ "title": "US 9", "price": 98.0, "available": true }]
}
]
}
}
A partir de ahí, puedes mapear el catálogo con las colecciones, obtener el detalle completo de un producto por handle, o leer los metadatos de la tienda — todo con la misma key (cada endpoint recibe la url de la tienda):
h = {"x-api-key": "YOUR_API_KEY"}
base, store = "https://api.crawlora.net/api/v1/shopify", "https://www.allbirds.com"
collections = requests.get(f"{base}/collections", headers=h, params={"url": store}).json()["data"]
product = requests.get(f"{base}/products/wool-runner", headers=h, params={"url": store}).json()["data"]
meta = requests.get(f"{base}/store", headers=h, params={"url": store}).json()["data"]
Usa /collections/{handle}/products para recorrer una colección, y los endpoints de sitemap para descubrir cada URL de producto y colección del storefront — útil cuando /products.json está desactivado.
Storefronts headless de Shopify y facetas
/products.json cubre la mayoría de las tiendas, pero no todas. Algunos comerciantes usan un storefront headless: un frontend en Next.js o React que renderiza las páginas en el servidor contra un backend de catálogo de Shopify, pero que nunca expone el feed clásico ni un dominio *.myshopify.com localizable. Los mismos endpoints manejan ambos tipos de tienda de forma transparente: pasas la misma url, sin ningún flag extra. Cuando el feed clásico y el fallback por dominio myshopify no están disponibles, la API recurre a analizar el payload de resultados de búsqueda incrustado del propio storefront.
Un ejemplo real: el storefront de Gymshark para EE. UU., www.gymshark.com, es una tienda Shopify clásica; /products.json responde con normalidad. Su storefront internacional, row.gymshark.com, es headless: /products.json y /collections.json fallan, y no hay ningún dominio myshopify en la página. Por debajo sigue siendo un catálogo respaldado por Shopify, y la misma llamada funciona sin cambios:
curl -G "https://api.crawlora.net/api/v1/shopify/collections/leggings/products" \
-H "x-api-key: $CRAWLORA_API_KEY" \
--data-urlencode "url=https://row.gymshark.com" \
--data-urlencode "limit=3"
import requests
resp = requests.get(
"https://api.crawlora.net/api/v1/shopify/collections/leggings/products",
headers={"x-api-key": "YOUR_API_KEY"},
params={"url": "https://row.gymshark.com", "limit": 3},
)
data = resp.json()["data"]
print(data["transport_mode"], data["total_items"], data["total_pages"])
Esa llamada devuelve datos reales de listado y facetas de la colección (186 productos en 4 páginas en el momento de escribir esto), marcados con transport_mode: "ssr_embedded":
{
"code": 200,
"msg": "OK",
"data": {
"store_url": "https://row.gymshark.com",
"source_url": "https://row.gymshark.com",
"collection": "leggings",
"page": 1,
"limit": 3,
"total_items": 186,
"total_pages": 4,
"transport_mode": "ssr_embedded",
"products": [ /* 3 productos, cada uno con la forma del producto de abajo */ ],
"facets": {
"fit": { "regular": 128, "tall": 18, "short": 10 },
"canonicalColour": { "black": 90, "pink": 12 }
},
"facets_stats": {
"price": { "min": 27, "max": 85, "avg": 56.46 }
}
}
}
facets asocia cada campo de filtro con sus buckets de valor a recuento (los mismos datos que alimentan la barra lateral de filtros del propio storefront) y facets_stats da min/max/avg para campos numéricos como el precio. Los nombres de los campos de faceta reflejan los que usa el propio storefront (los de Gymshark van en camelCase, como canonicalColour), así que trátalos como específicos de cada tienda y no como una lista fija; ambos se omiten por completo cuando el payload de un storefront no incluye datos de facetas.
Los productos de un storefront headless también traen campos que el feed clásico nunca devuelve: colour, canonical_colour, discount_percentage, rating, rating_count, collection_tags, labels e inventory_quantity por variante. Aquí hay uno real, extraído de la propia página de producto de row.gymshark.com:
{
"handle": "gymshark-train-t-shirt-ss-tops-black-aw26",
"title": "Train T-Shirt",
"price": 35,
"colour": "black",
"canonical_colour": "black",
"rating": 4.2,
"rating_count": 1549,
"collection_tags": ["all-products", "new-releases"],
"labels": ["new"],
"variants": [
{ "title": "XS", "price": 35, "available": true, "inventory_quantity": 16 }
]
}
No hay ningún transporte aparte que configurar: misma URL base, mismo header x-api-key, mismos parámetros que en la ruta clásica de arriba.
El mismo fallback cubre además tres endpoints más allá de los listados: recomendaciones de producto (intent=related o intent=complementary, con los mismos parámetros que la ruta clásica), páginas estáticas (analizadas desde el CMS del propio storefront en lugar de un feed /pages.json) y la lista de colecciones (enumerada a partir del sitemap del propio storefront). Misma url, sin flags:
curl -G "https://api.crawlora.net/api/v1/shopify/products/gymshark-train-t-shirt-ss-tops-black-aw26/recommendations" \
-H "x-api-key: $CRAWLORA_API_KEY" \
--data-urlencode "url=https://row.gymshark.com" \
--data-urlencode "intent=related"
Una salvedad sobre la lista de colecciones: sin un feed /collections.json del que leer, title es una aproximación derivada del slug de la URL (all-products → "All Products"), no el nombre real que muestra el storefront, y no hay recuento de productos por colección; es menos fiel que la ruta clásica. Úsala para descubrir y confirma el detalle real con /shopify/collections/{handle}/products.
Ordenar y filtrar resultados
En un storefront headless, /shopify/products y /shopify/collections/{handle}/products también aceptan un parámetro sortBy y parámetros de filtro por faceta, que se reenvían al índice de búsqueda del propio storefront exactamente como los enviaría su interfaz de ordenar/filtrar. sortBy es uno de sortLTH (precio de menor a mayor), sortHTL (precio de mayor a menor) o newest; los filtros son un parámetro de consulta por nombre de campo de faceta (los mismos nombres que aparecen en el campo facets de ese listado), separados por comas para varios valores dentro de una faceta y combinados con & entre facetas:
curl -G "https://api.crawlora.net/api/v1/shopify/collections/leggings/products" \
-H "x-api-key: $CRAWLORA_API_KEY" \
--data-urlencode "url=https://row.gymshark.com" \
--data-urlencode "sortBy=sortLTH" \
--data-urlencode "fit=regular" \
--data-urlencode "limit=5"
Cifras reales de la colección de leggings de row.gymshark.com (186 productos sin filtrar): ordenar por sortLTH devuelve los primeros cinco a 21, 21, 27, 33, 33 (no decreciente); sortHTL devuelve 85, 85, 85, 81, 76.5. Filtrar solo por fit=regular reduce total_items de 186 a 128; añadir canonicalColour=black encima lo reduce a 34, un recorte real en el servidor, no un estado de la interfaz del cliente. La respuesta devuelve lo que realmente se aplicó:
{
"total_items": 34,
"sort": "sortLTH",
"filters": { "fit": ["regular"], "canonicalColour": ["black"] }
}
Las tiendas Shopify clásicas rechazan sortBy y los filtros de plano, con un error 400 tipado, en lugar de ignorarlos en silencio. No es una función que nos falte: el propio feed público /products.json de Shopify no tiene ningún ordenamiento ni filtrado en el servidor. Pedir el JSON de la misma tienda clásica con sort_by=price-ascending y con sort_by=price-descending devuelve un orden de productos idéntico byte a byte; el ordenamiento solo ocurre en la página del propio tema (o vía la Storefront API, que necesita las credenciales de la tienda). Así que llamar a www.gymshark.com (una tienda clásica) con sortBy devuelve un error en lugar de una respuesta que parece ordenada pero no lo está:
{ "code": 400, "msg": "sort and filters are only supported for headless storefronts served via the embedded-SSR-JSON fallback transport (transport_mode ssr_embedded); Shopify's classic public catalog JSON has no server-side sort or filter support" }
Si necesitas ordenar o filtrar en una tienda clásica, aplícalo en el cliente después de obtener los datos, que es lo mismo que hace el tema del propio storefront.
Qué puedes recopilar
- Productos: título, handle, precio, disponibilidad, imágenes y variantes
- Colecciones y los productos dentro de una colección
- Metadatos de la tienda y sitemaps del storefront
- Sugerencias de búsqueda y recomendaciones de producto
Limitaciones y desafíos comunes
- No todas las tiendas exponen el feed. Algunas desactivan
/products.jsono lo limitan por tasa (HTTP430); el endpoint de tienda puede recurrir a un dominio público*.myshopify.com, pero la cobertura no está garantizada — usa los endpoints de sitemap como respaldo para el descubrimiento. - Variantes y metafields. Los productos anidan variantes, opciones e imágenes; aplánalas en filas y ten en cuenta que los metafields específicos del theme varían entre tiendas.
- Paginación. Productos y colecciones se paginan con un máximo de 250 por página; recórrelas y deduplica entre páginas.
- Las imágenes y los textos tienen derechos de autor. Los precios y la disponibilidad son datos que puedes recopilar; las fotos y descripciones de producto tienen copyright — no las repúbliques más allá de lo que tu caso de uso y la ley permitan.
Fuentes
Dónde encaja esto
Pruébalo primero, gratis: pasa cualquier URL pública por el Free Web Scraper, o comprueba si un sitio bloquea bots con el Anti-Bot Checker — sin registro.
Los datos de Shopify impulsan el monitoreo de catálogo, el seguimiento de precios de la competencia y la investigación de surtido. Combínalos con cómo hacer scraping de Shop.app para la vista del marketplace de consumo — la superficie entre tiendas donde estos mismos comercios se descubren — y la API de scraping de Amazon para precios entre canales, todo bajo el workflow de inteligencia de producto e-commerce. Para la misma estrategia en otros marketplaces, consulta cómo hacer scraping de datos de producto de Amazon y cómo hacer scraping de eBay, o cómo elegir una API de web scraping.
Empieza probando el endpoint en el Playground, leyendo el esquema de request y response en los docs de la API, y revisando los costos en créditos en la página de precios.
Preguntas frecuentes
¿Puedo hacer scraping de tiendas Shopify sin que me bloqueen?
Crawlora se encarga del enrutamiento de proxies, el ritmo, los reintentos y los fallbacks detrás de la API, y devuelve JSON normalizado — incluso para tiendas que limitan por tasa (HTTP 430) o desactivan el feed público de productos.
¿Todas las tiendas Shopify tienen un feed products.json?
La mayoría sí — /products.json es un endpoint sin credenciales que devuelve hasta 250 productos por página —, pero algunas tiendas lo desactivan o lo limitan por tasa (HTTP 430). Si el dominio propio lo bloquea, Crawlora puede recurrir a un dominio público *.myshopify.com, y los endpoints de sitemap descubren las URLs de producto y colección como respaldo.
¿No tiene Shopify ya una API?
Las Admin y Storefront APIs de Shopify requieren las credenciales del dueño de la tienda y están pensadas para tiendas que tú controlas. Para investigar storefronts que no son tuyos, Crawlora recopila la superficie pública de productos como JSON normalizado.
¿Qué datos puedo recopilar?
Productos con variantes, precios, disponibilidad e imágenes; colecciones y los productos dentro de ellas; metadatos de la tienda; páginas estáticas; sitemaps del storefront; sugerencias de búsqueda; y recomendaciones de producto. Los precios y la disponibilidad son datos; las imágenes y textos de producto tienen derechos de autor.
¿Cómo apunto a una tienda específica?
Pasa la URL de la tienda al endpoint de productos. Usa el handle del producto (junto con la url de la tienda) para el detalle completo, el endpoint de colecciones para mapear la estructura del catálogo, y los endpoints de sitemap para enumerar URLs.
¿Puedo monitorear precios y cambios de catálogo?
Sí. Vuelve a ejecutar las llamadas de producto y colección con un calendario y guarda el histórico para seguir los movimientos de precio y los cambios de surtido.