Troubleshooting
If requests fail, run zero doctor check-connector --env-name STRAPI_TOKEN or zero doctor check-connector --url https://docs.strapi.io/cms/api/rest --method GET
IMPORTANT: Discovery Workflow
Before performing any content operations, always discover the user's content types first. Every Strapi instance has different content types, fields, and relationships. Never assume what types exist.
Step 1: Discover Content Types
curl -s "$STRAPI_BASE_URL/api/content-type-builder/content-types" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq '[.data[] | select(.schema.visible != false) | {uid: .uid, name: .schema.displayName, kind: .schema.kind, attributes: [.schema.attributes | to_entries[] | {name: .key, type: .value.type, required: .value.required}]}]'
This returns all content types with their fields. Key properties:
uid: The unique identifier (e.g.,api::article.article)kind: EithercollectionType(multiple entries) orsingleType(one entry)attributes: Fields with their types (string,richtext,media,relation,enumeration, etc.)
Step 2: Identify the API ID
The API endpoint path is derived from the content type's plural name. For api::article.article, the endpoint is /api/articles. Common pattern:
- Collection type
article→GET /api/articles - Single type
homepage→GET /api/homepage
If unsure, check the content type info from Step 1 — the uid follows the pattern api::{singularName}.{singularName}.
Step 3: Proceed with Operations
Once you know the content types and their fields, proceed with the appropriate CRUD operations below.
Collection Types (Multiple Entries)
List Entries
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
List with Pagination, Sort, and Filter
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID?pagination[page]=1&pagination[pageSize]=25&sort=createdAt:desc&filters[fieldName][\$eq]=value" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Get Single Entry
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Get Entry with Relations Populated
Use populate to include related data:
# Populate all first-level relations
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID?populate=*" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
# Populate specific relations
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID?populate[0]=category&populate[1]=author" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
# Deep populate nested relations
curl -s "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID?populate[author][populate]=avatar" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Create Entry
Write to /tmp/strapi_request.json:
{
"data": {
"title": "My New Entry",
"description": "Entry content here",
"category": 1
}
}
Then run:
curl -s -X POST "$STRAPI_BASE_URL/api/PLURAL_API_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" \
--header "Content-Type: application/json" \
-d @/tmp/strapi_request.json | jq .
Update Entry
Write to /tmp/strapi_request.json:
{
"data": {
"title": "Updated Title"
}
}
Then run:
curl -s -X PUT "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" \
--header "Content-Type: application/json" \
-d @/tmp/strapi_request.json | jq .
Delete Entry
curl -s -X DELETE "$STRAPI_BASE_URL/api/PLURAL_API_ID/DOCUMENT_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Single Types (One Entry)
Get Single Type
curl -s "$STRAPI_BASE_URL/api/SINGULAR_API_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Update Single Type
Write to /tmp/strapi_request.json:
{
"data": {
"title": "Updated Homepage Title",
"seo": {
"metaTitle": "Home",
"metaDescription": "Welcome"
}
}
}
Then run:
curl -s -X PUT "$STRAPI_BASE_URL/api/SINGULAR_API_ID" \
--header "Authorization: Bearer $STRAPI_TOKEN" \
--header "Content-Type: application/json" \
-d @/tmp/strapi_request.json | jq .
Media Upload
Upload a File
curl -s -X POST "$STRAPI_BASE_URL/api/upload" \
--header "Authorization: Bearer $STRAPI_TOKEN" \
-F "files=@/path/to/file.jpg" | jq '.[0] | {id, name, url, mime}'
Upload and Attach to Entry
curl -s -X POST "$STRAPI_BASE_URL/api/upload" \
--header "Authorization: Bearer $STRAPI_TOKEN" \
-F "files=@/path/to/image.jpg" \
-F "ref=api::article.article" \
-F "refId=DOCUMENT_ID" \
-F "field=cover" | jq .
Filtering Reference
Strapi uses operator-based filtering:
| Operator | Description | Example |
|---|---|---|
$eq |
Equal | filters[title][$eq]=Hello |
$ne |
Not equal | filters[title][$ne]=Hello |
$lt |
Less than | filters[price][$lt]=100 |
$lte |
Less than or equal | filters[price][$lte]=100 |
$gt |
Greater than | filters[price][$gt]=50 |
$gte |
Greater than or equal | filters[price][$gte]=50 |
$in |
In array | filters[id][$in][0]=1&filters[id][$in][1]=2 |
$contains |
Contains (case-sensitive) | filters[title][$contains]=word |
$containsi |
Contains (case-insensitive) | filters[title][$containsi]=word |
$startsWith |
Starts with | filters[title][$startsWith]=He |
$null |
Is null | filters[image][$null]=true |
$notNull |
Is not null | filters[image][$notNull]=true |
Combining Filters
# AND: multiple filters on different fields
curl -s "$STRAPI_BASE_URL/api/articles?filters[status][\$eq]=published&filters[category][\$eq]=news" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
# OR: use $or operator
curl -s "$STRAPI_BASE_URL/api/articles?filters[\$or][0][status][\$eq]=published&filters[\$or][1][status][\$eq]=draft" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Pagination
# Page-based (default)
curl -s "$STRAPI_BASE_URL/api/articles?pagination[page]=1&pagination[pageSize]=25" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq '.meta.pagination'
# Offset-based
curl -s "$STRAPI_BASE_URL/api/articles?pagination[start]=0&pagination[limit]=25" \
--header "Authorization: Bearer $STRAPI_TOKEN" | jq .
Response includes pagination metadata:
{
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 4,
"total": 100
}
}
}
Guidelines
- Always discover first: Run the content type discovery endpoint before performing operations — never guess content type names or field names
- Wrap data in
datakey: All create and update payloads must wrap the fields in adataobject - Use
populatefor relations: By default, relations are not included in responses. Usepopulate=*for all first-level relations, or specify specific fields - Check response format: Successful responses return
{ data: { id, attributes, ... }, meta: { ... } } - Error format: Errors return
{ error: { status, name, message, details } } - API token scope: The operations available depend on the token type (Full Access, Read-Only, or Custom permissions per content type)
- Document IDs: Strapi v5 uses document IDs (not numeric IDs) for CRUD operations
- Sort syntax: Use
sort=field:ascorsort=field:desc. Multiple sorts:sort[0]=field1:asc&sort[1]=field2:desc
API Reference
- REST API:
https://docs.strapi.io/cms/api/rest - Filtering:
https://docs.strapi.io/cms/api/rest/filters-locale-publication - Population:
https://docs.strapi.io/cms/api/rest/populate-select - API Tokens:
https://docs.strapi.io/cms/features/api-tokens