BilbyHelp Center

Your first API call

Start a chat with a question over HTTP, stream Bilby's answer as it's written, then follow up in the same chat.

4 min read · Updated 31 Oct 2026 · For developers

Before you begin

You have an API key. See API keys. The examples read it from BILBY_API_KEY.
You can run curl, or Python 3 with the requests package.
Optional: if your key covers more than one organization, have the organization's workspaceId ready. Step 1 shows how to find it.

How a call works

Asking Bilby takes two requests. The first sends your question and returns at once with a streamUrl. The second follows that URL and receives the answer as it's written, because Bilby may search and read for a minute or more first.

POST /conversations with amessage202: conversationId, runId,streamUrlGET the streamUrlThe answer, as server-senteventsPOST /conversations/:id tofollow up
POST /conversations with amessage202: conversationId, runId,streamUrlGET the streamUrlThe answer, as server-senteventsPOST /conversations/:id tofollow up

Every request goes to https://pulse.bilby.ai/api/v1 with your key in the Authorization header.

Step 1: Find your organization

A key scoped to one organization works there without saying so: skip this step. Otherwise, list the organizations your key can reach:

curl https://pulse.bilby.ai/api/v1/workspaces \
  -H "Authorization: Bearer $BILBY_API_KEY"
{
  "items": [{ "id": "8d1e…", "name": "Acme Research", "slug": "acme-research" }],
  "total": 1
}

The API calls an organization a workspace. Use its id as workspaceId: in the body when you start a chat, and as a ?workspaceId= query on the requests after it.

Step 2: Ask a question

POST /conversations with a message starts a chat and admits the first answer:

curl -X POST https://pulse.bilby.ai/api/v1/conversations \
  -H "Authorization: Bearer $BILBY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "What changed in Malaysian data protection rules this year? Cite government sources."}'

Bilby replies 202 Accepted:

{
  "ok": true,
  "conversationId": "3f0c…",
  "status": "accepted",
  "runId": "…",
  "streamUrl": "/api/v1/conversations/3f0c…/stream?runId=…"
}
Body fieldRequiredWhat it does
messageYes, to get an answerYour question. Without it, you get 201 and an empty chat
workspaceIdFor keys with several organizationsThe organization to ask in
titleNoThe chat's name in Bilby. Bilby writes one if you leave it out
agentIdNoOne of the organization's agents. The default agent otherwise
reasoningEffortNoauto, low, medium, high or max
attachmentsNoUp to 10 files uploaded first with POST /attachments/init

Step 3: Stream the answer

Follow streamUrl on the same host. The answer arrives as server-sent events, in the AI SDK's UI message stream format (x-vercel-ai-ui-message-stream: v1). Each event is a data: line of JSON. The text of the answer comes in text-delta events, in their delta field, and the stream ends with data: [DONE].

curl -N "https://pulse.bilby.ai/api/v1/conversations/3f0c…/stream?runId=…" \
  -H "Authorization: Bearer $BILBY_API_KEY"
data: {"type":"start", …}
data: {"type":"text-delta","id":"…","delta":"Malaysia amended its Personal Data Protection Act"}
data: {"type":"text-delta","id":"…","delta":" in 2024, and the changes took effect in stages…"}
data: {"type":"finish", …}
data: [DONE]

Other events carry Bilby's working steps, such as the searches it runs. Skip the ones you don't need.

The same in Python, printing the answer as it arrives:

import json
import os

import requests

API = "https://pulse.bilby.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BILBY_API_KEY']}"}

started = requests.post(
    f"{API}/conversations",
    headers=HEADERS,
    json={"message": "What changed in Malaysian data protection rules this year? Cite government sources."},
)
started.raise_for_status()
turn = started.json()

with requests.get(f"https://pulse.bilby.ai{turn['streamUrl']}", headers=HEADERS, stream=True) as stream:
    for line in stream.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data: "):
            continue
        data = line.removeprefix("data: ")
        if data == "[DONE]":
            break
        event = json.loads(data)
        if event["type"] == "text-delta":
            print(event["delta"], end="", flush=True)

Note

With a key for several organizations, add &workspaceId= and the organization's id to the streamUrl before you follow it.

If the connection drops, request the streamUrl again with &cursor= and the number of events you already have, or send a Last-Event-ID header, and the stream picks up from there. Once the answer is finished, the stream returns 204 No Content.

Step 4: Read the whole chat

GET /conversations/:id returns the chat with its messages, including the finished answer and its sources. Use it after a 204, or whenever you want the full text at once.

curl https://pulse.bilby.ai/api/v1/conversations/3f0c… \
  -H "Authorization: Bearer $BILBY_API_KEY"

While an answer is running, activeRun gives its runId and streamUrl. When it's null, the answer is complete.

Step 5: Follow up

POST /conversations/:id sends a follow-up in the same chat. Bilby keeps the history, so send only the new message:

curl -X POST https://pulse.bilby.ai/api/v1/conversations/3f0c… \
  -H "Authorization: Bearer $BILBY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: digest-2026-10-31" \
  -d '{"message": "Which of these changes affect transfers of data abroad?"}'

It returns 202 with a new streamUrl, as in Step 2. An Idempotency-Key makes a retried request safe: the same key won't send the message twice.

To stop an answer that's still running, POST /conversations/:id/abort. What Bilby wrote so far is kept.

When something goes wrong

Errors come back as JSON with a message in error.message.

StatusMeaning
400The body is missing something, such as an empty message, or a key for several organizations sent no workspaceId
401The key is wrong, expired or revoked
402Your allowance is used up. The message says when it resets
403The key isn't scoped to that organization, or you're no longer a member
404No such chat, or not one of yours
409An answer is already running in this chat. Wait for it, or abort it

Tip

Sometimes Bilby asks a few multiple-choice questions before it answers. They show in pendingQuestions on GET /conversations/:id. Answer them with POST /conversations/:id/tool-outputs, and Bilby carries on.

Try it in Bilby

Make a key, then run Step 2.

The chat you start appears in Bilby too, so you can read the answer there.

Open Bilby →
Was this guide helpful?Still stuck?

On this page