Skip to main content

Pagination

All list endpoints in the Typograph API use offset-based pagination with a consistent response format.

Query Parameters​

ParameterTypeDefaultDescription
limitinteger10-20Number of items per page
offsetinteger0Number of items to skip

Limit Constraints​

  • Minimum: 1
  • Maximum: 50 for most endpoints; the few that take 100 are listed below
  • Default: Varies by endpoint (typically 10 or 20)

Exceeding the maximum limit returns a 400 Bad Request error:

{
"error": "invalid_request",
"error_description": "The limit can't be greater than 50."
}

Response Format​

All paginated endpoints return a standardized response structure:

{
"data": [
{ "id": "...", "name": "..." },
{ "id": "...", "name": "..." }
],
"meta": {
"total": 150,
"limit": 20,
"offset": 0,
"has_more": true
}
}

GET /v1/file/notifications is the exception: it takes limit and offset the same way, but answers { "results": [...], "total": 12 } without meta. A next page exists while offset + limit is below total.

Response Fields​

FieldTypeDescription
dataarrayArray of resource objects
meta.totalintegerTotal number of items available
meta.limitintegerNumber of items requested per page
meta.offsetintegerNumber of items skipped
meta.has_morebooleanWhether more items exist beyond this page

Usage Examples​

First Page​

curl "https://api.typograph.nl/v1/file/projects?limit=20&offset=0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
"data": [...],
"meta": {
"total": 150,
"limit": 20,
"offset": 0,
"has_more": true
}
}

Next Page​

curl "https://api.typograph.nl/v1/file/projects?limit=20&offset=20" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
"data": [...],
"meta": {
"total": 150,
"limit": 20,
"offset": 20,
"has_more": true
}
}

Last Page​

When you reach the last page, has_more will be false:

{
"data": [...],
"meta": {
"total": 150,
"limit": 20,
"offset": 140,
"has_more": false
}
}

Iterating Through All Results​

JavaScript/TypeScript​

async function getAllItems(endpoint: string, token: string) {
const items = [];
let offset = 0;
const limit = 50; // The maximum of most endpoints

while (true) {
const response = await fetch(
`${endpoint}?limit=${limit}&offset=${offset}`,
{ headers: { Authorization: `Bearer ${token}` } }
);
const { data, meta } = await response.json();

items.push(...data);

if (!meta.has_more) break;
offset += limit;
}

return items;
}

PHP​

function getAllItems(string $endpoint, string $token): array
{
$items = [];
$offset = 0;
$limit = 50; // The maximum of most endpoints

do {
$response = $client->request('GET', "{$endpoint}?limit={$limit}&offset={$offset}", [
'headers' => ['Authorization' => "Bearer {$token}"],
]);

$result = json_decode($response->getContent(), true);
$items = array_merge($items, $result['data']);
$hasMore = $result['meta']['has_more'];
$offset += $limit;
} while ($hasMore);

return $items;
}

Best Practices​

Choose an Appropriate Page Size​

Use CaseRecommended Limit
Initial page load10-20
Infinite scroll20-30
Data export/syncThe endpoint's maximum: 50, or 100 where the table below says so
Search results10-20

Handle Edge Cases​

  1. Empty Results: When total is 0, data will be an empty array
  2. Invalid Offset: If offset exceeds total, returns empty data with has_more: false
  3. Concurrent Updates: Items may shift between pages if data changes during pagination

Efficient Pagination​

  • Cache Total Count: The total value can help display page numbers or progress indicators
  • Use Maximum Limit for Exports: When syncing all data, use the endpoint's maximum limit (50, or 100 for the endpoints the table below lists) to minimize requests
  • Check has_more: More reliable than calculating from offset + length vs total

Endpoints Supporting Pagination​

EndpointDefault LimitMax Limit
GET /v1/file/projects1050
GET /v1/file/templates1050
GET /v1/file/projects/{projectId}/uploads1050
GET /v1/file/teams1050
GET /v1/file/notifications1050
GET /v1/publisher/jobs1050
GET /v1/converter/jobs1050
GET /v1/webhook/subscriptions2050
GET /v1/webhook/subscriptions/{id}/deliveries2050
GET /v1/identity/oauth/clients1050
GET /v1/identity/organizations/{organizationId}/members50100
GET /v1/identity/organizations/{organizationId}/invitations50100
GET /v1/identity/users/me/invitations50100
GET /v1/file/premade-templates50100