Documentation
Sign inGet started

Pagination

Every paginated list endpoint in the Bird API uses the same cursor-based contract: the same request parameters, the same response envelope, the same cursor semantics. Learn it once on GET /v1/email/messages and it applies everywhere.
A small number of bounded collections (for example, billing plans) return a plain {"data": [...]} array with no pagination fields at all — an endpoint either implements this contract in full or not at all, never partially.

Request parameters

ParameterTypeDescription
limitintegerMaximum items per page. Between 1 and 100; defaults to 25.
starting_afterstringCursor from the next_cursor field of a previous response. Returns items immediately after that position.
ending_beforestringCursor from the prev_cursor field of a previous response. Returns items immediately before that position.
include_totalbooleanWhen true, the response includes a total count. Defaults to false. Available on management endpoints only — high-volume data endpoints (messages, events, suppressions) do not accept it.
Cursors are opaque: they are not resource IDs, they encode the sort position internally, and their format can change at any time. Receive them in responses and pass them back unchanged. A malformed or expired cursor returns a 422 with code E01012 InvalidCursor — restart pagination without a cursor.
Most list endpoints also accept resource-specific sort and order parameters; the per-endpoint reference documents the allowed sort fields. Changing the sort invalidates cursors from the previous sort order.

Response envelope

Code example
{
  "data": [{ "...": "..." }],
  "next_cursor": "WyIyMDI2LTA2LTEwVDA5OjE0OjAzWiIsICJtc2dfMDFr...",
  "prev_cursor": null,
  "refresh_cursor": "WyIyMDI2LTA2LTEwVDEyOjAwOjAwWiIsICJtc2dfMDFr...",
  "total": 1432
}
FieldDescription
dataThe page of items.
next_cursorPass back as starting_after to fetch the next page. null when no next page exists — this is the loop-termination signal.
prev_cursorPass back as ending_before to step backward. null when no previous page exists (always null on the first page).
refresh_cursorA refresh anchor: save it, then pass it back as ending_before later to fetch items that have appeared since this response. Non-null whenever data is non-empty.
totalTotal items matching the request's filters across all pages. Present only when include_total=true was passed; otherwise null/absent.
next_cursor and prev_cursor are independent — each is null exactly when its direction has no further page. There is no separate has_more flag; check next_cursor for null.

Walking all pages with curl

Code example
CURSOR=""
while :; do
  RESP=$(curl -s "https://us1.platform.bird.com/v1/email/messages?limit=100${CURSOR:+&starting_after=$CURSOR}" \
    -H "Authorization: Bearer bk_us1_...")
  echo "$RESP" | jq -r '.data[].id'
  CURSOR=$(echo "$RESP" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done
The first request carries no cursor; every subsequent request passes the previous response's next_cursor as starting_after; the loop ends when next_cursor is null.

Pagination in the SDKs

You rarely write that loop by hand. Every Bird SDK exposes list endpoints in two modes: lazy iteration that fetches pages transparently as you consume items, and a single-page accessor for manual cursor control.
Code example
for await (const message of bird.email.list({ status: "bounced" })) {
  console.log(message.id);
}
Code example
for msg, err := range client.Email.List(context.Background(), bird.EmailListParams{Status: bird.EmailStatusBounced}) {
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(msg.Id)
}
Code example
for message in client.email.list(status="delivered"):
    print(message.id)

Rate limits

List endpoints share the list rate-limit group, separate from reads of individual resources and from writes — paginating through a large collection never consumes your send or management quota. The lazy iterators above issue one request per page, so a tight loop over a large collection draws down the list group at one request per limit items; use limit=100 for bulk reads.