Skip to content

The base URL, the JSON:API endpoint shape, the headers, and the shared error codes are identical on every Source CMS site. The endpoint list is not: it is generated per site, and served from the site itself (see below).

These shared endpoints are also published as a machine-readable OpenAPI 3.1 spec at a stable URL, for code generators and API collections. To explore endpoints interactively, use your site’s own OpenAPI documentation (API > OpenAPI documentation): it renders the endpoints your site actually exposes, against your real content model, and calls them live.

The authoritative, always-current list of endpoints for a specific site lives on that site:

Tool Where What it’s for
OpenAPI documentation API > OpenAPI documentation in your site’s admin UI The source of truth for your site’s live, generated endpoint list. Browse every available endpoint and test calls interactively.

Because every enabled content type gets its endpoints generated automatically, any static copy of the list goes stale. Your site’s OpenAPI documentation answers “what endpoints does my site have”; the shared rules below answer “what is true of every site”.

Used in: query quickstart, querying guide.

$DRUPAL_SITE_URL/api
  • The base URL is your site URL (the canonical DRUPAL_SITE_URL environment variable, no trailing slash) plus /api.
  • There is no version segment in the path. The surface follows the JSON:API 1.1 specification; every response’s jsonapi.version member reports 1.1.
  • GET $DRUPAL_SITE_URL/api (the API root) returns the index of resource endpoints available on that site, keyed by resource type (for example node--article).
  • All requests and responses use the JSON:API media type application/vnd.api+json (see Headers).

Used in: auth quickstart, first fetch.

Every resource endpoint follows the same pattern, built from an entity type machine name and a bundle machine name (the mapping is in the content model reference):

Path Returns Used in
/api/{entity_type}/{bundle} Collection of entries of that bundle, e.g. /api/node/article query quickstart
/api/{entity_type}/{bundle}/{uuid} One entry, addressed by its UUID (the id in every response) querying guide
/api/{entity_type}/{bundle}/{uuid}/{field} The related entries a relationship field points at querying guide
/api/{entity_type}/{bundle}/{uuid}/relationships/{field} The relationship itself (type + id identifiers only, not full entries) querying guide

Collections accept the query parameters documented exhaustively in the query parameter reference (filter, fields, include, page, sort). Every endpoint returns one JSON:API document; the response document reference annotates each envelope member (data, attributes, relationships, links, meta, included, errors) on a live response.

One request/response pair per path shape. All four use the same article, so the shapes are directly comparable. $TOKEN is an access token from step 3 of the auth quickstart.

Collection: data is an array:

Terminal window
curl -s "$DRUPAL_SITE_URL/api/node/article" \
-H "Accept: application/vnd.api+json" \
-H "Authorization: Bearer $TOKEN"
{
"jsonapi": { "version": "1.1", "meta": { … } },
"data": [
{ "type": "node--article", "id": "7f7dd525-fa49-465e-976d-a50ab5425c49",
"attributes": { "title": "Spring launch recap", … }, "relationships": { … } },
…
],
"meta": { "count": 4 },
"links": { "self": { … } }
}

Individual entry: same shape, but data is one object, and there is no meta.count:

Terminal window
curl -s "$DRUPAL_SITE_URL/api/node/article/7f7dd525-fa49-465e-976d-a50ab5425c49" \
-H "Accept: application/vnd.api+json" \
-H "Authorization: Bearer $TOKEN"
{
"jsonapi": { "version": "1.1", "meta": { … } },
"data": {
"type": "node--article", "id": "7f7dd525-fa49-465e-976d-a50ab5425c49",
"attributes": { "title": "Spring launch recap", … }, "relationships": { … }
},
"links": { "self": { … } }
}

Related subpath (/{field}): the referenced entries themselves, full attributes:

