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

ParameterTypeDefaultDescription
countnumber100Number of items to return (max 500)
offsetnumber0Number of items to skip

Some endpoints also support:

ParameterTypeDescription
pagenumberPage number (alternative to offset)
limitnumberAlias for count

Response Format

Paginated responses include metadata about the result set:

{
  "data": [...],
  "meta": {
    "count": 100,
    "offset": 0,
    "total": 1543,
    "hasMore": true
  }
}
FieldTypeDescription
countnumberNumber of items in this response
offsetnumberCurrent offset position
totalnumberTotal items available
hasMorebooleanWhether 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

EndpointDefault CountMax Count
GET /emails/messages100500
GET /emails/templates100500
GET /emails/bounces100500
GET /emails/suppressions100500
GET /emails/stats100500

SMS Module

EndpointDefault CountMax Count
GET /sms/messages100500
GET /sms/inbound100500
GET /sms/templates100500
GET /sms/suppressions100500

Forms Module

EndpointDefault CountMax Count
GET /forms20100
GET /forms/:id/submissions20100

Support Module

EndpointDefault CountMax Count
GET /support/conversations50100
GET /support/visitors50100
GET /support/companies50100

Auth Module

EndpointDefault CountMax Count
GET /auth/users50100
GET /auth/logs50500
GET /auth/organizations50100

Calendar Module

EndpointDefault CountMax Count
GET /calendar/bookings50100
GET /calendar/event-types50100

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