Pagination
All list endpoints in the Typograph API use offset-based pagination with a consistent response format.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 10-20 | Number of items per page |
offset | integer | 0 | Number of items to skip |
Limit Constraints
- Minimum: 1
- Maximum: 50 (per endpoint)
- 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
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
data | array | Array of resource objects |
meta.total | integer | Total number of items available |
meta.limit | integer | Number of items requested per page |
meta.offset | integer | Number of items skipped |
meta.has_more | boolean | Whether 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; // Maximum allowed
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; // Maximum allowed
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 Case | Recommended Limit |
|---|---|
| Initial page load | 10-20 |
| Infinite scroll | 20-30 |
| Data export/sync | 50 (maximum) |
| Search results | 10-20 |
Handle Edge Cases
- Empty Results: When
totalis 0,datawill be an empty array - Invalid Offset: If offset exceeds total, returns empty
datawithhas_more: false - Concurrent Updates: Items may shift between pages if data changes during pagination
Efficient Pagination
- Cache Total Count: The
totalvalue can help display page numbers or progress indicators - Use Maximum Limit for Exports: When syncing all data, use
limit=50to minimize requests - Check
has_more: More reliable than calculating from offset + length vs total
Endpoints Supporting Pagination
| Endpoint | Default Limit | Max Limit |
|---|---|---|
GET /v1/file/projects | 10 | 50 |
GET /v1/file/templates | 10 | 50 |
GET /v1/file/projects/{projectId}/uploads | 10 | 50 |
GET /v1/file/teams | 10 | 50 |
GET /v1/publisher/jobs | 10 | 50 |
GET /v1/converter/jobs | 10 | 50 |
GET /v1/webhook/subscriptions | 20 | 50 |
GET /v1/webhook/subscriptions/{id}/deliveries | 20 | 50 |
GET /v1/identity/oauth/clients | 10 | 50 |
Related
- API Overview - General API concepts
- Rate Limiting - Request limits and throttling