Pagination
All list endpoints use cursor-based pagination. This provides consistent results even when new records are added while you paginate.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Number of records per page (max 100) |
cursor | string | — | Cursor from next_cursor in previous response |
Response Format
{
"object": "list",
"data": [
{ "id": "job_01HV...", ... },
{ "id": "job_01HW...", ... }
],
"has_more": true,
"next_cursor": "job_01HW..."
}| Field | Description |
|---|---|
data | Array of records for this page |
has_more | true if more records exist beyond this page |
next_cursor | Pass as cursor parameter to fetch the next page. null when has_more is false. |
Example: Paginating All Jobs
# First page
curl "https://api.firsthandapi.com/v1/jobs?limit=10" \
-H "Authorization: Bearer fh_live_..."
# Next page (use next_cursor from previous response)
curl "https://api.firsthandapi.com/v1/jobs?limit=10&cursor=job_01HW..." \
-H "Authorization: Bearer fh_live_..."TypeScript Example
async function getAllJobs(client: FirstHandClient) {
const allJobs = [];
let cursor: string | undefined;
do {
const page = await client.listJobs({ limit: 100, cursor });
allJobs.push(...page.data);
cursor = page.has_more ? page.next_cursor : undefined;
} while (cursor);
return allJobs;
}Python Example
def get_all_jobs(client):
jobs = []
cursor = None
while True:
page = client.list_jobs(limit=100, cursor=cursor)
jobs.extend(page["data"])
if not page["has_more"]:
break
cursor = page["next_cursor"]
return jobsEndpoints That Support Pagination
| Endpoint | Sorted By |
|---|---|
GET /v1/jobs | Created at (newest first) |
GET /v1/jobs/:id/files | Created at (newest first) |
GET /v1/jobs/:id/submissions | Created at (newest first) |
GET /v1/webhook_endpoints | Created at (newest first) |
GET /v1/api_keys | Created at (newest first) |
GET /v1/billing/transactions | Created at (newest first) |
GET /v1/audit-events | Created at (newest first) |
Why Cursor-Based?
Cursor-based pagination is more reliable than offset-based (?page=2) because:
- No skipped or duplicated records when items are added during pagination
- Consistent performance — cursors use indexed lookups, not
OFFSETscans - Stable across concurrent writes — safe for real-time data
Filtering
Most list endpoints support filtering alongside pagination:
# List only completed jobs, 50 per page
curl "https://api.firsthandapi.com/v1/jobs?status=completed&limit=50" \
-H "Authorization: Bearer fh_live_..."Filters reduce the total result set before pagination is applied.