Shopify Graph QL not filtering on dates

Hello,

I am trying to get a query i have written for Shopify Grap QL to work properly.

I am executing it through a http post event (curl type) and the first part is

{“query”: "query {orders(first: 250, query: "created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:99Z")

I seem to get the correct start date each time, but the end date is ignored and when i check for the next cursor the cursors just keep going.

I have tried created_at and createdat but it seems to make no difference?

Thanks

Lucy

Hey @LucyHutchinson

You’re almost there — the issue seems to be with the way the created_at filter is being used, especially the timestamp format for the end time.

A couple of things to double-check:

End timestamp formatting:
2025-06-01T23:59:99Z is invalid — seconds can only go up to 59. So this should be:

created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:59Z

Use of multiple filters:
When combining filters in GraphQL, make sure they’re properly spaced or wrapped in parentheses (though Shopify’s query parser is a bit lenient, malformed queries can still behave unexpectedly).

So your final query should look something like this:

{
  "query": "query { orders(first: 250, query: \"created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:59Z\") { edges { node { id createdAt } cursor } pageInfo { hasNextPage } } }"
}

Pagination loop:
If the date range is correct and you’re still seeing an infinite pagination loop, double-check if you’re using the endCursor from pageInfo correctly in your next request. Sometimes, repeating the same cursor can re-fetch the same page.

Let me know if you have any questions!

Doh .. i changed the end time but the same thing is happening, and If I look at the returned json shopify pagination continues to show me has nextpage:true and the cursor, so my program fetches it - so I have to set a counter to terminate it. I already have a work REST version - but a this is being retired I am trying to convert everthing over :disappointed_face:

It just doesn’t seem to like the end parameters, I am expecting circa 400 orders so 2 pages…

Thanks

Lucy

Hi Lucy,

I spotted the issue with your Shopify GraphQL query! There are actually a couple of problems that are causing your end date to be ignored and the cursors to keep going.

Main Issues:

  1. Invalid seconds format: You have 23:59:99Z in your end date, but seconds can only go from 0-59, not 99. This invalid format is likely causing Shopify to ignore your end date entirely.
  2. Missing single quotes: According to Shopify’s documentation, when you include specific times with hours/minutes/seconds and timezone, you need to wrap your timestamps in single quotes.
  3. Query structure: You need to use AND between your date conditions for proper filtering.

Here’s the corrected query:

{
  "query": "query {
    orders(first: 250, query: \"created_at:>='2025-06-01T00:00:00Z' AND created_at:<='2025-06-01T23:59:59Z'\") {
      edges {
        node {
          id          createdAt          // ... your other fields
        }
        cursor      }
      pageInfo {
        hasNextPage        endCursor      }
    }
  }"}

Key changes:

  • Fixed 23:59:99Z to 23:59:59Z (valid seconds)
  • Added single quotes around both timestamps: ‘2025-06-01T00:00:00Z’
  • Added AND between the two date conditions
  • Used >= and <= operators for inclusive range

Alternative approach for single day queries: If you’re always querying for a single day, you could also use:

"created_at:>='2025-06-01T00:00:00Z' AND created_at:<'2025-06-02T00:00:00Z'"

This uses “less than” the start of the next day, which is often cleaner than trying to get the exact end-of-day timestamp.

This should fix both the end date being ignored and stop the infinite pagination issue you’re experiencing.

Let me know if you need any clarification or run into other issues!

Best regards,

Shubham | Untechnickle

Hey @LucyHutchinson

Issue: hasNextPage: true loop even with end date filter

Even though you’ve set an end date, Shopify may still paginate endlessly if:

  • Orders near the cut-off time have the same created_at timestamp (common during busy periods).

  • Your pagination logic keeps re-sending the same endCursor (due to overlapping timestamps or cursor reuse).

  • The GraphQL query string is too loose, and created_at boundaries aren’t enforced strictly.

**Suggested Fixes:**1. Double-check your pagination logic
Ensure you’re updating the endCursor after every successful response, like:

"after": "cursorValueHere"

If you’re not using the after: param, it will keep pulling from page 1.

  1. Sort your results
    Use sortKey: CREATED_AT to enforce ordering and avoid duplicate pages:

    orders(first: 250, query: "created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:59Z", sortKey: CREATED_AT)
    
  2. Use a manual page counter (as you mentioned)
    Yes, adding a counter is sadly a practical way to prevent infinite loops — set a max of 2-3 pages based on your expected volume (e.g. 400 orders / 250 per page = ~2 pages).

Example with pagination logic:

{
  "query": "query { orders(first: 250, after: \"<cursor_here>\", query: \"created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:59Z\", sortKey: CREATED_AT) { pageInfo { hasNextPage endCursor } edges { node { id createdAt } } } }"
}

Make sure you’re:

  • Updating after: each time with the new endCursor

  • Breaking the loop when hasNextPage is false or after a safe number of pages (your fallback)

Let me know if you have any questions!

Hi,

thanks for the reply but with

{“query”: "query {orders(first: 250, query: "created_at:>=‘2025-06-01T00:00:00Z’ AND created_at:<=‘2025-06-02T00:00:00Z’")

It still ignores the end date and keeps going…

Thanks

Lucy

Thanks for replying again,

I tried modifying my query as suggest but yourself and troy but it keeps going .. I check this block below to determine If I need to read more and look at has next page and the end cursor to issue the next request after..

“pageInfo”: {
“hasNextPage”: true,
“startCursor”: “eyJsYXN0X2lkIjoxMTc5MTc0NjAwNzQxNCwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDYgMTI6NTM6MjYuNjk2OTU3In0=”,
“endCursor”: “eyJsYXN0X2lkIjoxMTc5MzIyMjIzODU4MiwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDcgMDc6MDA6MTQuNDY0NzI2In0=”
}

Hi Camille, I am a shopify partner already and have been working with shopify for over 8 years now at the dev end, so - not really random code. The solutions being provided don’t seem to work - which is why I posted on here.

Thanks

Lucy

Hi Lucy,

Thanks for the follow-up! I see the date format is now correct, but you’re still experiencing the infinite pagination issue. This is actually a common problem with Shopify’s GraphQL orders query, and there are a few additional things we need to address:

## Issue #1: Missing sortKey Parameter

The most likely cause is that you’re missing a sortKey parameter. Without this, Shopify’s pagination can return inconsistent results and cause infinite loops.

Updated Query:

{
  "query": "query {
    orders(
      first: 250, 
      query: \"created_at:>='2025-06-01T00:00:00Z' AND created_at:<='2025-06-02T00:00:00Z'\",
      sortKey: CREATED_AT,
      reverse: false
    ) {
      edges {
        node {
          id
          createdAt
          // ... your other fields
        }
        cursor
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }"
}

## Issue #2: API Scope Limitations

By default, Shopify only allows access to the last 60 days of orders. If you’re querying for June 1st, 2025, and that’s beyond 60 days from today, you won’t get any results, but the API might still show hasNextPage: true.

Solution: Your app needs the read_all_orders scope to access historical orders beyond 60 days.

## Issue #3: Verify Your Date Range

Double-check that there are actually orders in your specified date range. You can test this by:

  1. First, check if orders exist in that range:
{
  "query": "query {
    orders(
      first: 10, 
      query: \"created_at:>='2025-06-01T00:00:00Z' AND created_at:<='2025-06-02T00:00:00Z'\",
      sortKey: CREATED_AT
    ) {
      edges {
        node {
          id
          createdAt
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }"
}
  1. If no results, try a broader range:
{
  "query": "query {
    orders(
      first: 10,
      sortKey: CREATED_AT,
      reverse: true
    ) {
      edges {
        node {
          id
          createdAt
        }
      }
    }
  }"
}

## Issue #4: Pagination Logic Check

Make sure your pagination logic checks for hasNextPage before continuing:

// Pseudo-code for proper pagination
let hasNextPage = true;
let cursor = null;
let allOrders = [];

while (hasNextPage) {
  const response = await makeGraphQLQuery(cursor);
  const orders = response.data.orders;
  
  // Add orders to your collection
  allOrders.push(...orders.edges.map(edge => edge.node));
  
  // Update pagination variables
  hasNextPage = orders.pageInfo.hasNextPage;
  cursor = orders.pageInfo.endCursor;
  
  // Safety check - if you're getting the same cursor, break
  if (previousCursor === cursor) {
    console.log("Same cursor detected, breaking loop");
    break;
  }
  previousCursor = cursor;
}

## Complete Working Example:

{
  "query": "query($cursor: String) {
    orders(
      first: 250,
      after: $cursor,
      query: \"created_at:>='2025-06-01T00:00:00Z' AND created_at:<='2025-06-02T00:00:00Z'\",
      sortKey: CREATED_AT,
      reverse: false
    ) {
      edges {
        node {
          id
          createdAt
          orderNumber
        }
        cursor
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }",
  "variables": {
    "cursor": null
  }
}

The sortKey: CREATED_AT parameter should resolve the infinite pagination issue you’re experiencing. This ensures Shopify returns results in a consistent order, which is essential for proper cursor-based pagination.

Let me know if this resolves the issue or if you’re still seeing problems after adding the sortKey!

Best regards,

Shubham | Untechnickle

Hi Lucy,

Thanks for the clarification, I totally get where you’re coming from now. Since you’ve been working with Shopify for 8+ years, I’ll cut right to the root of the issue with your query.

The problem isn’t with your created_at filter key, it’s with the timestamp format, specifically the seconds portion of your created_at:<= clause:

graphql

created_at:<=2025-06-01T23:59:99Z

The issue:

99 seconds is not valid — valid ranges for seconds are 00–59. Shopify silently ignores invalid date filters, which is why the end date is effectively dropped, and you’re getting all results after the start date.

The fix:

Use this corrected timestamp instead:

graphql

created_at:<=2025-06-01T23:59:59Z

So your full query should look like this:

json

{

“query”: “query { orders(first: 250, query: "created_at:>=2025-06-01T00:00:00Z created_at:<=2025-06-01T23:59:59Z") { edges { node { id createdAt } } } }”

}

Bonus Tip:

Shopify’s GraphQL API doesn’t throw explicit errors for invalid filter formats, it just processes what it can. So in cases like this, it helps to double-check ISO-8601 formatting strictly, especially on time ranges.

Let me know if you’re still getting an endless cursor loop after correcting the timestamp, I’ve handled pagination in some very large datasets too and can help finetune if needed.

Hi ya,

Unfortunately I still get the same results, shopify is ignoring the second part of the date field, and not returning me any errors, and unless I force terminate it will just continue…

{“query”: "query {orders(first: 250, query: "created_at:>=‘2025-06-01T00:00:00Z’ AND created_at:<=‘2025-06-02T00:00:00Z’",sortKey: CREATED_AT, reverse: false)

So I read after the first call…

“pageInfo”: {
“hasNextPage”: true,
“startCursor”: “eyJsYXN0X2lkIjoxMTc3NzE1NjMxNzU1OCwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDEgMDA6MTU6MDQuMjg4OTc5In0=”,
“endCursor”: “eyJsYXN0X2lkIjoxMTc3ODcyNjk4NjEwMiwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDEgMTY6MjU6NTYuNjc3Mzc5In0=”
}

Then the next page

“pageInfo”: {
“hasNextPage”: true,
“startCursor”: “eyJsYXN0X2lkIjoxMTc3ODcyOTgwNDE1MCwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDEgMTY6MjY6NTQuNTgxOTY4In0=”,
“endCursor”: “eyJsYXN0X2lkIjoxMTc4MDgxMjYwMzc2NiwibGFzdF92YWx1ZSI6IjIwMjUtMDYtMDIgMTI6NTI6MjEuNjMyOTMyIn0=”
}

But it should have stopped here as the created_at is now 2025-06-03 .. but unless I terminate it It will carry on indefinitely…

If I can’t find a direct fix, I will hack it and read the created_at myself and terminate when I pass my end date, but it’s annoying as the old way always worked!

thanks

Lucy

Hi Camille,

I’ve corrected this, and I am also now sending over in my headers.

o receive the search extension for all queries with and without warnings, you can send a Shopify-Search-Query-Debug=1 header to enable it manually.

So as you can see shopify now seems to be parsing what I am after correctly…

“search”: [
{
“path”: [
“orders”
],
“query”: “created_at:>=2025-06-01 AND created_at:<=2025-06-02”,
“parsed”: {
“field”: “created_at”,
“range_gte”: “2025-06-01T00:00:00+01:00”,
“range_lte”: “2025-06-02T23:59:59+01:00”
}
}
]

I took off the hours and minutes as it was making no difference to shopify.

The issue is still that the returned JSON object is still giving me continuous next page cursor page objects. And the end date has gone well past where I want it.

I think my solution for now is do examine the JSON myself look at created_at and decide when I want to terminate it. It wont be an exact science but it will solve the looping issues.

Thanks
Lucy

Hello everyone,
I thought I would post and say shopify support came back to me in another thread.

The issue with using cursors and query’s is you need to supply the query back each time along with the cursor.

if you keep your query field in your request along with the after:cursor then it will stop when there’s no more in range.

{
  orders(
    first: 250
    after: "EndCursorValues"
    query: "created_at:<'2025-06-02T00:00:00Z' AND created_at:>'2025-06-01T00:00:00Z'"
  )


`

What’s happening when you don’t include the query, it’s looking for all of the orders after the End cursor, so it will keep going until there are no more orders. The query fields aren’t encoded in the the cursor so you’ll need to keep those in your paginated query.

This was from KyleG in shopify support.
Hope this helps some one else!
Regards
Lucy
1 Like