Skip to main content

Chatbase API: Integration Guide for Developers

· 11 min read
Achraf Bizyane
Software Engineer

Chatbase ships two REST API versions. API v1 is the original integration path; API v2 is a modern redesign with structured error codes, cursor-based pagination, Server-Sent Events (SSE) streaming, and a cleaner response contract. This guide covers v2 exclusively — it is the forward path for all new integrations in 2026. By the end, you will know how to authenticate, send streaming chat messages, paginate through conversation history, handle rate limits, and feed fresh website content scraped with Apify into your Chatbase knowledge base.

Prerequisites: API v2 requires a Standard plan or above. Free and Hobby accounts cannot use the API. Check the pricing guide to evaluate upgrading.

Getting Your API Key

  1. Log into the Chatbase dashboard.
  2. Navigate to Workspace Settings → API Keys.
  3. Click Create API Key and copy the generated key immediately — it is only shown once.
  4. Store the key in a secrets manager or environment variable. Never commit it to a repository or expose it in client-side code.

Every API key grants full write access to its workspace. Treat it with the same care as a database password.

Authentication

All v2 endpoints (except GET /api/v2/health) require a Bearer token in the Authorization header:

Authorization: Bearer YOUR_API_KEY

The base URL for all v2 requests is:

https://www.chatbase.co/api/v2

Quick auth test

curl -X GET 'https://www.chatbase.co/api/v2/health' \
-H 'Authorization: Bearer YOUR_API_KEY'

A 200 OK response confirms your key and plan are valid.

Available Endpoints

MethodEndpointDescription
GET/api/v2/healthHealth check (no auth required)
POST/api/v2/agents/{agentId}/chatSend a message, receive a response
POST/api/v2/agents/{agentId}/conversations/{conversationId}/retryRetry the last message in a conversation
GET/api/v2/agents/{agentId}/conversationsList all conversations (paginated)
GET/api/v2/agents/{agentId}/conversations/{conversationId}Get a single conversation
GET/api/v2/agents/{agentId}/conversations/{conversationId}/messagesList messages in a conversation
GET/api/v2/agents/{agentId}/users/{userId}/conversationsList conversations for a user
POST/api/v2/agents/{agentId}/conversations/{conversationId}/tool-resultSubmit a client action result
PATCH/api/v2/agents/{agentId}/conversations/{conversationId}/messages/{messageId}/feedbackSubmit message feedback

Getting your Agent ID

In the Chatbase dashboard, select your AI agent, go to Settings → General, and copy the Agent ID field.

Sending Your First Chat Message

The simplest non-streaming request:

curl -X POST 'https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/chat' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"message": "What is your return policy?",
"stream": false
}'

Successful response:

{
"data": {
"id": "msg_abc123",
"role": "assistant",
"parts": [
{
"type": "text",
"text": "Our return policy allows returns within 30 days of purchase..."
}
],
"metadata": {
"userMessageId": "msg_xyz789",
"conversationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"userId": null,
"finishReason": "stop",
"usage": { "credits": 2 }
}
}
}

The conversationId in the response metadata is the key to multi-turn conversations. Pass it back in subsequent requests to maintain context.

Multi-turn conversation

curl -X POST 'https://www.chatbase.co/api/v2/agents/YOUR_AGENT_ID/chat' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"message": "What if the item is damaged?",
"conversationId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"stream": false
}'

Omit conversationId to start a new conversation thread.

Streaming Responses with SSE

For production UIs, streaming delivers a far better perceived response time — characters arrive as the model generates them rather than after the full response is ready. Chatbase v2 streaming uses Server-Sent Events over a regular HTTP response with Content-Type: text/event-stream.

Set "stream": true (this is the default) in the request body:

// Node.js streaming example
const response = await fetch(
`https://www.chatbase.co/api/v2/agents/${agentId}/chat`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
message: "Summarize your product features",
stream: true,
conversationId: existingConversationId, // omit for new conversation
}),
}
);

const reader = response.body.getReader();
const decoder = new TextDecoder();
let conversationId = null;

while (true) {
const { done, value } = await reader.read();
if (done) break;

const lines = decoder.decode(value, { stream: true }).split("\n");
for (const line of lines) {
if (!line.trim() || line === "data: [DONE]") continue;

const event = JSON.parse(line);
switch (event.type) {
case "text-delta":
// Stream text to your UI
process.stdout.write(event.delta);
break;
case "message-metadata":
conversationId = event.conversationId;
console.log(`\nCredits used: ${event.usage.credits}`);
break;
case "error":
console.error("Stream error:", event.errorText);
break;
}
}
}

