🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup


Querying Sanity Content Lake with GROQ.

The GROQ query API is located on:


You can also send queries to the CDN endpoint for edge-cached results:

  • projectId and dataset can be found in the Sanity Studio configuration file or in the project management dashboard
  • The <YYYY-MM-DD> is the API version you want to target. In most cases, you can use the current date to target the newest API version at the time.

While you can use the HTTP API endpoint directly, we recommend using a client library if you can.

The GET method

The basic query API endpoint is:

GET /data/query/<dataset>?query=<GROQ-query>

GROQ-queries may also use parameters. In the query, these will be prefixed by a dollar sign ($) and could look something like this: $language. These are submitted as normal URL parameters:

GET /data/query/<dataset>?query=<GROQ-query>&$language="es"

Because GROQ needs to know the type of the query param clearly, you need to enclose string values in quotation marks, while numbers and booleans are written in the normal manner:

GET /data/query/<dataset>?query=<GROQ-query>&$string="es"&$bool=true&$number=12.8

Due to limitations in some browsers, we’ve put the max limit on GET queries at 11 KB. If your URL is longer than that, you must submit the query as a POST (see below). Queries sent over POST will also be cached on the CDN.


  • querystring

    the GROQ query itself

  • explainboolean | "only"

    Whether to include the query execution plan as plain text in an explain field, which may be useful when optimizing slow queries. Valid values are true (include explain output in result), only (only return the query plan—do not execute the query), or false (do not include explain output). Note that the plan output is only advisory - the contents are not documented and subject to change without warning.

  • variablesstring

    GROQ-queries may also use parameters. In the query these will be prefixed by a dollar sign. These are submitted as normal url-params

  • resultSourceMapboolean

    If true, the query result will include content source map metadata

  • perspectivestring

    Runs the query against the selected perspective:

    • 'previewDrafts': query runs as if all draft documents and changes were published
    • 'published': query runs as if no draft documents or changes exist
    • 'raw': query runs without distinguishing between drafts and published documents

    Visit the main documentation article on Perspectives to learn more about this feature.

  • tagstring

    Request tags are values assigned to API and CDN requests that can be used to filter and aggregate log data within request logs from your Sanity Content Lake. Learn more in the request tags documentation.

Result (queries)

  "ms": <server-side processing time>,
  "query": <submitted query>,
  "result": <query result>

Caveat: It is common to assume that the result is always an array of documents, but it isn't. It can be any valid JSON value. If the query is *[_id=="myId"][0], the result is the document object unwrapped, for count(*[_type == 'actor' && name match 'weaver']) the result will be a number. If the query is *[_type == 'actor'].name, the result is an array of strings (given that the names are strings).

“Not found” is considered harmless

When a document is not found, GROQ still evaluates to a value. Thus, you don't get 404 errors through this endpoint. Some examples:

  • *[_id == "missing"] results in []
  • count(*[_type == "typoType"]) results in 0
  • *[_id == "missing"][0] results in null
  • *[_id == "missing"][0].someProp also results in null

Syntax errors

  "error": {
    "query": "*[",
    "description": "Expected ']' following expression",
    "start": 1,
    "end": 2,
    "type": "queryParseError"

The start/end-values correspond to character positions in the submitted query and could be used to provide visual feedback as to where the error occurred. description is a human-readable description of the error, which may be revised and improved over time and thus should not be used for machine processing while type is a machine-readable symbol that is intended to be part of the stable API.

The POST method

Queries longer than 11kB can’t be submitted using the GET method, so they must be POSTed. Queries sent using the POST method will also be cached on the CDN.

POST /data/query/<dataset>

The payload is a JSON document like this:

  "query": "<the GROQ query>",
  // The params-part is optional:
  "params": {
    "language": "es" // you don't use the $-prefixes when submitting as json

The results of this endpoint are the same as those of the corresponding GET form described above.

Was this article helpful?