Pagination
Every list returns one page at a time with the newest first. Each page carries a cursor for the next one. Read one page, loop through them all, or let the SDKs do it.
Every list in the API answers one page at a time, newest first. Ask for up to 100 items with limit, and pass the page’s next_cursor as cursor to get the next one. These lists work this way:
| List | Ordered by |
|---|---|
GET /v1/messages | When each text was made or copied |
GET /v1/chats | When BlueBot first saw each chat |
GET /v1/stops | When each person was stopped |
GET /v1/events | When each event happened |
GET /v1/webhooks | When each webhook was made |
GET /v1/webhooks/{id}/deliveries | When each delivery was made |
One page
/v1/messagescurl "https://usebluebot.com/api/v1/messages?direction=in&limit=100" \
-H "Authorization: Bearer $BLUEBOT_API_KEY"{
"object": "list",
"data": [
{
"id": "msg_3Lp3VsC7hGqE5tZyKbWn2d",
"object": "message",
"direction": "in",
"state": "received",
"from": "+15555550123",
"text": "Perfect, see you there at 3"
}
],
"has_more": true,
"next_cursor": "WyIyMDI2LTExLTEzVDE5OjQ0OjEwLjAwMFoiLCJiMmI4ZjE0Yi0zZTM4LTRmNTQtODZiYi0wOWE2OGYwYzE2ZjEiXQ"
}| Field | What it tells you |
|---|---|
data | This page’s items, newest first. |
has_more | true when another page follows. |
next_cursor | Send this as cursor for the next page. null on the last page. |
| Parameter | What it does |
|---|---|
limit | How many items, 1 to 100. Default 25. |
cursor | Where to start: the next_cursor of the page before. Leave it out for the first page. |
The next page
Send the same request with cursor added:
/v1/messagescurl "https://usebluebot.com/api/v1/messages?direction=in&limit=100&cursor=WyIyMDI2LTExLTEzVDE5OjQ0OjEwLjAwMFoiLCJiMmI4ZjE0Yi0zZTM4LTRmNTQtODZiYi0wOWE2OGYwYzE2ZjEiXQ" \
-H "Authorization: Bearer $BLUEBOT_API_KEY"Keep every other parameter the same from page to page. A cursor says where the last page ended, not what you were looking for, so changing direction or person partway gives you a page of a different list.
A cursor is a position, not a snapshot. Lists page on each item’s time and id, so a text that arrives while you page never shifts an item onto a second page or makes you miss one. New items appear at the front of the list, which you’ve already read; to catch them, start again from the first page, or use since on lists that take it.
Treat a cursor as an opaque string: store it and send it back as it is. A cursor BlueBot didn’t make, or one cut short, is 400 invalid_request.
Every page
Loop until has_more is false:
import BlueBot from '@usebluebot/sdk';
const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });
let cursor: string | undefined;
do {
const page = await client.messages.list({ direction: 'in', limit: 100, cursor });
for (const message of page.data) {
console.log(message.receivedAt, message.from, message.text);
}
cursor = page.hasMore ? (page.nextCursor ?? undefined) : undefined;
} while (cursor);import os
from bluebot import BlueBot
client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])
cursor = None
while True:
page = client.messages.list(direction="in", limit=100, cursor=cursor)
for message in page.data:
print(message.received_at, message.from_, message.text)
if not page.has_more:
break
cursor = page.next_cursorLet the SDK page for you
A list call in the SDKs returns its first page, and iterating over the result walks every page after it, asking for the next one only when you get there:
for await (const message of client.messages.list({ direction: 'in', limit: 100 })) {
console.log(message.receivedAt, message.from, message.text);
}for message in client.messages.list(direction="in", limit=100):
print(message.received_at, message.from_, message.text)With the async Python client, use async for message in client.messages.list(...). Break out of the loop when you have what you need; no more pages are fetched after that.
Big reads
- Ask for 100 at a time.
limit=100reads in one request what the default of 25 takes four to read. Every page is a request against your 120 a minute. - Narrow the list first.
GET /v1/messagestakessince,until,person,contact_id,chat_id,state,directionandsource. A narrow list has fewer pages. - Keep your place. For a sync that runs every few minutes, keep the newest
created_atyou’ve stored and ask withsincenext time, rather than paging everything again.sinceincludes that moment, so skip anyidyou already have. - Catch up on missed webhooks with
GET /v1/events?since=…, which keeps 30 days of events.