SSE event types

Event typeDescription
message-startSignals the start of a new message; includes messageId
text-deltaA text chunk from the model; concatenate event.delta to build the full response
tool-input-availableA client action has been invoked with full input object
tool-output-availableResult of a tool call with full output object
start-step / finish-stepStep lifecycle markers for multi-step reasoning
finishStream complete; finishReason is stop or tool-calls
message-metadataChatbase metadata: conversationId, userId, finishReason, usage.credits
errorError from the model or API; check errorText

The stream terminates with data: [DONE].

Cursor-Based Pagination

List endpoints (conversations, messages) use cursor-based pagination. Cursors are opaque, base64-encoded strings — treat them as tokens, not parse them.

Parameters

ParameterTypeDefaultDescription
cursorstringCursor from a previous response; omit to start from the beginning
limitinteger20Items per page. Range: 1–100

Response shape

{
"data": [...],
"pagination": {
"cursor": "eyJpZCI6Im1zZ19hYmMxMjMifQ==",
"hasMore": true,
"total": 347
}
}

pagination.cursor is null and hasMore is false when you have reached the last page.

Paginating through all conversations

import requests

def fetch_all_conversations(agent_id: str, api_key: str) -> list:
conversations = []
cursor = None
base_url = f"https://www.chatbase.co/api/v2/agents/{agent_id}/conversations"
headers = {"Authorization": f"Bearer {api_key}"}

while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor

response = requests.get(base_url, headers=headers, params=params)
response.raise_for_status()
payload = response.json()

conversations.extend(payload["data"])
pagination = payload["pagination"]

if not pagination["hasMore"]:
break
cursor = pagination["cursor"]

return conversations

Note on message pagination: The messages endpoint paginates backward from the newest message. The first page contains the most recent messages; passing cursor fetches the next older page. Within each page, messages are returned in chronological (oldest → newest) order.

Rate Limiting

The API enforces 100 requests per 10-second sliding window, scoped per API key and IP address.

Every response includes these rate limit headers:

HeaderDescription
X-RateLimit-LimitMaximum requests in the window (100)
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp (ms) when the window resets
Retry-AfterSeconds to wait before retrying (only on 429 responses)

Handling 429 responses

import time
import requests
from requests.exceptions import HTTPError

def chat_with_backoff(agent_id, api_key, message, max_retries=5):
url = f"https://www.chatbase.co/api/v2/agents/{agent_id}/chat"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
payload = {"message": message, "stream": False}

for attempt in range(max_retries):
response = requests.post(url, json=payload, headers=headers)

if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
print(f"Rate limited. Retrying in {retry_after}s...")
time.sleep(retry_after)
continue

response.raise_for_status()
return response.json()

raise Exception("Max retries exceeded")

Always log the x-request-id header from failed requests — it is required when contacting Chatbase support about an error.

Piping Apify-Scraped Content into Chatbase

One of the most powerful developer workflows is combining Apify's web scraping infrastructure with Chatbase's knowledge base to keep your AI agent current with live website content. This is the foundation of any solid data-for-AI RAG pipeline.

The workflow

  1. Scrape your documentation, product pages, or help center using Apify's Website Content Crawler
  2. Clean the output — strip navigation, footers, and boilerplate
  3. Format as plain text or Markdown
  4. Upload to Chatbase via the Sources API

Step 1: Run the Apify scraper

Using the Apify API to trigger a crawl:

import requests

APIFY_API_TOKEN = "your_apify_token"
actor_id = "apify~website-content-crawler"

run_input = {
"startUrls": [{"url": "https://docs.yoursite.com"}],
"maxCrawlDepth": 3,
"maxCrawlPages": 100,
"outputFormats": ["markdown"],
}

response = requests.post(
f"https://api.apify.com/v2/acts/{actor_id}/runs",
json=run_input,
params={"token": APIFY_API_TOKEN},
)
run = response.json()
run_id = run["data"]["id"]
print(f"Crawl started: {run_id}")

See the full scrape website content guide for crawler configuration options.

Step 2: Retrieve the scraped content

import time

def wait_for_run(run_id: str, token: str) -> list:
while True:
status_resp = requests.get(
f"https://api.apify.com/v2/actor-runs/{run_id}",
params={"token": token},
)
status = status_resp.json()["data"]["status"]
if status == "SUCCEEDED":
break
time.sleep(5)

dataset_resp = requests.get(
f"https://api.apify.com/v2/actor-runs/{run_id}/dataset/items",
params={"token": token, "format": "json"},
)
return dataset_resp.json()

