Export orders for a date range with a bulk operation

Export every Shopify order in a date range, with line items, to a JSONL file using bulkOperationRunQuery and an orders search filter.

API
Admin GraphQL API
Operations
bulkOperationRunQuery, bulkOperation
Access scopes
read_orders; add read_all_orders for orders older than 60 days
Replaces (REST)
GET /admin/api/orders.json with created_at_min and created_at_max
API version
2026-10
Last checked

In short

To export Shopify orders for a date range, pass an orders query with a created_at filter to the bulkOperationRunQuery mutation, poll the bulkOperation query until the status is COMPLETED, then download the JSONL file from url. Line items arrive as separate lines with a __parentId. Your app needs read_orders, plus read_all_orders for orders older than 60 days.

How to use it

  1. Write the orders query with a search filter such as created_at:>=2026-07-01 AND created_at:<2026-10-01. Put only the fields you need inside it.
  2. Send it as the query variable of StartOrderExport and keep the returned bulk operation ID.
  3. Poll OrderExportStatus every few seconds, or subscribe to the bulk_operations/finish webhook, until status is COMPLETED.
  4. Download the file at url and read it line by line. Attach each line item to its order with __parentId.

Code

bulk-operation-run-query.graphql
mutation StartOrderExport($query: String!) {
  bulkOperationRunQuery(query: $query) {
    bulkOperation {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}
bulk-operation-run-query.variables.json
{
  "query": "{ orders(query: \"created_at:>=2026-07-01 AND created_at:<2026-10-01\", sortKey: CREATED_AT) { edges { node { id name createdAt displayFinancialStatus displayFulfillmentStatus currencyCode totalPriceSet { shopMoney { amount currencyCode } } lineItems { edges { node { id sku quantity originalUnitPriceSet { shopMoney { amount } } } } } } } } }"
}
bulk-operation-run-query.response.json
{
  "data": {
    "bulkOperationRunQuery": {
      "bulkOperation": {
        "id": "gid://shopify/BulkOperation/1234567890",
        "status": "CREATED"
      },
      "userErrors": []
    }
  }
}
bulk-operation-status.graphql
query OrderExportStatus($id: ID!) {
  bulkOperation(id: $id) {
    id
    status
    errorCode
    objectCount
    rootObjectCount
    url
    partialDataUrl
  }
}
bulk-operation-status.variables.json
{
  "id": "gid://shopify/BulkOperation/1234567890"
}
bulk-operation-status.response.json
{
  "data": {
    "bulkOperation": {
      "id": "gid://shopify/BulkOperation/1234567890",
      "status": "COMPLETED",
      "errorCode": null,
      "objectCount": "15872",
      "rootObjectCount": "4210",
      "url": "https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/bulk-operation-outputs/example.jsonl",
      "partialDataUrl": null
    }
  }
}
result.jsonl
{"id":"gid://shopify/Order/1111111111","name":"#1001","createdAt":"2026-07-01T08:14:22Z","displayFinancialStatus":"PAID","displayFulfillmentStatus":"FULFILLED","currencyCode":"EUR","totalPriceSet":{"shopMoney":{"amount":"98.00","currencyCode":"EUR"}}}
{"id":"gid://shopify/LineItem/2222222222","sku":"LS-M","quantity":2,"originalUnitPriceSet":{"shopMoney":{"amount":"49.00"}},"__parentId":"gid://shopify/Order/1111111111"}

Written by Lintel. MIT license: free to use in your own store and in client work.

Limits and edge cases

  • Without read_all_orders, only orders created in the last 60 days are returned. Public apps need Shopify’s approval for it; apps created in the Dev Dashboard for stores in your own organization don’t.
  • The filter takes a date or a full ISO 8601 timestamp, such as created_at:>='2026-07-01T00:00:00Z', when the range must be exact to the second.
  • Customer fields such as email and addresses fall under Shopify’s protected customer data rules. Request that access before adding them to the query.
  • rootObjectCount counts orders only; objectCount also counts the line items.

Questions

How do I export Shopify orders by date with GraphQL?
Run bulkOperationRunQuery with an orders connection filtered by created_at, wait for the operation to complete, then download the JSONL file. For a few hundred orders, a paginated orders query with the same filter is enough.
Why does my export stop at 60 days?
read_orders only covers orders created in the last 60 days. Request read_all_orders alongside it to read older orders.
Can I filter by updated date instead of created date?
Yes. The orders query accepts updated_at and processed_at filters too, which suits incremental syncs that only pick up changed orders.
How do I turn the JSONL into a CSV?
Read the file line by line, keep orders in a map by id, add each line item to its parent with __parentId, then write one CSV row per order or per line item.
  • Validated against the Admin or Storefront API schema for version 2026-10 on the date shown. Check the version in your request URL.
  • IDs and store addresses in the examples are placeholders. Replace them with your own.
  • Admin API calls need an access token from your app. Never put an Admin API token in a storefront or theme.
  • MIT license. Use the code in your own store or in client work.

Need this built, tested and maintained for your store? See API integrations.

Have something to build? Let’s talk it through.

Taking a limited number of projects at a time. Every brief is read by an engineer, not a salesperson.

Gencer KarakayaShopify Engineer

An engineer reads your brief, and we reply by email.