Contentful delivery
Contentful’s own paths, over the same content the Strapi surface reads. A site written against the CDA needs its base URL changed and nothing else.
GET /spaces/:space/environments/:env/entriesGET /spaces/:space/environments/:env/entries/:idGET /spaces/:space/environments/:env/content_typesThe environment-scoped spellings are served too, for clients configured with a base URL that already carries the environment:
GET /spaces/:space/entriesGET /spaces/:space/entries/:idGET /spaces/:space/content_typesSpaces and environments
Section titled “Spaces and environments”The :space segment selects a content space. An
unknown space is 404, and the message lists the spaces the tenant declares.
The :env segment is accepted and is not a selector. A Contentful environment is a copy of a
space’s content, which on this platform is a tenant, and the tenant is already established by
the request’s CEPT headers. Accepting the segment keeps every client URL working; treating it as a
second tenancy axis would let a token choose its own data.
Reading entries
Section titled “Reading entries”content_type is required
Section titled “content_type is required”GET /spaces/blog/environments/master/entries?content_type=postA type-less query is 400. There is no single table to read, and fanning out across every content
type would turn one request into one query per type. The refusal says so rather than answering with
an empty array.
A by-id fetch does not need one, because the id is a document identity:
GET /spaces/blog/environments/master/entries/01J9Y…The type is found by asking each of the space’s types for that document, which is what the CDA’s
own /entries/{id} does.
Query parameters
Section titled “Query parameters”| Parameter | Default | Notes |
|---|---|---|
content_type | required on /entries | Type name or plural |
locale | the row’s own | * pivots every locale into one entry |
limit | 100 | Clamped to 1000 |
skip | 0 | |
include | 1 | Link resolution depth. Above 10 is 400 |
order | (documentId, locale) | - prefixes a descending term |
select | every field | Comma-separated |
Filters
Section titled “Filters”Field equality and bracketed operators, in the CDA’s spelling:
?fields.slug=hello?fields.price[gte]=300?fields.slug[in]=hello,second?sys.id=01J9Y…| Operator | Meaning |
|---|---|
ne | not equal |
in nin | membership |
lt lte gt gte | ordered comparison |
exists | presence |
match | substring |
A field the type does not declare is 400.
Ordering
Section titled “Ordering”?order=fields.title?order=-sys.createdAt,fields.titleA leading minus is descending.
Selecting fields
Section titled “Selecting fields”?select=fields.title,fields.slugsys is always rendered, so naming it does not restrict the projection and is not an error. The
columns sys is assembled from are projected whether or not they were listed.
The entry envelope
Section titled “The entry envelope”Every entry is sys, which the platform owns, plus fields, which the author owns.
{ "sys": { "type": "Entry", "id": "01J9Y…", "space": { "sys": { "type": "Link", "linkType": "Space", "id": "blog" } }, "contentType": { "sys": { "type": "Link", "linkType": "ContentType", "id": "post" } }, "locale": "en", "createdAt": "2026-09-15T09:12:44Z", "updatedAt": "2026-09-15T09:14:02Z", "firstPublishedAt": "2026-09-15T09:14:02Z", "publishedAt": "2026-09-15T09:14:02Z", "revision": 1 }, "fields": { "title": "Hello world", "slug": "hello", "price": 100 }}sys.id is the document identity, and it is the same value as the Strapi surface’s
documentId. An entry resolved through a link by one client is findable by the other under the
same name.
A published entry carries revision, firstPublishedAt and publishedAt. A draft carries none of
them, which is how a client tells them apart without a status field.
The collection envelope
Section titled “The collection envelope”{ "sys": { "type": "Array" }, "total": 2, "skip": 0, "limit": 100, "items": [ /* entries */ ], "includes": { "Entry": [ /* linked entries, once each */ ] }}includes is present only when links were resolved.
Link resolution
Section titled “Link resolution”The CDA does not inline a linked entry. The field holds a Link, and the entries themselves
arrive once in a top-level includes.Entry[] however many fields point at them.
"fields": { "title": "Hello world", "author": { "sys": { "type": "Link", "linkType": "Entry", "id": "01J9A…" } }}That shape is why include is a depth rather than a list of names: the client walks links against
the includes block.
Four properties are worth knowing.
The block is flat at every depth. A post’s author and that author’s publisher are siblings in
includes.Entry[], joined by the Link the author carries. Nesting the publisher inside the author
would put it out of reach of a client that follows links by id.
An entry appears once. A page of twenty-five posts by the same author carries one author.
Resolution is batched across each level. Every parent sharing a relation at level n
contributes its keys to one query, so the cost is depth × distinct relations and never a function
of the number of rows. Twenty-five posts, three authors and two publishers is three queries.
A cycle terminates. A model where a page’s parent is a page resolves to exactly the depth asked for.
Each link is resolved through the target type’s own read rule, so include never becomes a way to
read a type the caller is not entitled to.
The locale pivot
Section titled “The locale pivot”locale=* collapses a document’s locale rows into one entry whose localized fields are maps of
locale to value, which is how Contentful stores content natively.
{ "sys": { "type": "Entry", "id": "01J9Y…" }, "fields": { "title": { "en": "Hello world", "de": "Hallo Welt" }, "price": 100 }}Fields marked localized: false are rendered as plain values, since they are the same across every
locale by construction. Wrapping them would tell a client they vary.
sys.locale is omitted on this view, because a pivoted entry is not in one locale.
The pivot gathers a document’s rows and is the call to reach for in management and migration
tooling. ?locale=en is the delivery path, and is a single-row read.
Preview
Section titled “Preview”The CDA models preview as a separate host with its own token. One instance serves both here, so the draft view is selected by any of:
| Signal | For |
|---|---|
X-Contentful-Preview: true | A client that can set a header |
A /preview/ path prefix | A client pointed at a preview base URL |
| A preview token | A front end with no editorial credential |
Drafts require permission to edit the type, or a token minted by somebody who has it.
Content type discovery
Section titled “Content type discovery”GET /spaces/blog/environments/master/content_types{ "sys": { "type": "Array" }, "total": 1, "skip": 0, "limit": 1, "items": [ { "sys": { "type": "ContentType", "id": "post", "space": { "sys": { "type": "Link", "linkType": "Space", "id": "blog" } } }, "name": "post", "displayField": "title", "fields": [ { "id": "title", "name": "title", "type": "Symbol", "required": true, "localized": true }, { "id": "body", "name": "body", "type": "Text", "required": false, "localized": false }, { "id": "price", "name": "price", "type": "Integer","required": false, "localized": false }, { "id": "hero", "name": "hero", "type": "Link", "required": false, "localized": false } ] } ]}Field types are mapped into Contentful’s vocabulary, which is what a client’s codegen reads:
| Declared | Rendered |
|---|---|
string | Symbol |
text | Text |
int, bigint | Integer |
float, decimal, money | Number |
bool | Boolean |
datetime, date, time | Date |
json | Object |
array | Array |
file | Link |
The CMS columns are not listed as fields, since they belong to sys.
Errors
Section titled “Errors”{ "sys": { "type": "Error", "id": "NotFound" }, "message": "The resource could not be found.", "requestId": ""}| Status | id | When |
|---|---|---|
400 | BadRequest | content_type missing, or a malformed query string |
400 | InvalidQuery | An unknown field, or include above the ceiling |
403 | AccessDenied | Well formed, and the caller is not entitled |
404 | NotFound | Unknown space, unknown type, or no such entry |
See also
Section titled “See also”- Strapi delivery: the same content, flat-shaped, with writes
- The content model: relations, locales and publication