Skip to the page

API basics

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:

ListOrdered by
GET /v1/messagesWhen each text was made or copied
GET /v1/chatsWhen BlueBot first saw each chat
GET /v1/stopsWhen each person was stopped
GET /v1/eventsWhen each event happened
GET /v1/webhooksWhen each webhook was made
GET /v1/webhooks/{id}/deliveriesWhen each delivery was made

One page

GET/v1/messagesList texts →
curl "https://usebluebot.com/api/v1/messages?direction=in&limit=100" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
Response200A page with more after it
{
  "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"
}
FieldWhat it tells you
dataThis page’s items, newest first.
has_moretrue when another page follows.
next_cursorSend this as cursor for the next page. null on the last page.
ParameterWhat it does
limitHow many items, 1 to 100. Default 25.
cursorWhere 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:

GET/v1/messagesList texts →
curl "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:

all-replies.ts
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);
all_replies.py
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_cursor

Let 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:

all-replies.ts
for await (const message of client.messages.list({ direction: 'in', limit: 100 })) {
    console.log(message.receivedAt, message.from, message.text);
}
all_replies.py
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=100 reads in one request what the default of 25 takes four to read. Every page is a request against your 120 a minute. Rate limits
  • Narrow the list first. GET /v1/messages takes since, until, person, contact_id, chat_id, state, direction and source. A narrow list has fewer pages.
  • Keep your place. For a sync that runs every few minutes, keep the newest created_at you’ve stored and ask with since next time, rather than paging everything again. since includes that moment, so skip any id you already have.
  • Catch up on missed webhooks with GET /v1/events?since=…, which keeps 30 days of events. Webhooks