Shopify Bulk Operations API: A Practical Guide for Catalog Updates
What the Shopify Bulk Operations API is, how bulk queries and mutations work, its limits and gotchas, and how to get the same bulk updates across stores without writing code.
Key Takeaways
- The Bulk Operations API runs large GraphQL queries/mutations asynchronously, returning JSONL.
- It avoids rate limits but is developer-heavy: staged uploads, polling, JSONL parsing, partial-failure handling.
- It has no native scheduling or rollback, and one operation of each type runs at a time.
- Apimio uses the same bulk infrastructure to give catalog teams no-code bulk updates across stores.
Table of Contents▼
- TL;DR
- What is the Shopify Bulk Operations API?
- How a bulk operation works, step by step
- Bulk query vs bulk mutation
- A practical example
- When to use the Bulk Operations API
- Limits and gotchas
- The catch: it’s a developer tool, not a catalog tool
- Bulk updates without code: how Apimio uses the Bulk API
- Who should use the API vs a no-code tool
- Bulk API vs REST vs regular GraphQL
- Why rate limits make the Bulk API necessary
- Handling errors and partial failures
- Versioning and maintenance — the long tail
- Best practices for bulk updates on Shopify
- Frequently asked questions
- What is the Shopify Bulk Operations API and when should I use it?
- How do I bulk update products in Shopify?
- What are the Shopify Bulk Operations API limits?
- How do I update thousands of Shopify products at once?
- Can I bulk update Shopify products without code?
- Does the Bulk API support scheduling and rollback?
TL;DR
The Shopify Bulk Operations API runs large GraphQL queries and mutations asynchronously, returning results as a JSONL file — the right tool for reading or updating tens of thousands of products without hitting standard rate limits. It’s powerful but developer-heavy: staged uploads, polling, and JSONL parsing. Apimio uses the same bulk infrastructure under the hood to give catalog teams no-code bulk updates across every store.
What is the Shopify Bulk Operations API?
The Shopify Bulk Operations API is a GraphQL feature for working with large volumes of data asynchronously. Instead of paginating through thousands of products with rapid-fire API calls — and hitting rate limits almost immediately — you submit one bulk operation, Shopify runs it in the background, and you collect the results as a single JSONL file when it finishes. It’s the supported way to read or update tens of thousands of records without fighting throttling, and it comes in two flavours: bulk queries (read) and bulk mutations (write).
If your job is “export every product and variant” or “update the price and tags on 40,000 SKUs,” this is the API built for it. The trade-off is that it’s asynchronous and file-based, which makes it powerful at scale but heavier to work with than a simple synchronous call — and squarely a developer tool, not something a merchandiser can run from an admin screen.
It’s worth understanding why Shopify designed it this way, because the design explains both its power and its friction. A synchronous API has to answer immediately, which caps how much work a single call can do. By making bulk operations asynchronous, Shopify can let one request do an enormous amount of work without holding a connection open or blowing through a rate budget — it queues the job, runs it server-side, and hands back a file. That’s genuinely the best architecture for the problem. The friction is simply that your code now has to deal with a job that finishes later and returns a file rather than a response — submit, wait, fetch, parse — which is a different and more involved programming model than “call endpoint, read JSON.” Everything that makes the Bulk API scale is also what makes it harder to wire into a simple workflow, and that tension is the theme of this whole guide.
How a bulk operation works, step by step
A bulk query follows a clear lifecycle:
- Submit the operation with the bulkOperationRunQuery mutation, passing the GraphQL query you want to run across your whole catalog.
- Shopify returns a bulk operation ID and starts running it in the background.
- Poll the currentBulkOperation field (or subscribe to the BULK_OPERATIONS_FINISH webhook) until the status is COMPLETED.
- Download the resulting JSONL file from the returned URL — one JSON object per line, one line per record.
- Parse the JSONL and do whatever you needed with the data.
A bulk mutation (writing data) adds an upload step at the front: you stage a JSONL file of the changes you want to make, then run bulkOperationRunMutation pointing at that file, and poll the same way. So a bulk write is really: build a JSONL of changes → stage-upload it → run the mutation → poll → check the results file for per-row errors.
Bulk query vs bulk mutation
The distinction matters because they have different shapes and different failure modes:
- Bulk query (read): one GraphQL query, run across all matching records, returned as JSONL. Great for exports, audits, and reconciliation.
- Bulk mutation (write): a staged JSONL of inputs, applied row by row. Great for mass updates — prices, tags, metafields, inventory — but you must handle partial failures, because some rows can succeed while others fail.
Bulk mutations are where most of the engineering effort goes, because writing at scale means validating inputs, batching, retrying failed rows, and reconciling what actually changed against what you intended.
A practical example
Here’s the shape of a bulk query that reads every product with its variants — the kind of operation you’d run before a mass update:
mutation {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
variants {
edges { node { id price sku } }
}
}
}
}
}
"""
) {
bulkOperation { id status }
userErrors { field message }
}
}You’d then poll currentBulkOperation until it’s COMPLETED, download the JSONL, and — for a write — build a JSONL of variant price updates, stage-upload it, and run bulkOperationRunMutation against the productVariantsBulkUpdate mutation. None of this is exotic for a developer, but it’s real code, real error handling, and real maintenance.
When to use the Bulk Operations API
Reach for the Bulk API when:
- You’re moving tens of thousands of records and the regular API would throttle.
- You need a complete, consistent export for an audit, migration, or reconciliation.
- You’re building an integration or internal tool that runs scheduled mass updates.
- You have developer time and the volume justifies the engineering.
Don’t reach for it when the job is occasional, merchandiser-driven, or could be done by a catalog tool — building and maintaining bulk-mutation pipelines for a once-a-month price change is effort spent in the wrong place.
Limits and gotchas
The Bulk API removes rate-limit pain but introduces its own constraints:
- One bulk operation of each type (query/mutation) can run at a time per shop — you can’t fan out ten concurrent bulk jobs.
- It’s asynchronous — there’s no instant response; you poll or wait on a webhook, which complicates user-facing workflows.
- Results are JSONL, not a tidy API response ��� you have to stream and parse potentially huge files.
- Mutations can partially fail — some rows succeed, others error, and you must read the results file to know which.
- Staged uploads add steps — writes require uploading a JSONL before the mutation runs.
- No native scheduling or rollback — the API runs when you call it; “apply Friday, revert Monday” is something you have to build.
These aren’t flaws — they’re the cost of operating at scale. But they’re exactly why a merchandiser can’t use this directly, and why teams often wrap it in a tool.
Bulk-update your Shopify catalog without writing code
Apimio runs on the same bulk infrastructure under the hood, so catalog teams get mass updates across stores with no GraphQL, no JSONL, no polling. Free to install from the Shopify App Store.
The catch: it’s a developer tool, not a catalog tool
Everything above assumes someone who writes code, handles authentication and API versions, parses JSONL, manages partial failures, and maintains the pipeline as Shopify’s API evolves. That’s fine if you have engineering capacity and a high enough volume to justify it. But most catalog work is done by merchandisers and ecommerce ops, not developers — and the jobs they need (bulk-edit prices, tags, descriptions, metafields; do it across multiple stores; schedule a sale and revert it) shouldn’t require a GraphQL project. The Bulk API is the right engine; it’s the wrong cockpit for a non-developer.
Bulk updates without code: how Apimio uses the Bulk API
Apimio sits on top of the same bulk infrastructure and exposes it as a no-code workflow. You filter the products you want — by collection, vendor, tag, or type — set the change, and Apimio handles the staged uploads, the mutations, the polling, the partial-failure handling, and the retries. A furniture brand re-tags 8,000 SKUs for a new collection structure; a fashion brand bulk-updates fabric and care metafields across a season’s range; a beauty brand corrects ingredient data across hundreds of products — all without a line of GraphQL. And because Apimio publishes from one source of truth, the same bulk update applies across every connected store, which the raw API would make you orchestrate per shop.
What you can change in bulk covers the jobs catalog teams actually run into:
- Prices and compare-at prices — including scheduled sales with auto-revert via Sale Scheduler.
- Tags and collection membership — restructure how the catalog is organised in one pass.
- Titles, descriptions, and SEO fields — bulk-edit or AI-generate across thousands of products.
- Metafields at product, variant, and category level — the data the native admin can’t bulk-edit.
- Images and alt text — AI-generated, spec-grounded alt text across the whole catalog.
Crucially, each of these is a normal task in Apimio, not a project. The merchandiser who needs to drop prices on a collection, fix a season of metafields, or re-tag a range does it the same afternoon they decide to — instead of writing a ticket and waiting for an engineer to build, test, and babysit a bulk-mutation script. That shift — from “file a request” to “do it yourself” — is the real difference between owning the Bulk API and owning a tool built on it.
Who should use the API vs a no-code tool
A simple way to decide:
| Your situation | Best fit |
|---|---|
| Building a custom integration at high volume | Bulk Operations API (direct) |
| One-off engineering-led migration or audit | Bulk Operations API (direct) |
| Merchandiser-driven catalog edits | No-code tool (Apimio) |
| Bulk edits across multiple stores | No-code tool (Apimio) |
| Scheduled sales with auto-revert | No-code tool (Apimio) |
Many teams use both: developers use the API for deep integrations, while the catalog team uses Apimio for the day-to-day bulk work that would otherwise sit in an engineering backlog.
Give your catalog team the Bulk API — without the code
Apimio turns Shopify’s bulk infrastructure into point-and-click mass updates across every store. Free to install.
Bulk API vs REST vs regular GraphQL
It helps to place the Bulk API against the other ways to talk to Shopify, because choosing wrong is a common cause of throttling and slow integrations:
- REST Admin API: simple and synchronous, but every product and variant is a separate call, so large jobs hit the leaky-bucket rate limit fast and crawl. Fine for small, targeted updates; painful at catalog scale.
- Regular GraphQL Admin API: more efficient per call and cost-based rather than per-request, but you still paginate and still hit query-cost limits on very large reads. Good for moderate volumes and precise queries.
- Bulk Operations API: asynchronous and file-based, designed specifically so a single operation can traverse the entire catalog without you managing pagination or rate limits. The right tool once volume is high.
The rule of thumb: a handful of records, use REST or GraphQL directly; a moderate query, GraphQL with pagination; tens of thousands of records, the Bulk API. The mistake is using REST in a loop for a catalog-wide job — it’s the classic way to get throttled and end up with a slow, fragile script.
Why rate limits make the Bulk API necessary
Shopify protects its platform with rate limits — the REST API uses a leaky-bucket model, and GraphQL uses a calculated query cost with a replenishing budget. For everyday app traffic these are generous. For catalog-wide operations they’re a wall: try to read or update 40,000 variants with individual calls and you spend most of your time backing off and retrying, with total runtime measured in hours. The Bulk API exists precisely to sidestep this — you hand Shopify one operation, it runs server-side at its own pace, and you collect the output. That’s the entire value proposition: scale without throttling. But it’s also why the model is asynchronous and file-based, which is the complexity a no-code tool has to absorb on your behalf.
Handling errors and partial failures
The hardest part of bulk writes is that they don’t simply “work” or “fail” — they partially succeed. In a 40,000-row mutation, 39,800 rows might apply and 200 might fail validation. The API tells you this in the results JSONL, with per-row errors you have to parse, group, and act on. Robust automation means:
- Reading the full results file, not just the operation status, to find per-row errors.
- Categorising failures — bad input vs transient errors — and retrying only what should be retried.
- Keeping the source data so you can rebuild and re-submit the failed rows.
- Reconciling what actually changed against what you intended, so a silent partial failure doesn’t leave your catalog half-updated.
This is exactly the kind of unglamorous, high-stakes logic that takes real engineering to get right and real maintenance to keep right as Shopify versions its API. It’s also invisible until something goes wrong on a Friday — which is the argument for not hand-rolling it unless you must.
Versioning and maintenance — the long tail
Shopify versions its API quarterly, and mutations evolve — fields are deprecated, input shapes change, new capabilities (like newer variant bulk mutations) replace old ones. A bulk pipeline you build today is code you maintain indefinitely: tracking version deadlines, testing against new versions, and updating mutations before old ones sunset. For a team whose product is something other than Shopify integrations, that’s ongoing cost with no competitive upside. A maintained tool absorbs that churn — when Shopify changes the underlying mutation, the tool updates, and your catalog team never notices.
Best practices for bulk updates on Shopify
- Always read the results file after a bulk mutation — never assume every row succeeded.
- Test on a small product subset before running a mutation across the whole catalog.
- Keep a source of truth so you can re-publish correct data if a bulk write goes wrong.
- Batch sensibly and handle retries for the rows that fail.
- For multi-store, apply changes from one source rather than orchestrating per shop.
- For recurring, merchandiser-driven jobs, use a no-code tool instead of maintaining a pipeline.
Related reading: bulk editing Shopify product prices and managing Shopify metafields at scale.
Frequently asked questions
What is the Shopify Bulk Operations API and when should I use it?
It’s a GraphQL API that runs large queries and mutations asynchronously, returning results as a JSONL file. Use it for reading or updating tens of thousands of records without hitting standard rate limits — when you have developer time and the volume justifies it.
How do I bulk update products in Shopify?
With code, you stage a JSONL of changes and run bulkOperationRunMutation, then poll for completion and check the results. Without code, use Apimio: filter products and apply the change in bulk across every store.
What are the Shopify Bulk Operations API limits?
One bulk operation of each type runs at a time per shop, it’s asynchronous, results come as JSONL, mutations can partially fail, and there’s no native scheduling or rollback.
How do I update thousands of Shopify products at once?
Either via the Bulk Operations API (developer-led) or a no-code tool like Apimio that uses the same bulk infrastructure under the hood and handles the orchestration for you.
Can I bulk update Shopify products without code?
Yes. Apimio gives you no-code bulk updates to prices, tags, descriptions, and metafields across every connected store, built on Shopify’s bulk infrastructure.
Does the Bulk API support scheduling and rollback?
No — it runs when called. Scheduling a change and auto-reverting it is something you’d build yourself, or get out of the box with Apimio’s Sale Scheduler.
The Bulk API, made usable for catalog teams
Apimio puts Shopify’s bulk power behind a no-code workflow — mass updates across every store, no GraphQL required. Install free from the Shopify App Store.

Product Manager & Developer
Zia ur Rehman is Product Manager and lead developer at Apimio, building the Shopify-native catalog operations platform. He writes the technical guides on running Shopify catalogs at scale.
More about Zia ur Rehman →Ready to streamline your product data?
See how Apimio can help you manage product information across all your channels.