Pagination
How to paginate through large result sets.
Overview
List endpoints return paginated results to handle large datasets efficiently. The Transactional API uses offset-based pagination with consistent parameters across all modules.
Pagination Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
count | number | 100 | Number of items to return (max 500) |
offset | number | 0 | Number of items to skip |
Some endpoints also support:
| Parameter | Type | Description |
|---|---|---|
page | number | Page number (alternative to offset) |
limit | number | Alias for count |
Response Format
Paginated responses include metadata about the result set:
{
"data": [...],
"meta": {
"count": 100,
"offset": 0,
"total": 1543,
"hasMore": true
}
}| Field | Type | Description |
|---|---|---|
count | number | Number of items in this response |
offset | number | Current offset position |
total | number | Total items available |
hasMore | boolean | Whether more pages exist |
Basic Pagination
First Page
curl "https://api.usetransactional.com/v1/emails/messages?count=50" \
-H "Authorization: Bearer YOUR_API_KEY"Subsequent Pages
# Page 2 (items 51-100)
curl "https://api.usetransactional.com/v1/emails/messages?count=50&offset=50" \
-H "Authorization: Bearer YOUR_API_KEY"
# Page 3 (items 101-150)
curl "https://api.usetransactional.com/v1/emails/messages?count=50&offset=100" \
-H "Authorization: Bearer YOUR_API_KEY"SDK Examples
TypeScript SDK
import { Transactional } from 'transactional-sdk';
const client = new Transactional({ apiKey: 'your-api-key' });
// Fetch a single page
const page1 = await client.emails.messages.list({
count: 50,
});
console.log(`Retrieved ${page1.data.length} of ${page1.meta.total}`);
// Fetch all pages
async function fetchAllMessages() {
const allMessages = [];
let offset = 0;
const count = 100;
while (true) {
const response = await client.emails.messages.list({
count,
offset,
});
allMessages.push(...response.data);
if (!response.meta.hasMore) {
break;
}
offset += count;
}
return allMessages;
}
// Using async iterator (if supported)
async function* iterateMessages() {
let offset = 0;
const count = 100;
while (true) {
const response = await client.emails.messages.list({
count,
offset,
});
for (const message of response.data) {
yield message;
}
if (!response.meta.hasMore) {
break;
}
offset += count;
}
}
// Use the iterator
for await (const message of iterateMessages()) {
console.log(message.id);
}Python SDK
from transactional import Transactional
client = Transactional(api_key="your-api-key")
# Fetch a single page
page1 = client.emails.messages.list(count=50)
print(f"Retrieved {len(page1.data)} of {page1.meta.total}")
# Fetch all pages
def fetch_all_messages():
all_messages = []
offset = 0
count = 100
while True:
response = client.emails.messages.list(
count=count,
offset=offset,
)
all_messages.extend(response.data)
if not response.meta.has_more:
break
offset += count
return all_messages
# Using a generator
def iterate_messages():
offset = 0
count = 100
while True:
response = client.emails.messages.list(
count=count,
offset=offset,
)
for message in response.data:
yield message
if not response.meta.has_more:
break
offset += count
# Use the generator
for message in iterate_messages():
print(message.id)Pagination with Filters
Combine pagination with filters to narrow results:
curl "https://api.usetransactional.com/v1/emails/messages?count=50&status=DELIVERED&fromDate=2024-01-01" \
-H "Authorization: Bearer YOUR_API_KEY"TypeScript
const delivered = await client.emails.messages.list({
count: 50,
status: 'DELIVERED',
fromDate: '2024-01-01T00:00:00Z',
toDate: '2024-01-31T23:59:59Z',
});Python
delivered = client.emails.messages.list(
count=50,
status="DELIVERED",
from_date="2024-01-01T00:00:00Z",
to_date="2024-01-31T23:59:59Z",
)Best Practices
1. Use Reasonable Page Sizes
- Default of 100 is good for most cases
- Use smaller sizes (20-50) for UI pagination
- Use larger sizes (500) for bulk operations
// UI pagination
const uiPage = await client.emails.messages.list({ count: 20 });
// Bulk export
const bulkPage = await client.emails.messages.list({ count: 500 });2. Handle Empty Results
const response = await client.emails.messages.list({ count: 50 });
if (response.data.length === 0) {
console.log('No messages found');
return;
}3. Check for More Pages
Always use hasMore instead of comparing counts:
// Good
if (response.meta.hasMore) {
// Fetch next page
}
// Avoid - fragile
if (response.data.length === 50) {
// Might miss the last page with exactly 50 items
}4. Add Delays for Large Exports
When paginating through large datasets, add delays to avoid rate limiting:
async function exportAll() {
let offset = 0;
while (true) {
const response = await client.emails.messages.list({
count: 500,
offset,
});
await processMessages(response.data);
if (!response.meta.hasMore) break;
offset += 500;
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
}Endpoints Supporting Pagination
Email Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /emails/messages | 100 | 500 |
GET /emails/templates | 100 | 500 |
GET /emails/bounces | 100 | 500 |
GET /emails/suppressions | 100 | 500 |
GET /emails/stats | 100 | 500 |
SMS Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /sms/messages | 100 | 500 |
GET /sms/inbound | 100 | 500 |
GET /sms/templates | 100 | 500 |
GET /sms/suppressions | 100 | 500 |
Forms Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /forms | 20 | 100 |
GET /forms/:id/submissions | 20 | 100 |
Support Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /support/conversations | 50 | 100 |
GET /support/visitors | 50 | 100 |
GET /support/companies | 50 | 100 |
Auth Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /auth/users | 50 | 100 |
GET /auth/logs | 50 | 500 |
GET /auth/organizations | 50 | 100 |
Calendar Module
| Endpoint | Default Count | Max Count |
|---|---|---|
GET /calendar/bookings | 50 | 100 |
GET /calendar/event-types | 50 | 100 |
Cursor-Based Pagination
Some endpoints support cursor-based pagination for real-time data:
{
"data": [...],
"meta": {
"cursor": "eyJpZCI6MTIzNH0=",
"hasMore": true
}
}Use the cursor for the next request:
curl "https://api.usetransactional.com/v1/webhooks/deliveries?cursor=eyJpZCI6MTIzNH0=" \
-H "Authorization: Bearer YOUR_API_KEY"Cursor pagination is available for:
- Webhook delivery logs
- Real-time event streams
- Audit logs