Batch searchEndpoints
POST /v1/search/batch
- Scope
search- Tier
- Starter and up
- Price
- Per served row, at search prices
- Free credit
- Yes, once Starter opens it
Send many queries in one round trip. Every row shares the lane, the result count and the domain filters.
Request body
| Parameter | Type | Default | Description |
|---|---|---|---|
queriesRequired | string[] | · | 1 to your tier's batch size. Each query is 1 to 512 characters after trimming. |
lane | string | auto | web, fast, deep or auto, case-insensitive. Anything else is a 400. |
num_results | integer | 5 | Results per query, 1 to 25. Numbers outside are clamped; anything that is not a number is a 400. Also read as numResults. |
include_domains | string[] | · | Only results from these hosts or their subdomains, up to 20. The web lane applies it before ranking; the others filter after retrieval. Also includeDomains. |
exclude_domains | string[] | · | Never return results from these hosts or their subdomains, up to 20. Also excludeDomains. |
fields | string[] | · | Result keys to keep, 1 to 20 of title, url, snippet, source, published_date, text, text_truncated, text_source. Leave it out for all of them. Any other name is a 400 invalid_fields. |
Unread fields come back in kaer.ignored_params. The body is limited to 64 KB, and the whole batch to 35 seconds.
| Tier | Queries per batch |
|---|---|
| Starter | 10 |
| Build | 50 |
| Scale | 100 |
| Pro | 100 |
Understand how a batch is billed
- Each row that comes back with
ok: trueis billed like a single search, at the lane that served it. - Queries that match after trimming, lowercasing and collapsing spaces run once and are billed once; the repeats come back with
charged: falseandduplicate_of. - Rows that fail carry
charged: falseand cost nothing. - While the batch runs, the gateway holds the dearest possible price for every row (
autoholds the deep price). - If the whole batch fails, nothing is charged.
Response
| Field | Type | Description |
|---|---|---|
object | string | search.batch |
lane | string | The lane you asked for. |
count | integer | Rows requested. |
served | integer | Rows served, repeats included. See billed_queries for what was billed. |
billed_queries | integer | Distinct queries served and billed. |
deduplicated | integer | Rows that repeated an earlier query and were not billed. |
duration_ms | integer | Time the batch took, in milliseconds. |
results[].index | integer | Position in queries. |
results[].query | string | The query. |
results[].ok | boolean | Whether the row was served. |
results[].served_lane | string | Served rows: the lane billed. |
results[].results | array | Served rows: the hits, shaped like Search results; deep rows carry text, text_truncated and text_source. |
results[].charged | boolean | false on rows that failed and on repeats. |
results[].duplicate_of | integer | Repeats: the index of the row that ran the query. |
results[].deep | boolean | Deep rows: true when pages were read, false when the row fell back to snippets. |
results[].degraded_reason | string | Deep rows with deep: false: timeout, engines_failed or pages_unreadable. |
results[].quality | object | partial (boolean), coverage (0 to 1) and result_count, when the lane reports them. |
results[].code | string | Failed rows: timeout, backpressure, upstream_unavailable, invalid_query, no_results or internal. |
results[].error | string | Failed rows: a fixed message for code. |
kaer.cost_usd | number | What the batch cost. |
Rows carry only the fields above. Retry timeout, backpressure and upstream_unavailable rows; fix invalid_query rows; internal is ours to fix.
Headers: x-request-id and the x-ratelimit-* headers. The cost is kaer.cost_usd in the body; there is no x-kaer-cost-usd header.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_json | The body is not valid JSON. Images also needs a JSON object. |
| 400 | queries_required | queries is missing or empty. |
| 400 | batch_too_large | More queries than your tier's batch size. |
| 400 | invalid_query | A batch query is blank or over 512 characters. |
| 400 | invalid_lane | lane is not web, fast, deep or auto. |
| 400 | invalid_num_results | num_results is not a number. |
| 400 | invalid_domains | include_domains or exclude_domains is not an array of up to 20 host names. |
| 403 | tier_required | Your tier does not include this service yet. |
| 413 | body_too_large | The body is over the endpoint's limit: chat 1 MB, search 16 KB, batch 64 KB, images 8 KB, mail 512 KB. |
| 502 | search_failed | The search service failed. Nothing was charged. |
| 503 | search_busy | Search is at capacity. Nothing was charged. |
| 504 | search_timeout | Search ran out of time. Nothing was charged. |
| 502 or 503 | search_unavailable | Search is unreachable or paused. Nothing was charged. |
curl https://api.kaer.ai/v1/search/batch \
-H "Authorization: Bearer $KAER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"queries": ["solar panel efficiency 2026", "heat pump grants uk"],
"lane": "web",
"num_results": 3
}'