Terminal window
curl -s "$DRUPAL_SITE_URL/api/node/article/7f7dd525-fa49-465e-976d-a50ab5425c49/image" \
-H "Accept: application/vnd.api+json" \
-H "Authorization: Bearer $TOKEN"
{
"jsonapi": { "version": "1.1", "meta": { … } },
"data": {
"type": "media--image", "id": "5f2c5cde-9f49-48a9-8fb3-fbf328a710a2",
"attributes": { "name": "Portal verification hero", … }, "relationships": { … }
},
"links": { "self": { … } }
}

Relationship subpath (/relationships/{field}): the linkage only: type + id identifiers, no attributes. Use it to read or rewrite what an entry points at without fetching the targets:

Terminal window
curl -s "$DRUPAL_SITE_URL/api/node/article/7f7dd525-fa49-465e-976d-a50ab5425c49/relationships/tags" \
-H "Accept: application/vnd.api+json" \
-H "Authorization: Bearer $TOKEN"
{
"jsonapi": { "version": "1.1", "meta": { … } },
"data": [
{ "type": "taxonomy_term--tags", "id": "dbc476ff-1ff2-4bec-a1fe-cb3981dd385a", "meta": { … } },
{ "type": "taxonomy_term--tags", "id": "19059886-e390-4520-b880-e372c2d1d784", "meta": { … } }
],
"links": { "self": { … }, "related": { … } }
}
Method Target Requires
GET Collection or individual entry Always enabled
POST Collection (create an entry) The site’s allowed-operations setting at API > JSON:API set to Read and write, and a token from an API client granted the content:administer scope; the full scope list is in the authentication reference
PATCH Individual entry (update) Same as POST
DELETE Individual entry (remove) Same as POST

Writes are opt-in per site; the writing content guide covers enabling and using them.

Used in: query quickstart, writing content guide.

Header Value When Used in
Accept application/vnd.api+json Every request query quickstart, direct-fetch guide
Content-Type application/vnd.api+json Every request with a body (POST, PATCH) writing content guide
Authorization Bearer <access token> Every request, unless the site has enabled public API access at API > JSON:API auth quickstart, auth guide

Tokens come from the OAuth 2.0 endpoints documented in the authentication reference; the API client that issued the token determines which scopes it carries.

When access control hides entries from a collection response (most commonly unpublished entries), the response stays 200 and signals the omission instead of failing (an example is in the response document reference):

  • meta.count is the collection total including entries the request cannot see, so data can be shorter than meta.count, or empty.
  • A meta.omitted block appears, with "detail": "Some resources have been omitted because of insufficient authorization.". Each omitted entry is a link under meta.omitted.links whose own meta.detail names the reason. For unpublished entries: "The current user is not allowed to GET the selected resource. The 'view any unpublished content' permission is required."
  • Pagination links are computed before access filtering: a page can be "data": [] and still carry links.next/links.last. An empty page is not the end of a collection; follow links.next or check meta.omitted.

Used in: querying guide, writing content guide.

The status codes below mean the same thing on every endpoint. Auth-specific codes (401/403) are detailed in the authentication reference and diagnosed step-by-step in the auth guide’s troubleshooting.

Status Meaning Typical cause Where to look
400 Bad Request The query itself is malformed Bad filter syntax, unknown field path in filter/sort/include, invalid page value Query parameter reference → 400 responses
401 Unauthorized No valid token Missing/malformed Authorization header, expired token, wrong client ID or secret at /oauth/token Authentication reference, auth guide troubleshooting
403 Forbidden Token is valid but not allowed to do this The API client the token came from doesn’t grant the needed scope: content writes need content:administer (full scope list in the authentication reference) Authentication reference, writing content guide
404 Not Found The resource doesn’t exist Wrong entity type or bundle machine name in the path, or a UUID that doesn’t exist; check your site’s OpenAPI documentation for the real endpoint list Content model reference
405 Method Not Allowed Write sent while the site allows only reads The Allowed operations setting at API > JSON:API is Read-only (the default); the response’s detail names the settings page Writing content guide
429 Too Many Requests Rate limited Too many requests in a short window. Acquia does not publish Content API rate limits; if you receive a 429, back off and honor a Retry-After header when present None

Was this page helpful?