Response document
Every response the Content API returns is one JSON:API document with the same envelope. Each member is explained below, with the matching slice of a live response pinned alongside it. For the endpoints that produce these documents, see the Content API reference; for the query parameters that shape them, see the query parameter reference.
The success document
Section titled “The success document”The slices below come from one response to GET /api/node/article?include=image,tags&page[limit]=1.
jsonapi: always present. The spec version the document conforms to.
"jsonapi": { "version": "1.1", "meta": { … }}data: the requested resource(s). Its shape follows the endpoint:
| Request | data is |
|---|---|
A collection (/api/node/article) |
an array of resource objects |
A single resource (/{uuid}) |
one resource object |
| A relationship pointing at nothing | null |
Each resource is identified by type + id together, the same pair used in data, in included, and inside relationship linkage. A document is a success or a failure, never both: data and errors never appear together.
"data": [ { // type + id identify any resource, everywhere. "type": "node--article", "id": "7f7dd525-fa49-465e-976d-a50ab5425c49", "links": { "self": { "href": "…/api/node/article/7f7dd525-…" } }, "attributes": { … }, "relationships": { … } }]data[].attributes: every non-reference field of the entry, keyed by machine name. The field-type table maps each type to its JSON shape.
"attributes": { "langcode": "en", "status": true, "title": "Spring launch recap", "created": "2026-07-02T14:51:56+00:00", "moderation_state": "published", "body": { "value": "The spring launch shipped on time…", "format": "filtered_html", "processed": "<p>The spring launch shipped on time…</p>" }}data[].relationships: every reference field, keyed by machine name. Each holds resource identifiers (type + id), never the referenced entries themselves:
| Cardinality | data is |
|---|---|
| One-to-one | one identifier, or null |
| One-to-many | an array of identifiers |
The entries themselves arrive in top-level included when the request adds ?include=.
"relationships": { // one-to-one: "data" is one identifier (or null) "image": { "data": { "type": "media--image", "id": "5f2c5cde-…" } }, // one-to-many: "data" is an array of identifiers "tags": { "data": [ { "type": "taxonomy_term--tags", "id": "dbc476ff-…" }, { "type": "taxonomy_term--tags", "id": "19059886-…" } ] }}included: the referenced entries themselves, present only when the request used ?include= and at least one named relationship resolved to an entity; an include that matches nothing (every relationship empty) omits the key entirely rather than sending [], so read it as doc.included ?? []. Each entry:
- has the same
type/id/attributes/relationshipsshape as adataresource, - is deduplicated: one entry per resource, however many relationships point to it,
- is matched back to a relationship by its
type+id.
"included": [ { "type": "media--image", "id": "5f2c5cde-…", "attributes": { "name": "Portal verification hero", … } }, { "type": "taxonomy_term--tags", "id": "dbc476ff-…", "attributes": { "name": "news", … } }]meta.count: collection responses only, the total number of matching entries, counted before pagination and before access filtering. It can therefore exceed what data actually contains (see the empty-data case below).
"meta": { "count": 4 }links: where this document came from and how to page through it:
| Key | Appears |
|---|---|
self |
always |
next / prev |
when a next / previous page exists |
first / last |
on a paginated collection |
"links": { "self": { "href": "…/api/node/article?…" }, "next": { "href": "…/api/node/article?page%5Boffset%5D=1&…" }, "last": { "href": "…/api/node/article?page%5Boffset%5D=3&…" }}When data is empty but the collection is not
Section titled “When data is empty but the collection is not”When access control hides entries (most commonly unpublished ones), the response stays 200 and signals the gap in meta. The slices below are a separate capture of the article collection (a different snapshot than the one above, hence the different meta.count), on a page whose two newest entries are unpublished.
data: []: nothing visible on this page, but an empty page is not the end of the collection: links.next still points onward, so keep following it.
"data": [],"links": { "self": { … }, "next": { … }, "last": { … }}meta.omitted: meta.count (7) disagrees with data.length on purpose: count is the whole collection, data is only what you may see. omitted.links names why each hidden resource is missing, one link per entry.
"meta": { "count": 7, "omitted": { "detail": "Some resources have been omitted because of insufficient authorization.", "links": { "help": { "href": "https://www.drupal.org/docs/8/modules/json-api/filtering#filters-access-control" }, "item--IYrGUC8": { "href": "…/api/node/article/c59ab903-…", "meta": { "rel": "item", "detail": "The current user is not allowed to GET the selected resource. The 'view any unpublished content' permission is required." } } } }}The error document
Section titled “The error document”On failure, errors replaces data entirely. Here, a filter on a field that doesn’t exist returns 400 Bad Request:
errors: replaces data on failure; one object per error, every member a string:
| Member | Holds |
|---|---|
detail |
the diagnostic: names exactly what was wrong (read this first) |
title |
the generic status name, e.g. "Bad Request" |
status |
the HTTP status as a string, e.g. "400" |
links |
via (the offending request) and info (the spec for that status) |
"errors": [ { "title": "Bad Request", // generic name of the status code "status": "400", // the HTTP status, as a string // the member to read: names exactly what was wrong "detail": "Invalid nested filtering. The field `nonexistent_field`, given in the path `nonexistent_field`, does not exist.", "links": { "via": { "href": "…/api/node/article?filter…" }, "info": { "href": "https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1" } } }]Used in
Section titled “Used in”- Content API quickstart: first sight of
data,attributes,meta.count - Querying guide:
relationshipsvs.included, pagination links, omissions - First fetch and direct-fetch guide: reading
attributesandincludedfrom frontend code - Creating & updating content via the API: the error document on rejected writes
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)