Pagination

page and pageSize parameters

Every list route paginates the same way. You pass page, counting from 1, and pageSize, and the response carries an items array plus a meta object that says where you are and how much is left.

The routes that paginate are GET /v1/jobs, GET /v1/jobs/{id}/emails and GET /v1/credits/ledger. They share the same parameters and the same response shape, so one loop works for all three.

Loop until page reaches meta.totalPages. For a finished job, the CSV export is simpler than any loop. Use pages when you want to filter, or to read rows while the job is still running.

Shell
curl "https://app.zapbounce.com/api/v1/jobs/0b6f2d3e-6a52-4c58-9d0e-3f6a1c9a7e10/emails?status=unknown&page=2&pageSize=100" \
  -H "X-API-Key: $ZB_API_KEY"
JSON
{
  "items": [
    { "email": "grace@example.org", "status": "unknown", "reasonCode": "TEMPORARY_FAILURE" }
  ],
  "meta": { "page": 2, "pageSize": 100, "total": 260, "totalPages": 3 }
}

Fields

FieldTypeWhat it carries
pageintegerStarts at 1. Default 1.
pageSizeintegerRows per page, 1 to 100. Default 20.
itemsarrayThe rows on this page.
metaobjectpage, pageSize, total and totalPages. totalPages is 0 when there are no rows.

Streaming rows into your database while the job runs

Say a job of 250,000 addresses is running and you want rows in your own database as they land. Ask for pages with pageSize=100. At that size the full set takes about 2,500 requests, against 12,500 at the default of 20, and each one counts against your rate limit.

Write each page with an upsert keyed on the email address, so reading a page twice does no harm. After each page commits, save the page number. If your process dies, it resumes from the saved page and doesn't start over from the first row.

Catching up with a job that is still writing rows is normal, so pair the loop with the job status call. Treat the download as finished only when the status has left running and page has reached meta.totalPages. If you catch up early, wait a little before asking again. For a finished job, the CSV export is one request.

Four ways a results loop goes wrong

The first is paging a job that is still running. Rows are still being written, so a page you already read can shift. Wait for the status to settle when you need a stable read.

Next comes asking for pages bigger than the cap. A pageSize above 100 is rejected as a bad request, so ask for more pages with a size of 100 or less.

A quieter problem is filtering in the wrong place. The status parameter on /emails does the filtering for you, so use it to pull only the unknowns. It costs fewer requests than reading every row and keeping the ones you want on your side.

The last one is skipping the final count. When the loop ends, compare the number of rows you stored with totalEmails on the status call. A gap that repeats don't explain means a page went missing somewhere. It's far easier to find that today than after the job has been deleted.

Questions developers ask

What is the fastest way to get everything?

For a finished job, download the CSV. The page loop is for filtering and for reading results as they land.

Can I search within a list?

Yes. GET /v1/jobs and the ledger take q, and /emails takes both q and status.

How do I know I have reached the end?

When page equals meta.totalPages, or when items comes back empty.

Try it against a sandbox key

Scripted verdicts, no SMTP connections, no credits. Live keys come with 100 free checks a month and no card.