Query Parameters
Field Selection
The fields parameter allows you to request only specific content fields, reducing payload size and improving performance.
Syntax:
?fields=field1,field2,field3 # include only these fields
?fields=-field1,-field2 # include everything except these fields
A - prefix excludes that field. For example, ?fields=-body returns every field except body. Include and exclude entries are parsed from the same comma-separated list; if any plain (include) field is present, the include list takes precedence and exclusions are ignored — so combine one style or the other, not both.
Behavior:
- Always includes:
id,name,slug,type,seo,publishedAt,updatedAt - Filters only the
fieldsobject - Unknown field names are silently ignored
- Empty
fieldsparameter returns all fields -prefix excludes a field (e.g.?fields=-body)
Example: everything except the body field
GET /api/v1/pages/hello-world?fields=-body
Example: Full fields (default)
GET /api/v1/pages/hello-world
Response fields:
{
"fields": {
"title": "Hello World",
"excerpt": "My first post",
"body": "Lorem ipsum dolor sit amet...",
"author": "John Doe",
"tags": ["tech", "blog"],
"publishDate": "2025-12-15"
}
}
Example: Title and excerpt only
GET /api/v1/pages/hello-world?fields=title,excerpt
Response fields:
{
"fields": {
"title": "Hello World",
"excerpt": "My first post"
}
}
Performance Impact:
| Scenario | Payload Size | Performance Gain |
|---|---|---|
| Full blog post (all fields) | ~15 KB | Baseline |
| Title + excerpt only | ~2 KB | 86% smaller |
| Title only | ~1 KB | 93% smaller |
Use Cases:
- Card views: Request
title,excerpt,imageonly - Navigation menus: Request
title,slugonly - Search results: Request
title,excerpt,publishDateonly - Full page: Request all fields (omit parameter)
Media Serialization
The media parameter controls how media references (images, files) are serialized in the response.
Syntax:
?media=object # default — full media object
?media=url # just the media URL as a string
| Mode | A media field looks like | When to use |
|---|---|---|
object (default) | { "_url": "...", "_mimeType": "image/webp", "_width": 112, "_height": 128, "_altText": "..." } | You need dimensions (responsive srcset, layout-shift avoidance) or alt text (accessibility). |
url | "https://tento-api.intelligentlending.co.uk/api/v1/media/{project}/{file}" | You only need <img src>. Lighter payload. |
Behavior:
- Applies uniformly to media anywhere in the response — pages, collections, and blog
featuredImage(across single-item and list endpoints). objectmode omits_width/_height/_altTextwhen the media has no such value (they are absent, notnull).- Any value other than
urlis treated as the defaultobject. - Each mode is cached separately at the edge, so both stay fast.
Example:
GET /api/v1/pages/hello-world?media=url
{ "fields": { "hero": { "icon": "https://tento-api.intelligentlending.co.uk/api/v1/media/proj-1/abc.webp" } } }
Pagination
The page and limit parameters enable pagination through large result sets. Pagination is page-number based (1-indexed) — there is no offset parameter.
Syntax:
?page={page-number}&limit={page-size}
Parameters:
| Parameter | Type | Default | Min | Max | Description |
|---|---|---|---|---|---|
page | number | 1 | 1 | ∞ | Page number (1-indexed) |
limit | number | 20 | 1 | 100 | Number of results per page |
Response:
{
"data": [...],
"pagination": {
"total": 145,
"page": 3,
"limit": 20,
"totalPages": 8
}
}
Pagination Fields:
| Field | Type | Description |
|---|---|---|
total | number | Total number of results (across all pages) |
page | number | Current page (1-indexed) |
limit | number | Current page size |
totalPages | number | Total number of pages (ceil(total / limit)) |
Example: Fetch pages 1-3
# Page 1 (items 1-20)
GET /api/v1/pages?page=1&limit=20
# Page 2 (items 21-40)
GET /api/v1/pages?page=2&limit=20
# Page 3 (items 41-60)
GET /api/v1/pages?page=3&limit=20
Example: Client-side pagination logic
async function fetchPage(pageNumber, pageSize = 20) {
const response = await fetch(
`https://tento-api.intelligentlending.co.uk/api/v1/pages?page=${pageNumber}&limit=${pageSize}`,
{ headers: { 'X-API-Key': process.env.CMS_API_KEY } }
)
const data = await response.json()
return {
items: data.data,
total: data.pagination.total,
currentPage: data.pagination.page,
totalPages: data.pagination.totalPages,
hasNextPage: data.pagination.page < data.pagination.totalPages,
hasPreviousPage: data.pagination.page > 1
}
}
// Usage
const page1 = await fetchPage(1)
const page2 = await fetchPage(2)
Best Practices:
- Use
page < totalPagesto determine if a next page exists - Keep
limitsmall for list views (20-50) - Use
limit=100only for bulk operations - Cache paginated results client-side to reduce API calls
Sorting
The sort parameter controls the order of results.
Syntax:
?sort={field} # Ascending
?sort=-{field} # Descending (prefix with -)
Supported Fields (pages):
| Field | Description | Example Values |
|---|---|---|
publishedAt | Publication timestamp | 2025-12-15T10:00:00Z |
updatedAt | Last update timestamp | 2025-12-15T14:30:00Z |
createdAt | Creation timestamp | 2025-12-10T08:00:00Z |
name | Page name (alphabetical) | "About Us", "Contact" |
slug | Page slug (alphabetical) | "about-us", "contact" |
Collections support the same fields plus _sortOrder for authored order. Blog supports createdAt, updatedAt, publishedAt, title, slug.
Example: Most recent first (default)
GET /api/v1/pages?sort=-publishedAt
Example: Oldest first
GET /api/v1/pages?sort=publishedAt
Example: Alphabetical by name
GET /api/v1/pages?sort=name
Example: Reverse alphabetical
GET /api/v1/pages?sort=-name
Example: Multi-field sort (primary then secondary)
GET /api/v1/pages?sort=-publishedAt,name
Combining with Other Parameters:
# Blog posts, newest first, 10 per page, title only
GET /api/v1/pages?pageType=blog-post&sort=-publishedAt&limit=10&fields=title
Filtering
Filtering via filter[field][operator]=value is collections-only — GET /api/v1/collections/:type supports it; the pages and blog list endpoints do not accept filter[...] params at all.
Syntax:
?filter[field][operator]=value
field is either a core field (name, slug) or a custom content field prefixed with fields. (e.g. fields.price, or fields.meta.author for a nested JSON path). Omitting [operator] defaults to eq.
Operators:
| Operator | Description | Example |
|---|---|---|
eq | Equals (the default when [operator] is omitted) | filter[fields.category][eq]=tech |
ne | Not equal | filter[fields.status][ne]=draft |
gt | Greater than (value cast to a number before comparing) | filter[fields.price][gt]=50 |
lt | Less than (value cast to a number before comparing) | filter[fields.price][lt]=100 |
gte | Greater than or equal (value cast to a number before comparing) | filter[fields.price][gte]=50 |
lte | Less than or equal (value cast to a number before comparing) | filter[fields.price][lte]=100 |
contains | Substring match (LIKE '%value%'); %, _, and \ in the value are escaped so they match literally rather than acting as SQL wildcards | filter[fields.title][contains]=guide |
in | Value is one of a comma-separated list | filter[fields.category][in]=tech,news,tutorial |
Example: numeric range + equality
GET /api/v1/collections/products?filter[fields.price][gte]=10&filter[fields.price][lte]=100&filter[fields.category][eq]=electronics
Example: in operator
GET /api/v1/collections/products?filter[fields.category][in]=electronics,home,garden
Limits & restrictions:
- Maximum 20 filter conditions per request; any beyond that are silently dropped
- Core (non-
fields.) filterable fields are limited to a small whitelist (name,slugtoday) — internal columns (ownership, audit fields, secrets) are never filterable through this API, even if you guess the right underlying column name fields.JSON paths accept only alphanumerics, underscores, and dots, with no leading/trailing/consecutive dots — a malformed path returnsINVALID_JSON_FIELD_PATH(see Error Codes)- An unrecognised core filter field returns
INVALID_FILTER_FIELD(see Error Codes)

