Different results querying Sanity via curl vs JavaScript client

8 replies
Last updated: Jan 12, 2021
Hello! Is there any reason I would get different results between these two methods of querying data?

curl <https://xuaf19yr.api.sanity.io/v1/data/query/production>\?query\=\*%5B_type%20%3D%3D%20%22articlePage%22%5D%20%7B%0A%09...%0A%7D

const sanity = require('@sanity/client')

const sanityClient = sanity({
  projectId: 'xuaf19yr',
  dataset: 'production',
  useCdn: false,
})

await sanityClient.fetch(`
  *[_type == "articlePage"] {
    ...
  }
`)
Any help appreciated!
AI Update

Good question! Since both methods are using API v1 (your curl explicitly uses /v1/ and the JavaScript client defaults to v1 when no apiVersion is specified), they should actually return the same results. Here are the most likely reasons you're seeing differences:

1. Missing apiVersion Parameter (Best Practice Issue)

While both default to v1, you should always explicitly set the apiVersion in your client to ensure consistent behavior and avoid surprises when defaults change:

const sanityClient = sanity({
  projectId: 'xuaf19yr',
  dataset: 'production',
  useCdn: false,
  apiVersion: '2024-01-01', // Add this!
})

2. CDN vs API Endpoint

Your JavaScript client has useCdn: false, which should hit the API directly. Double-check that your curl is also using the API endpoint (not the CDN):

  • API: https://xuaf19yr.api.sanity.io ✓ (what you're using)
  • CDN: https://xuaf19yr.apicdn.sanity.io

If one were accidentally hitting the CDN, you might see cached data up to 10 minutes old.

3. Authentication Differences

If your JavaScript client has any authentication token configured (like a token parameter), it would have access to different data than your unauthenticated curl request. Make sure both requests have the same authentication status.

4. URL Encoding or Query Differences

Your curl shows URL-encoded parameters. Verify the actual query string being sent is identical. Try logging the network request from your JavaScript client to compare.

Debugging Steps

  1. Check what's different: Are you getting different numbers of documents, or different content within the documents?
  2. Add explicit apiVersion to rule out any version-related issues
  3. Log the actual requests from both methods to compare headers and query strings
  4. Check for draft documents: If you have documents with IDs starting with drafts., both methods should return them since v1 uses the raw perspective by default

Note: The default perspective only changed from raw to published in API version v2025-02-19, not in v1, so both your methods should be using the same raw perspective and returning all documents including drafts.

What specific differences are you seeing in the results? That would help narrow down the exact cause!

Show original thread
8 replies
Actually, I do get the same result!
However, locally using "Vision" in Studio I'm getting the result I am expecting. I am probably missing something here.
What's the result you are not getting that you'd like to get?
If it's something like including
drafts.<id>
documents in your results, this will require a read token as these documents are on a so-called non-root path (they have a
.
in the ID) and therefore private even in a public dataset.
To set up a read token, go to your project on
manage.sanity.io , then to Settings and API. You should see an option there to add a new token. If you create a read token, you can then add it to the client configuration as follows:
const sanityClient = sanity({
  projectId: 'xuaf19yr',
  dataset: 'production',
  token: '<SANITY_READ_TOKEN>'
  useCdn: false,
})
Thank you for your reply
user M
! Using a token I'm getting the results I'm expecting!
Very helpful and a huge load off my shoulders!
Glad to hear! Was there anywhere you checked in the documentation and expecting to find this information? Just in case we can make it more visible 🙂
I’m not sure to be honest, I encountered this while using the seemingly excellent https://github.com/LiamMartens/sanity-plugin-intl-input . The plugin namespaces translated documents using i18n.&lt;id&gt;, which explains the issue. Maybe it would be a good idea for the plugin author to mention this somewhere in the plugin documentation, I’ll mention it when closing the issue I opened regarding this.
Thanks Jacob, that's great feedback indeed. I think Liam (the plugin's author) is considering adding the option to either use non-root paths or the former notation that put the i18n info at the back of the ID without a
.
. Feels like a good opportunity to add the note you suggested alongside the option itself 🙂

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?