Reading documents back through the API.

The query API is located on https://<projectId>.api.sanity.io/v2021-06-07/data/query/<dataset>. You can find more information about API URLs and how to find your project id here

The GET form

The basic query api endpoint is very simple:

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-params:

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 quotes, while numbers and booleans are written in the normal manner:

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

A nice feature of GROQ/Sanity is that you can submit very large queries composing a lot of separate queries as one. This is especially powerful when going through the API CDN, since the entire composite is cached as one which can make for very very fast frontends indeed. But of course there is a limit: 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 will have to submit the query as a POST (see below) foregoing any caching.


  • 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

Result (queries)

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

Caveat: It is a common mistake to assume that the result is 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" 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 corresponds 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 an 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 form

Queries longer than 11kB can't be submitted using the GET-method (which otherwise should always be the preferred way), so must be POSTed. This endpoint is not supported by the API 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 is the same as its corresponding GET form described above.

Was this article helpful?