Widget and Queries API
The widget runs on the endpoints below, and the same API has calls for managing an agent from your backend.
Base URL: https://api.crawlbrain.com/v1
Search and answers
These four take an agent key, not an API key. They work on every account, Pro or not, because they are what the widget runs on, and they spend credit like any other answer or search.
Send it as an Agent-Key header, or as ?key= on any of them. See Authentication.
Instant search
GET /agent/search
This is the type-ahead layer: hybrid vector plus keyword search with no model call, so it is fast.
| Parameter | Type | Required | Notes |
|---|---|---|---|
key |
string | yes | Agent key |
q |
string | yes | Max 200 characters |
limit |
integer | no | 1 to 20. Defaults to the widget's results setting |
exclude |
string | no | Comma-separated URLs to leave out, max 4,000 characters |
filters |
string | no | Store filters, for example color:red|green;size:38. Up to 6 attributes |
session |
string | no | Your visitor session id, max 96 characters |
curl "https://api.crawlbrain.com/v1/agent/search?key=YOUR-AGENT-KEY&q=shipping&limit=5"
{
"success": true,
"results": [
{
"title": "Shipping and delivery",
"url": "https://example.com/shipping",
"urlPath": "/shipping",
"snippet": "We ship to Canada in 5 to 8 business days...",
"badges": ["both"],
"section": "Help",
"product": null,
"meta": { "image": "https://example.com/img/shipping.jpg" }
}
],
"total": 4
}
| Field | Meaning |
|---|---|
badges |
Why it matched: both, title, url, semantic or keyword |
section |
Breadcrumb category, when the page has one |
product |
Product card (price, image, stock) on store pages, else null |
meta |
Featured image, date and excerpt, when known |
total |
Results after duplicates and filters are removed |
corrected |
Present when a misspelt or foreign-language query was corrected. Show "Showing results for ..." |
facets |
Store filters (Color, Size, Brand) with counts, stores only |
ready |
false while the website's index is still being built |
A query shorter than 2 characters returns no results. Each search is paid from credit. When the balance runs low, search keeps working on keyword matching only, at no cost. Search is rate limited to 600 per minute per agent.
exclude is how a "keep searching" button works: pass the URLs already shown and get the next set.
Ask a question
POST /agent/query
Use this one for the AI answer, with citations.
| Field | Type | Required | Notes |
|---|---|---|---|
question |
string | yes | Max 1,000 characters |
session |
string | no | Your visitor session id, max 96 characters |
history |
array | no | Earlier turns, [{"question": "...", "answer": "..."}], last 20 kept |
exclude_urls |
array | no | Up to 50 URLs to leave out of the answer |
Add ?locale=fr to the URL to answer in a given language.
curl -X POST https://api.crawlbrain.com/v1/agent/query \
-H "Agent-Key: YOUR-AGENT-KEY" \
-H "Content-Type: application/json" \
-d '{"question":"Do you ship to Canada during winter?"}'
{
"success": true,
"response": "Yes. Winter delivery to Canada takes 6 to 10 business days [Source 1].",
"sources": [
{ "n": 1, "url": "https://example.com/shipping", "title": "Shipping and delivery", "product": null }
],
"cited": [1],
"products": []
}
| Field | Meaning |
|---|---|
response |
The answer. [Source N] markers point into sources |
sources |
The pages the answer used, with product cards on store pages |
cited |
The n values the answer actually cites. Show only these |
products |
Store products the answer names by model code |
When the site's content does not cover the question, the response carries only success and response (a short explanation) with no sources, and the call costs nothing. Answers are cached for 10 minutes, so a repeat of the same question is fast and free.
Each answer is paid from credit. With too little credit the call returns 402:
{ "success": false, "code": "credit_balance_empty", "response": "AI answers are temporarily unavailable..." }
Questions are rate limited to 20 per minute.
Follow-up questions. Send the earlier turns in history and the answer takes them into account, so "how much for a sofa?" after "do you ship to Europe?" answers about shipping a sofa to Europe. An empty history list means a fresh question with no context. If you leave history out, CrawlBrain uses the turns it remembers for that session.
Streaming. Send Accept: text/event-stream to receive the answer as Server-Sent Events: meta, then chunk events with text, then citations and done. The streamed answer has no product cards and no products list.
Feedback
POST /agent/feedback
| Field | Type | Required | Notes |
|---|---|---|---|
question |
string | yes | Max 1,000 characters |
feedback |
string | yes | like or dislike |
session |
string | no | The session sent with the question |
This records the thumbs up or down. It shows up in Conversations, where filtering by thumbs down is the fastest way to find answers worth fixing.
Settings
GET /agent/settings
The response holds everything needed to render the widget: placement, colors, texts and starter questions.
curl "https://api.crawlbrain.com/v1/agent/settings?key=YOUR-AGENT-KEY"
Use it when you are building your own front end instead of embedding ours, so your UI stays in sync with the dashboard.
Add ?locale= with the visitor's language to get starter questions and labels in that language. Settings are cached for 5 minutes, so a change in the dashboard can take that long to show. The response also carries siteType (store, docs, blog or business), which decides the result layout, and topResults, the pages to show before the visitor types.
Products
GET /agent/products
| Parameter | Type | Required | Notes |
|---|---|---|---|
list |
string | no | popular (default) or new |
limit |
integer | no | 1 to 24, default 10 |
{
"success": true,
"list": "popular",
"products": [
{
"url": "https://example.com/product/linen-shirt",
"title": "Linen Shirt",
"image": "https://example.com/img/linen-shirt.jpg",
"price": 70.4,
"regularPrice": 88,
"currency": "USD",
"inStock": true,
"rating": 4.5,
"reviewCount": 12
}
]
}
These are the products shown before the visitor types. The list comes back empty unless the website type is store.
POST /agent/products/click
| Field | Type | Required |
|---|---|---|
url |
string | yes |
A click on a product card is recorded here and feeds the popular list. You get success: false back when the URL is not a known product.
Latest articles
GET /agent/pages
| Parameter | Type | Required | Notes |
|---|---|---|---|
list |
string | no | latest |
limit |
integer | no | 1 to 24, default 10 |
The response holds pages, each with url, title and, when known, image, date, excerpt and section. It is empty unless the website type is blog.
Managing an agent
These calls take an API key and need Pro.
Get the agent
GET /agent/get
| Field | Type | Required |
|---|---|---|
id |
string | yes |
This call returns the agent for a website, including its public agent key, and it is the one to make when generating an embed snippet for a customer.
{
"success": true,
"agent": {
"key": "3f6b2a1c-...",
"name": "Ask AI",
"placement": "search-replace",
"search_selector": null,
"floating_position": "bottom-right",
"brand_color": "#0F172A"
}
}
The agent also carries its texts, colors, sizes and icon settings, the same fields /agent/update accepts. A website with no widget returns 404 with agent_not_found.
Update appearance and placement
POST /agent/update
| Field | Values |
|---|---|
id |
Website id, required |
placement |
floating, search-replace, search-adjacent |
search_selector |
CSS selector of the search box. Leave empty to detect it automatically |
floating_position |
bottom-right, bottom-left. Required for floating |
search_placeholder |
Max 60 characters |
brand_color |
#RRGGBB |
button_color |
#RRGGBB |
button_text_color |
#RRGGBB |
button_radius |
0 to 80 |
button_icon |
none, search, sparkle, chat, lightning, robot, question, custom |
font_size |
small, medium, large |
font_family |
system, sans-serif, serif, monospace, rounded, inherit |
search_results_shown |
3 to 10 |
Send only what you want to change. Sending a field as null clears it.
{ "success": true, "changed": ["brand_color"], "agent": { "key": "3f6b2a1c-...", "brand_color": "#1D4ED8" } }
A request with none of these fields returns 422 with no_fields. Changes reach visitors within 5 minutes.
This is deliberately narrower than the Setup page: it covers appearance and placement, while the model tier, tone and translations are account decisions that stay in the dashboard.
Starter questions
All three calls take an API key.
POST /agent/question/add { id, question }
GET /agent/question/list { id }
DELETE /agent/question/delete { id, question }
Use them to manage starter questions from your own code, for example for seasonal rotation: swap in shipping-deadline questions in December and swap them back in January.
addignores a question that already exists, including one CrawlBrain wrote and later switched off.listreturns every question in display order, including ones written automatically and ones switched off. The widget shows the active ones only.deleteneeds the exact text and returns 404 withquestion_not_foundwhen nothing matches.
Reading the question log
GET /agent/question/log
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | |
days |
integer | yes | |
type |
string | no | ERROR for unanswered questions only, INFO for answered ones |
{
"success": true,
"log": [
{ "type": "ERROR", "question": "Do you ship to Iceland?", "response": "I could not find shipping details for Iceland..." }
]
}
The log holds every visitor question for that period with the answer it got. This call takes an API key.
The obvious use is pulling unanswered questions into whatever you already run, so content gaps land in your backlog rather than waiting to be noticed.
curl "https://api.crawlbrain.com/v1/agent/question/log?id=WEBSITE_ID&days=7" \
-H "Api-Key: cb_your_key_here"
Building your own front end
The widget is optional. If you want your own interface, the pattern is:
GET /agent/settingsonce, to pick up texts and starter questions.GET /agent/searchon each keystroke, debounced by 150 to 250 milliseconds.POST /agent/queryon submit, with asessionid you generate and keep for the visit, and the earlier turns inhistoryfor follow-ups.POST /agent/feedbackwhen someone rates an answer.
Render the sources from the query response. Do not skip them: they are how a visitor verifies the answer, and they are the reason to trust it at all.
An agent key is safe in front-end code, but an API key is not.