I haven’t run over the cost yet (which I used to when graphql first came out). But what I do is on the variants portion of the products query, add in the pageInfo with the endCursor. Then I follow up with a call to the product passing in that query with the after on the variants.
pageInfo {
hasNextPage
endCursor
}
Now in my case I’m not getting all products, I’m getting one product, and it’s variants (and more info). So it’s possible you may hit costs I don’t. Not 100% what you asked but all I have (since I haven’t hit the cost limit but aren’t requesting all products).
(realized after writing this the emailed post was different than what you have now oh well).
Thanks for the info, yeah I ended up figuring out the nested pagination. I guess the only way to really find out about the cost limits is going to be to play around with various queries and product layouts and find some heuristic values that work…
If anyone has tuned this type of query for themselves would love to know what you came up with!
The Bulk Operations API is perfect for retrieving all data from a store of a specific type without hitting limits or implementing pagination.
You essentially create a meta-mutation that contains your query, then Shopify will give you a link to poll, or you can specify a webhook that should receive this data in a JSONL format for easy parsing.
I highly recommend this approach if you need all data of a certain type from a given Shopify store.
Note that you can only run one of these queries of each type (query or mutation) at a given time. So if you have parallel batch scripts attempting this they can sometimes run into each other and you have to sync them. Just something to consider before you plan to use it.
I use it to try to sync a set of products stored in another system as well as inventory so they sometimes have to wait for the other to finish.
You can run only one bulk operation of each type (bulkOperationRunMutation or bulkOperationRunQuery) at a time per shop. This limit is in place because operations are asynchronous and long-running. To run a subsequent bulk operation for a shop, you need to either cancel the running operation or wait for it to finish.
You’ve got two solid paths, depending on whether you truly need “everything” or just need to page reliably.
For on-demand reads, keep the nested pagination you discovered: page products, and for each product page its variants connection with pageInfo { hasNextPage endCursor } and an after cursor until done. The variants connection is capped at 250 per page, so you can’t raise it above that in a single call only iterate (Paginating results with GraphQL)
If your goal is to export all products + variants without juggling cursors or running into cost spikes, use Bulk Operations.
Create a bulkOperationRunQuery that selects products and their variants, then poll the URL (or receive a webhook) and parsee the JSONL. It’s designed exactly for “give me the whole dataset” scenarios and avoids normal pagination and throttling concerns (Perform bulk operations with the GraphQL Admin API)
A practical approach I’ve used: run Bulk Ops for full backfills or periodic reconciliation, and keep your GraphQL cursor-based query for deltas or per-product pages in the app UI.