Query parameters
Every collection endpoint (/api/{entity_type}/{bundle}, see the Content API reference) accepts the JSON:API query parameters below. Parameters combine freely with &. Field paths always use field machine names, exactly as they appear as keys in responses, not admin-UI labels. A self-managed Drupal site on Cloud Platform serves its own JSON:API rather than the Content API; for its parameters, see Drupal’s JSON:API documentation.
For learning-oriented walkthroughs, see the query quickstart and the querying guide.
All fifteen operators below work on Source CMS. Two caveats: date fields compare as UNIX timestamps (see the operators section), and filtering the computed
moderation_statefield returns a server error (503) with any operator, including plain equality; filter onstatusinstead.
filter: match entries by field value
Section titled “filter: match entries by field value”Short form (equality)
Section titled “Short form (equality)”?filter[field_name]=valueExample: published entries only (fields trims the response for display, see sparse fieldsets):
GET /api/node/article?filter[status]=1&fields[node--article]=title{ "jsonapi": { "version": "1.1", "meta": { … } }, "data": [ { "type": "node--article", "id": "7f7dd525-…", "attributes": { "title": "Spring launch recap" } }, { "type": "node--article", "id": "08d88ef2-…", "attributes": { "title": "Designing for headless from day one" } }, { "type": "node--article", "id": "36579530-…", "attributes": { "title": "Q3 platform roadmap highlights" } }, { "type": "node--article", "id": "ba1f4fb1-…", "attributes": { "title": "How editorial workflows changed our release cadence" } } ], "meta": { "count": 4 }, "links": { "self": { … } }}Canonical form (any operator)
Section titled “Canonical form (any operator)”?filter[LABEL][condition][path]=FIELD_PATH&filter[LABEL][condition][operator]=OPERATOR&filter[LABEL][condition][value]=VALUELABEL is any name you choose for the condition. The condition keys:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
[condition][path] |
string | yes | none | Field path, machine names only; dot notation follows relationships (tags.name) |
[condition][operator] |
string | no | = |
One of the operators below, exactly as written |
[condition][value] |
string or array of string | all operators except IS NULL/IS NOT NULL |
none | The comparison value; multi-value operators take repeated value[] entries |
[condition][memberOf] |
string | no | none | Attaches the condition to a named filter group |
Example: titles containing “launch”:
GET /api/node/article?filter[t][condition][path]=title &filter[t][condition][operator]=CONTAINS &filter[t][condition][value]=launch &fields[node--article]=title{ "jsonapi": { "version": "1.1", "meta": { … } }, "data": [ { "type": "node--article", "id": "7f7dd525-…", "attributes": { "title": "Spring launch recap" } } ], "meta": { "count": 1 }, "links": { "self": { … } }}Operators
Section titled “Operators”| Operator | Matches | Example (condition parts) |
|---|---|---|
= |
Exact value (the default; the short form uses it) | path=status, operator==, value=1 |
<> |
Anything except the value | path=status, operator=<>, value=0 |
> |
Greater than | path=created, operator=>, value=1893456000 (a timestamp; see the date caveat below) |
>= |
Greater than or equal | path=field_price, operator=>=, value=10 |
< |
Less than | path=created, operator=<, value=1893456000 (a timestamp; see the date caveat below) |
<= |
Less than or equal | path=field_price, operator=<=, value=100 |
STARTS_WITH |
String prefix | path=title, operator=STARTS_WITH, value=How |
CONTAINS |
Substring | path=title, operator=CONTAINS, value=release |
ENDS_WITH |
String suffix | path=title, operator=ENDS_WITH, value=2026 |
IN |
Any of several values, pass value[] multiple times |
path=tags.name, operator=IN, value[]=news&value[]=events (each entry repeats the same filter[...] prefix; full example below) |
NOT IN |
None of several values | path=tags.name, operator=NOT IN, value[]=archive |
BETWEEN |
Inside a range, exactly two value[] entries |
path=field_price, operator=BETWEEN, value[]=10&value[]=100 |
NOT BETWEEN |
Outside a range | path=field_price, operator=NOT BETWEEN, value[]=10&value[]=100 |
IS NULL |
Field has no value (omit value) |
path=field_subtitle, operator=IS NULL |
IS NOT NULL |
Field has any value (omit value) |
path=field_subtitle, operator=IS NOT NULL |
Date comparisons use UNIX timestamps (verified). created and changed are stored as timestamps, and comparison operators compare against the raw stored value, even though responses display ISO 8601. filter[t][condition][value]=2030-01-01 silently compares against the integer 2030 (matching almost nothing, or everything, depending on direction); filter[t][condition][value]=1893456000 compares against the date you meant. Convert dates to timestamps before filtering: date -d 2030-01-01 +%s (GNU) or date -j -f %Y-%m-%d 2030-01-01 +%s (macOS).
Full IN example:
GET /api/node/article?filter[tags][condition][path]=tags.name &filter[tags][condition][operator]=IN &filter[tags][condition][value][]=news &filter[tags][condition][value][]=eventsFilter groups (AND/OR)
Section titled “Filter groups (AND/OR)”Multiple conditions are ANDed by default. To OR them, declare a group with a conjunction and attach conditions to it with memberOf:
GET /api/node/article?filter[or-group][group][conjunction]=OR &filter[a][condition][path]=title &filter[a][condition][operator]=CONTAINS &filter[a][condition][value]=beta &filter[a][condition][memberOf]=or-group &filter[b][condition][path]=title &filter[b][condition][operator]=CONTAINS &filter[b][condition][value]=preview &filter[b][condition][memberOf]=or-groupThis matches articles whose title contains “beta” OR “preview”. Groups can nest: a group itself takes memberOf to join a parent group. The JSON:API Query Builder (the interactive query workbench in your site’s admin UI at API > JSON:API Query Builder) generates these in its Filter Groups (AND/OR logic) section.
Used in: querying guide → filtering.
fields[TYPE]: sparse fieldsets
Section titled “fields[TYPE]: sparse fieldsets”Return only the named fields for a resource type. The type key is the full resource type, {entity_type}--{bundle}:
GET /api/node/article? fields[node--article]=title,created,image- Fields not listed are omitted from both
attributesandrelationships. - Repeat the parameter per type to trim included resources too:
&fields[media--image]=media_image. - Smaller responses are faster; the Query Builder’s
Fieldstab builds this parameter.
Each entry’s attributes carries only the named field, and relationships is gone entirely:
{ "jsonapi": { "version": "1.1", "meta": { … } }, "data": [ { "type": "node--article", "id": "7f7dd525-…", "links": { … }, "attributes": { "title": "Spring launch recap" } }, … ], "meta": { "count": 4 }, "links": { "self": { … } }}Used in: querying guide → sparse fieldsets.
include: related resources in one request
Section titled “include: related resources in one request”Embed related entries in the response’s top-level included array instead of fetching them one by one:
GET /api/node/article?include=image- Comma-separate multiple relationships:
include=image,tags. - Dot notation follows relationships through intermediate entities, e.g. through a media entity to its file:
include=image.media_image. - No nesting-depth cap is documented, and none was encountered in verification (two-level chains like
image.media_imagework; each segment must be a relationship field, or the request 400s naming the failing segment, see 400 responses).
For the two-level chain GET /api/node/article?include=image.media_image&page[limit]=1, every entity on the chain lands in included, once each:
{ "jsonapi": { "version": "1.1", "meta": { … } }, "data": [ { "type": "node--article", "id": "7f7dd525-…", "attributes": { … }, "relationships": { "image": { "data": { "type": "media--image", "id": "5f2c5cde-…" } }, … } } ], "included": [ { "type": "media--image", "id": "5f2c5cde-…", "attributes": { "name": "Portal verification hero", … } }, { "type": "file--file", "id": "0d28b1dd-…", "attributes": { "filename": "portal-verification-hero.png", "uri": { "url": "/sites/default/files/2026-07/portal-verification-hero.png", … }, … } } ], "meta": { … }, "links": { … }}Used in: querying guide → relationships and the N+1 pitfall.
page[limit] and page[offset]: pagination
Section titled “page[limit] and page[offset]: pagination”GET /api/node/article?page[limit]=10&page[offset]=20| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page[limit] |
integer | no | 50 | Maximum number of entries in one response; values above 50 are clamped to 50 (verified). Lower limits make the server respond faster |
page[offset] |
integer | no | 0 | Number of entries to skip before the first returned entry |
Responses include a links.next URL while more entries remain. Follow it rather than computing offsets by hand. It adds the page[offset] and preserves every other parameter, with square brackets percent-encoded. The shape for GET /api/node/article?page[limit]=2&sort=-created on a five-entry collection:
{ "meta": { "count": 5 }, "links": { "next": { "href": "https://your-site.example.com/api/node/article?page%5Boffset%5D=2&page%5Blimit%5D=2&sort=-created" } }}meta.count is the collection total. Caveat: pagination links are computed before access filtering, so a page can return "data": [] and still carry links.next/links.last (for example when every entry on it is unpublished). Don’t treat an empty page as the end of the collection; follow links.next, or check meta.count and meta.omitted (see omitted resources).
Used in: query quickstart (page[limit] is its one variation), querying guide → pagination.
sort: result order
Section titled “sort: result order”?sort=FIELD_PATH| Syntax | Meaning |
|---|---|
sort=created |
Ascending by created |
sort=-created |
Descending: prefix the path with - |
sort=-status,created |
Multiple keys, comma-separated, applied left to right |
sort=tags.name |
Dot notation sorts by a related entry’s field |
Example: alphabetical by title:
GET /api/node/article?sort=title&fields[node--article]=title{ "jsonapi": { "version": "1.1", "meta": { … } }, "data": [ { "type": "node--article", "id": "08d88ef2-…", "attributes": { "title": "Designing for headless from day one" } }, { "type": "node--article", "id": "ba1f4fb1-…", "attributes": { "title": "How editorial workflows changed our release cadence" } }, { "type": "node--article", "id": "36579530-…", "attributes": { "title": "Q3 platform roadmap highlights" } }, { "type": "node--article", "id": "7f7dd525-…", "attributes": { "title": "Spring launch recap" } } ], "meta": { "count": 4 }, "links": { "self": { … } }}Used in: querying guide → sorting.
Language
Section titled “Language”The content language is selected per request with the langCode query parameter, provided by the JSON:API Multilingual module, which every Source CMS site ships active (confirmed 2026-08-19; behavior verified live the same day). Selection is strict: a missing translation is a 404 whose error object names the entry’s viewable translations in errors[0].detail and, machine-readably, in errors[0].meta.availableTranslations (captured: The "fi" translation of the specified resource does not exist. Available translations: en.). A language not enabled on the site is a 422 (captured: The specified language ("de") is invalid or has not been configured.).
Adding includeFallback=1 resolves the read through the site’s language fallback chain instead, so an existing entry always returns 200. It is valid only together with langCode, only on reads, and only with the literal value 1; the rejections are 400s (captured: The "includeFallback" query parameter requires the "langCode" query parameter. and The "includeFallback" query parameter only accepts the value "1".).
Both parameters apply to individual, collection, and related reads and, for langCode, to writes (reads and writes verified 2026-08-19). The language actually served is reported by the Content-Language response header (individual responses) and each entry’s langcode attribute. See the multilingual guide for requesting translations and detecting fallback, and translating content for the per-translation writes. A site without the module (a local stand-in, say) accepts langCode without acting on it, the treatment JSON:API’s parameter naming rules prescribe for implementation-specific parameters a server does not implement.
A langcode URL path prefix (for example /es/api/node/article) resolves for enabled languages and 404s otherwise. Whether it selects the content language is site negotiation configuration, not a contract: a local product instance negotiated by prefix (2026-07-03), while the Source CMS site verified 2026-08-19 returned the default-language entry under a prefix even when the translation existed. Select with langCode, not the prefix. filter[langcode] filters by translation existence without changing the language served (verified 2026-08-19: filter[langcode]=fi matched an entry with a Finnish translation and returned it in the default language).
A lowercase ?langcode= is rejected with 400 and the literal detail The following query parameters violate the JSON:API spec: 'langcode'. (verified 2026-07-03 and 2026-08-19), like any parameter that violates JSON:API’s naming rules, lang included (see 400 responses). The Accept-Language request header does not negotiate content language (verified 2026-07-03 and 2026-08-19). Each entry’s language appears verbatim as the langcode attribute in responses.
400 responses for malformed queries
Section titled “400 responses for malformed queries”A malformed query returns 400 Bad Request with a JSON:API error document:
{ "jsonapi": { "version": "1.1" }, "errors": [ { "title": "Bad Request", "status": "400", "detail": "..." } ]}The errors[].detail member names what was wrong. Causes mapped to the literal detail (JSON:API 1.1):
| Cause | Literal detail |
Fix |
|---|---|---|
Unknown field path in filter or sort |
Invalid nested filtering. The field `nonexistent_field`, given in the path `nonexistent_field`, does not exist. |
Use the field’s machine name, not its label; the Query Builder’s Field dropdown lists valid paths |
Invalid or misspelled operator |
The 'LIKE' operator is not allowed in a filter parameter. (the accepted set is not listed in the error) |
Use one from the operators table, exactly, uppercase where shown |
include path that isn’t a relationship |
`uid.bad.deep` is not a valid include path. `bad` is not a valid relationship field name. Possible values: roles. (the error lists the valid relationship names at the failing segment) |
Only relationship fields can be included; attributes cannot |
A non-numeric page[limit] or page[offset] does not return a 400. The invalid value is silently ignored and the default applies. If your limit doesn’t seem to take effect, check its spelling and value before suspecting the data.
An unknown all-lowercase query parameter violates JSON:API’s parameter naming rules and is rejected (verified): The following query parameters violate the JSON:API spec: 'cb'. A name carrying a non-lowercase character (langCode, say) is an implementation-specific parameter under those same rules: it passes validation and does nothing unless something on the site implements it (see Language). Either way there is no cache-busting via made-up parameters.
Used in: querying guide → troubleshooting.
Used in
Section titled “Used in”- Content API quickstart:
page[limit] - Querying guide: every parameter above
- Multilingual content guide: language handling
- Translating content: per-translation writes with
langCode - Creating & updating content via the API: reading back what you write
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)