21 May 2026
Shopify API Integration Documentation: A Practical Guide
A production-grade guide to Shopify API integration documentation: what matters, what to ignore, and how to ship stable integrations with REST/GraphQL, webhooks and proper rate limiting.
Shopify API Integration Documentation: A Practical Guide
If you're looking for Shopify API integration documentation, here’s the short version: decide whether you need Admin or Storefront, prefer GraphQL over REST for anything non-trivial, use OAuth with the minimum scopes, and design for rate limits and retries from day one. Webhooks drive near‑real‑time sync; bulk operations cover the big backfills. Version your calls and test against a dev store before going anywhere near production.
At Streamline Digital in Bournemouth we build integrations with the Shopify Admin API, Storefront API, GraphQL bulk operations, n8n, Postgres and Redis. Below is the practical, production‑grade documentation we wish more teams shipped with their apps.
What the Shopify APIs actually cover
Shopify’s API surface is bigger than it looks at first glance. The main pieces you’ll use in integrations:
- Admin API (REST and GraphQL): manage products, variants, inventory, orders, fulfilment orders, customers, metafields, discounts and more. This is where most back‑office automation lives.
- Storefront API (GraphQL): public, buyer‑facing queries and mutations for headless storefronts. You won’t manage admin data here; you’ll render catalogues, carts, and checkouts for shoppers.
- Webhooks: event notifications for changes (orders/create, products/update, inventory_levels/update, etc.). These keep your systems in sync without polling.
- Bulk operations (GraphQL Admin): async exports/imports of large datasets (orders, products, etc.) when you need to backfill millions of records without hitting rate limits.
- Partners/Access/Multipass/Functions: niche but useful. Partners APIs for app ops, Multipass for SSO to a storefront, Functions for on‑platform logic (discounts, shipping). Most third‑party integrations won’t need these initially.
A typical integration stack in our client work:
- App backend (Node with @shopify/shopify‑api, or Python with shopifyapi) running on Fly.io, Render or Kubernetes.
- Orchestration in n8n for straightforward workflows (webhook in → transform → Admin GraphQL mutation).
- Postgres for durable sync state, Redis for queues and locks, S3/Backblaze for bulk exports.
- Monitoring with Sentry and Datadog. Structured logs with request IDs and Shopify call cost.
Admin vs Storefront, REST vs GraphQL
Pick the API surface deliberately:
- Use Admin API for any back‑office data: inventory sync, ERP/3PL integrations, order ingest, PIM updates, metafields.
- Use Storefront API only for public, buyer‑side experiences (headless front ends). Don’t try to update admin data via Storefront.
REST vs GraphQL:
- Use GraphQL for most non‑trivial integrations. It’s faster per unit of data, gives predictable costs, supports bulk operations, and reduces round trips. For example, fetching 10k orders with nested line items and metafields via REST often takes 20–30k requests; the same via a GraphQL bulk export is one job plus a single JSONL download.
- Keep REST for endpoints not well supported in GraphQL yet, or for simple one‑off updates where the REST semantics are clear (e.g., a specific fulfilment call in the Fulfilment Orders API).
Real‑world example: a client pushing catalogue updates from a PIM. We moved from REST (about 25 calls per product across images, variants and metafields) to a single GraphQL mutation per product (productsUpdate with nested media and metafields). API calls dropped ~90%, sync time fell from ~50 minutes to ~6 minutes for 5k SKUs, and we stopped tripping rate limits during sales peaks.
If you must stay on REST, at least batch and use page_info pagination. Don’t ever use the old page/limit scheme; it’s deprecated and slow.
Authentication, scopes and app types
Shopify supports public apps (for the App Store) and custom apps (per‑store installs). For most integrations, use a custom app with OAuth 2.0 to obtain a long‑lived offline token for background jobs, and an online token only when you need a per‑user session for an embedded UI.
Key points we see missed:
- Request the minimum scopes you genuinely need. For a basic inventory/price sync: read_products, write_products, read_inventory, write_inventory, read_locations. For order processing: read_orders, write_orders, plus the relevant fulfilment scopes (read_fulfillments/write_fulfillments or Fulfilment Orders scopes).
- Store tokens per shop securely (encrypted at rest). Rotate on uninstall/reinstall. Never log tokens.
- For embedded apps, validate Shopify session tokens (JWT) on every request. Don’t trust cookies alone.
- Use HMAC validation for the install callback and for every webhook. Compute HMAC with your app secret and compare in constant time with the X‑Shopify‑Hmac‑Sha256 header.
We keep an install checklist in the repo README: scopes, redirect URLs, test plan, and a one‑liner curl to verify HMAC calculation. New devs get productive in under an hour.
Versioning, pagination and rate limits
Shopify versions its Admin and Storefront APIs quarterly (e.g., 2024‑10). You call a fixed versioned path (/admin/api/2024‑10/ for REST) or endpoint for GraphQL. Budget time each quarter to run deprecation checks and bump versions — it’s usually a few field renames and removed enums.
Pagination:
- REST: always use page_info with limit. Persist the last cursor so you can resume after failures.
- GraphQL: use first/after with edges/cursors. Never assume stable ordering without an explicit sort.
Rate limits (rough guides; Shopify can change these):
- REST Admin uses a leaky bucket (roughly 40 request burst, refilling ~2 req/s per shop). Respect the X‑Shopify‑Shop‑Api‑Call‑Limit header. Back off on 429 with jitter.
- GraphQL Admin uses a cost model (bucket ~1000 points, refilling ~50 points/s). Typical queries cost 10–50 points; bulk operations are separate and don’t consume the normal bucket during export processing.
Practical patterns that work:
- Central throttle: one in‑process semaphore per shop for REST, and a GraphQL cost‑aware scheduler. We store budget in Redis; workers block until budget is available. This prevents thundering herds at sale time.
- Idempotency by design: write operations tagged with a stable key on your side (e.g., external_id), and dedupe retries. Shopify’s headers include an API request ID — log it so you can raise a support ticket with evidence when needed.
- Bulk first for large reads: to export 2m orders we create a bulkOperationRunQuery, poll status every 2–5 seconds, then stream the resulting JSONL from the signed URL into S3 and parse line‑by‑line.
We’ve run nightly backfills of ~30m rows (products + variants + metafields) at ~20–40 MB/s network throughput using bulk exports and a simple Go/Node consumer. No rate‑limit alarms, no manual babysitting.
Webhooks that don’t fall over
Webhooks are the heartbeat of a good integration. Design them to be boring:
- Acknowledge fast: return 200 within a couple of seconds. Do the heavy work asynchronously via a queue (e.g., Redis lists, SQS, or n8n workflow runs). If you need to do validation, do it but keep it cheap.
- Verify authenticity: compute HMAC with your app secret and compare with X‑Shopify‑Hmac‑Sha256. Reject if missing or mismatched.
- De‑duplicate: Shopify includes an identifier (e.g., X‑Shopify‑Webhook‑Id). Store processed IDs for 24–48 hours in Redis and drop duplicates.
- Be idempotent: updates may arrive out of order. Always upsert by the primary key (product_id, order_id, inventory_item_id) and reconcile using updated_at.
- Expect retries: Shopify retries on non‑200 statuses with backoff. Don’t panic if you see spikes. We design for 10–50 webhook deliveries per minute during launches and Black Friday/Cyber Monday.
For orchestration, n8n works well: HTTP Webhook trigger → HMAC check function → transform → GraphQL mutation → success/fail branch → notify Slack on repeated failures. The whole thing is auditable and takes minutes to follow when production is noisy.
Data sync patterns that scale
The difference between a flaky integration and a solid one is usually data model discipline and state tracking. What we standardise on:
- Source of truth per entity: decide whether Shopify or the external system “owns” a field. For example, price may be owned by the ERP; title and description by PIM; inventory by WMS. Metafields are your friend for housing external IDs and state flags.
- Durable mapping tables: shop_id + external_id ↔ shopify_id for products, variants, customers, locations. Store in Postgres with unique constraints. Populate on first sync and use forever.
- Upserts everywhere: for products/variants, run productUpdates via GraphQL with external identifiers in metafields so you can re‑play safely. For inventory, prefer bulk adjust mutations so you set the absolute state, not deltas, reducing drift.
- Delta + periodic full recon: consume webhooks for near real‑time deltas, then run a nightly or weekly bulk export to confirm nothing drifted. Log discrepancies and repair automatically.
- Back‑pressure and queuing: one queue per shop, max in‑flight size tuned to rate limits (e.g., 10–20 messages). Use Redis locks per product_id to serialise conflicting writes.
Concrete recipes that work repeatedly:
- Inventory sync: webhook on inventory_levels/update for deltas; periodic bulk export of InventoryItem + InventoryLevel via GraphQL for full recon; adjust using the Inventory bulk adjust mutation; keep a per‑location ledger.
- Order ingest to ERP/3PL: orders/create and fulfilments/create webhooks; enrich with customer + metafields via a single GraphQL query; post to ERP; write back external IDs to order metafields; fulfilment updates via the Fulfilment Orders API.
- Catalogue updates from PIM: nightly bulk export of the PIM; diff against Postgres; batch productsUpdate mutations with media and metafields; throttle by GraphQL cost; notify on anomalies (missing images, option mismatch).
On a 5k SKU store with two locations, we see stable end‑to‑end inventory latency under 20 seconds via webhooks, and nightly full recon (~1.2m rows) completes in ~8–12 minutes using bulk exports and streaming parsers.
If you need hands‑on help, our team handles this daily — see our Shopify Development & Integrations service, or book a free discovery call and we’ll map out a pragmatic plan.
Testing, environments and deployment
Good integrations are tested against reality, not assumptions:
- Use a development store per project. Seed with products, variants, collections, metafields and at least two locations. Enable the Bogus Gateway to create test orders and refunds.
- Spin up a public HTTPS endpoint for local work with Cloudflare Tunnels or ngrok. Register webhook URLs against your dev store, not production.
- Contract tests: snapshot a few representative GraphQL queries/mutations and assert schemas. If Shopify deprecates a field, your CI should tell you before you deploy.
- Replayable fixtures: store webhook payloads as JSON in your test suite. Reproduce bugs by replaying the exact payload through your local pipeline.
- Observability: log every outbound call with method, path, response code, call cost, and request_id. Ship metrics for 429s and average webhook processing time. Alert when queues back up.
- Releases: small, frequent deploys. We tag each integration with a semantic version and a README changelog so client tech teams know what changed and when.
We also maintain living docs in the repo: supported flows, scopes required, data mappings, retry policy, and dashboards to watch. When the integration grows, this saves hours of guesswork.
FAQ
Do I use REST or GraphQL for a new Shopify integration?
Use GraphQL unless you have a strong reason not to. It reduces API calls, gives predictable costs, and unlocks bulk operations for big data moves. Keep REST for gaps or very simple updates.
How do I handle Shopify API rate limits safely?
Implement a central throttle per shop, respect the REST call‑limit headers, and schedule GraphQL by cost. Back off with jitter on 429s. For large reads, prefer GraphQL bulk exports to avoid consuming the normal rate bucket.
What scopes do I need for inventory and orders?
Inventory: read_inventory, write_inventory, read_products (variants), read_locations. Orders: read_orders, write_orders, plus the Fulfilment Orders scopes if you create or update fulfilments. Request the minimum you need.
What’s the fastest way to backfill millions of records?
GraphQL bulk operations. Kick off a bulkOperationRunQuery for the entity, poll until complete, then stream the JSONL from the signed URL into storage and process line‑by‑line. We regularly move tens of millions of rows this way without rate‑limit noise.
Related guides & services
Hand-picked next steps from across our guides and services.
- Guide
Shopify API Integration Guide
This cluster guide specifically focuses on Shopify API integration, detailing REST vs GraphQL and authentication, directly expanding on the source's topic.
- Service
Shopify API Integration
This service page offers professional Shopify API integration, aligning with the practical nature of the source guide and providing a solution for readers.
- Guide
Shopify Integration & API Architecture
As a pillar guide for Shopify integration, this page provides a broader context for the API integration concepts discussed in the source.
- Guide
Custom Shopify App Development
This cluster guide on custom Shopify app development is highly relevant as API integration is a core component of building custom apps.
- Article
Shopify Headless vs Standard: Which API Strategy Wins in 2026?
This blog post discusses Shopify headless vs standard, which involves API strategies, and provides further context to the technical aspects of Shopify integration.