Step 3: Upload to Chatbase

Chatbase's source management API (available on Standard+) allows programmatic addition of text content to your agent's knowledge base. Concatenate the cleaned page content and send it as a text source:

def upload_text_to_chatbase(agent_id: str, api_key: str, text_content: str, source_name: str):
url = f"https://www.chatbase.co/api/v1/update-chatbot-data"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
payload = {
"chatbotId": agent_id,
"sourceText": text_content,
}
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
return response.json()

# Build the corpus from Apify output
pages = wait_for_run(run_id, APIFY_API_TOKEN)
corpus = "\n\n---\n\n".join(
page.get("markdown", page.get("text", ""))
for page in pages
if page.get("markdown") or page.get("text")
)

upload_text_to_chatbase(agent_id=CHATBASE_AGENT_ID, api_key=CHATBASE_API_KEY, text_content=corpus, source_name="docs-crawl")

This pattern keeps your chatbot synchronized with your latest documentation on a schedule. Run the crawler weekly (or on every deployment), push the updated corpus to Chatbase, and trigger a retrain. On the Standard plan with auto-retrain enabled, the retraining step can happen automatically.

Best Practices

Scope credit consumption. Use economical models (1 credit/response) for straightforward FAQ lookups and reserve Claude Opus or GPT-5 tier models for complex queries that benefit from deeper reasoning. Track usage in Workspace → Usage to catch unexpected model consumption patterns.

Persist conversation IDs. Store the conversationId from the first API response in your session layer. Passing it on follow-up messages gives the agent context from the entire session, dramatically improving multi-turn accuracy.

Stream in production. Non-streaming responses for long answers can take 3–8 seconds to return. SSE streaming delivers the first characters within ~300ms and sustains a perceptibly faster UI experience.

Separate API keys per environment. Create distinct API keys for development, staging, and production workspaces. This isolates credit consumption and prevents test traffic from polluting production analytics.

Log x-request-id headers. Every response includes a unique request ID. Capture it in your application logs. When something goes wrong, this ID is the fastest path to resolution with Chatbase support.

Ready to start building? Create your Chatbase account and get your API key from the Standard plan. If you want to evaluate Chatbase against developer-first alternatives like Botpress, see the Chatbase vs Botpress deep dive.

Frequently Asked Questions

The Chatbase API v2 requires the Standard plan or above. Free and Hobby plan accounts will receive a 403 Forbidden error even with a valid API key. API v1 has the same restriction on Standard+.

The API enforces 100 requests per 10-second sliding window, scoped per API key and IP address. When you hit the limit, you receive a 429 status code with a Retry-After header indicating how many seconds to wait before retrying.

Capture the conversationId from the first API response's metadata object and pass it in the conversationId field of all subsequent requests to the same conversation thread. Omitting conversationId starts a fresh conversation with no prior context.

Yes. Set stream: true in the request body (it is the default) to receive responses as Server-Sent Events. Each line in the stream is a newline-delimited JSON object with a type field. The text-delta event carries text chunks; message-metadata carries the final conversationId and credit usage.

Yes. The Chatbase source management API (Standard plan+) accepts text content uploads to update an agent's knowledge base programmatically. This is the foundation for automated pipelines that pull fresh content from Apify crawls and push it directly into Chatbase without manual dashboard updates.

List endpoints return a pagination object with cursor, hasMore, and total fields. Pass the cursor value from one response as the cursor parameter in the next request to fetch the next page. When hasMore is false, you have reached the end of the list. Cursors are opaque tokens — do not decode or construct them manually.

Common mistakes and fixes

Getting 401 Unauthorized on every API request.

Verify your Authorization header uses the format 'Bearer YOUR_API_KEY' with a capital B. API keys are scoped to the workspace — make sure you are using the key from the correct workspace.

Receiving 403 Forbidden when calling API v2 endpoints.

API v2 requires Standard plan or above. Check your plan in Workspace Settings → Billing. Accounts on Free or Hobby plans will be rejected with a 403 regardless of a valid key.

Rate limit 429 errors during high-volume requests.

The API enforces 100 requests per 10-second sliding window per key. Read the Retry-After header on 429 responses and implement exponential backoff. For bulk operations, batch your requests and space them out.

Streaming SSE response is not arriving as expected.

Ensure you are reading the response as a stream (responseType: 'stream' in axios, or using fetch with ReadableStream). Each line is a newline-delimited JSON object with a 'type' field — parse each line individually.