Skip to main content
Docs · Kaer Search

The Search API.

Reference for POST /v1/search, batch search and images: every field, lane, error and limit, with examples you can paste.

Base URL
https://api.kaer.ai/v1
Auth
Authorization: Bearer kaer_live_…
Scope
search

Quickstart

Four steps from nothing to a first result. Every endpoint lives under https://api.kaer.ai/v1.

1. Create an account

Sign up at api.kaer.ai. New accounts get $10 of free credit, valid for 90 days.

2. Open search with a top-up

Search is locked on the Free tier and answers 403 tier_required there. Your first top-up of $5 or more moves you to Starter at once: search opens, and the free credit pays for it too.

3. Make a key with the search scope

In the dashboard, open Keys, create a key and tick search. Copy it right away: it starts with kaer_live_ and is shown only once. Keep it on your server.

curl https://api.kaer.ai/v1/search \
  -H "Authorization: Bearer $KAER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "eu packaging regulation importers",
    "lane": "deep",
    "num_results": 3
  }'

The answer names the lane that ran and what it cost:

Response, trimmed
{
  "object": "search.result",
  "lane": "deep",
  "served_lane": "deep",
  "results": [
    {
      "title": "The PPWR: EU Regulation for a More Circular Economy and Less Packaging Waste",
      "url": "https://everwave.de/en/2026/07/13/the-ppwr-eu-regulati…",
      "snippet": "The PPWR—Packaging and Packaging Waste Regulation; EU Regulation 2025/40—will take effect on August 12,…",
      "source": ["kaer_web"],
      "text": "The PPWR (Packaging and Packaging Waste Regulation, EU Regulation 2025/40) will take effect on 12 August 2026 and introd…",
      "text_source": "page",
      "text_truncated": false
    },
    { "title": "Regulation (EU) 2025/40 on packaging and packa…", … },
    { "title": "EU Packaging Regulation Training – Become PPWR…", … }
  ],
  "kaer": { "request_id": "req_…", "cost_usd": 0.003 }
}

Authentication

Every call except GET /v1/models needs a key. Send it as a bearer token:

HTTP
Authorization: Bearer kaer_live_...

or in an x-api-key header. When Authorization holds a bearer token, it wins.

Scopes

ScopeEndpointsOpens at
modelsPOST /v1/chat/completions, GET /v1/meFree
searchPOST /v1/search, POST /v1/search/batch, POST /v1/imagesStarter
mapsGET /v1/mapsStarter
mailPOST /v1/mail/send, GET /v1/mail/messages/{id}Build

What is checked, in order

  1. The key exists and is not revoked or expired: else 401 invalid_api_key (no key at all: missing_api_key).
  2. The account is active: else 403 account_inactive.
  3. The caller's IP is on the key's allow-list, when it has one: else 403 ip_not_allowed.
  4. The key has the endpoint's scope: else 403 insufficient_scope.
  5. Your tier includes the service: else 403 tier_required.
  6. Rate limits: else 429 rate_limit_exceeded.
  7. A hold for the call's maximum cost: else 402 (insufficient_credits, monthly_limit_reached or key_budget_exceeded).

Allow-lists, budgets, expiry and rotation are in Key security.

Batch search

POSTapi.kaer.ai/v1/search/batch
Scope
search
Tier
Starter and up
Price
Per served row, at search prices
Free credit
Yes, once Starter opens it

Many queries in one round trip. Every row shares the lane, the result count and the domain filters.

Request body

FieldTypeDefaultDescription
queriesRequiredstring[]·1 to your tier's batch size. Each query is 1 to 512 characters after trimming.
lanestringautoweb, fast, deep or auto, case-insensitive. Anything else is a 400.
num_resultsinteger5Results per query, 1 to 25. Numbers outside are clamped; anything that is not a number is a 400. Also read as numResults.
include_domainsstring[]·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_domainsstring[]·Never return results from these hosts or their subdomains, up to 20. Also excludeDomains.

Unread fields come back in kaer.ignored_params. The body is limited to 64 KB, and the whole batch to 40 seconds.

TierQueries per batch
Starter10
Build50
Scale100
Pro100

Billing

  • Each row that comes back with ok: true is billed like a single search, at the lane that served it.
  • Identical queries run once, but every row is billed, duplicates included.
  • Rows that fail carry charged: false and cost nothing.
  • While the batch runs, the gateway holds the dearest possible price for every row (auto holds the deep price).
  • If the whole batch fails, nothing is charged.

Response

FieldTypeDescription
objectstringsearch.batch
lanestringThe lane you asked for.
countintegerRows requested.
servedintegerRows served and billed.
results[].indexintegerPosition in queries.
results[].querystringThe query.
results[].okbooleanWhether the row was served.
results[].served_lanestringServed rows: the lane billed.
results[].resultsarrayServed rows: the hits, shaped like Search results.
results[].chargedbooleanfalse on rows that failed.
results[].errorstringFailed rows: why.
kaer.cost_usdnumberWhat the batch cost.

Rows can carry more diagnostic fields; read only the ones above.

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

StatusCodeWhen
400invalid_jsonThe body is not valid JSON. Images also needs a JSON object.
400queries_requiredqueries is missing or empty.
400batch_too_largeMore queries than your tier's batch size.
400invalid_queryA batch query is blank or over 512 characters.
400invalid_lanelane is not web, fast, deep or auto.
400invalid_num_resultsnum_results is not a number.
400invalid_domainsinclude_domains or exclude_domains is not an array of up to 20 host names.
403tier_requiredYour tier does not include this service yet.
413body_too_largeThe body is over the endpoint's limit: chat 1 MB, search 16 KB, batch 64 KB, images 8 KB, mail 512 KB.
502search_failedThe search service failed. Nothing was charged.
502 or 503search_unavailableSearch is unreachable, timed out 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
  }'

Images

POSTapi.kaer.ai/v1/images
Scope
search
Tier
Starter and up
Price
$0.002 per call
Free credit
Yes, once Starter opens it

Real photos, diagrams and illustrations for a subject, from Kaer's picture index with live Wikimedia Commons and Pexels results when the index is thin. Every picture is checked and comes with its licence, a ready-made credit line and whether a business website may reuse it.

Request body

FieldTypeDefaultDescription
queryRequiredstring·What to picture, 1 to 300 characters ("sourdough bread", "water cycle diagram").
countinteger121 to 30 pictures. Anything else, a numeric string included, is a 400.
reusablebooleanfalseOnly pictures a business website may use with the returned credit.

Other fields come back in kaer.ignored_params. The body is limited to 8 KB; a call times out after 15 seconds.

Price

One call costs $0.002 whatever count is, even when it finds no pictures. A call that fails is not charged.

Response

FieldTypeDescription
objectstringimages.result
querystringThe query, trimmed.
subjectstringThe subject the index searched for.
countintegerPictures returned.
reusable_onlybooleanEchoes reusable.
withheldobjectPresent when some pictures were held back.
results[].idstringStable picture id.
results[].kindstringphoto, diagram or illustration.
results[].titlestringPicture title.
results[].image_urlstringFull-size image (https).
results[].thumb_urlstringThumbnail (https).
results[].page_urlstringThe page the picture comes from.
results[].hoststringThat page's host.
results[].width, heightintegerPixel size, when known.
results[].sourcestringwikimedia_commons, pexels or web.
results[].licenseobjectname, code, label and url.
results[].creditstringAuthor or owner.
results[].attributionstringAttribution text from the source.
results[].credit_linestringA ready-made credit to publish with the picture.
results[].attribution_requiredbooleanPublish credit_line next to the picture when true.
results[].share_alikebooleanDerivatives must keep the same licence.
results[].commercial_okbooleanA business website may use it.
JSON
{
  "object": "images.result",
  "query": "sourdough bread",
  "subject": "sourdough bread",
  "count": 12,
  "reusable_only": true,
  "results": [
    {
      "id": "commons:sourdough.jpg",
      "kind": "photo",
      "title": "Sourdough loaf",
      "image_url": "https://upload.wikimedia.org/…",
      "thumb_url": "https://upload.wikimedia.org/…/500px-…",
      "page_url": "https://commons.wikimedia.org/wiki/File:…",
      "host": "commons.wikimedia.org",
      "width": 4000,
      "height": 3000,
      "source": "wikimedia_commons",
      "license": { "name": "…", "code": "cc-by-sa", "label": "CC BY-SA 4.0", "url": "https://creativecommons.org/licenses/by-sa/4.0/" },
      "credit": "Ann K",
      "attribution": "…",
      "credit_line": "…",
      "attribution_required": true,
      "share_alike": true,
      "commercial_ok": true
    }
  ],
  "kaer": { "request_id": "req_…", "cost_usd": 0.002 }
}

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

StatusCodeWhen
400invalid_jsonThe body is not valid JSON. Images also needs a JSON object.
400query_requiredquery is missing or blank.
400query_too_longquery is over 512 characters (search) or 300 (images).
400invalid_countcount is not an integer from 1 to 30.
400invalid_reusablereusable is not true or false.
403tier_requiredYour tier does not include this service yet.
413body_too_largeThe body is over the endpoint's limit: chat 1 MB, search 16 KB, batch 64 KB, images 8 KB, mail 512 KB.
502images_failedThe picture search failed. Nothing was charged.
503images_busyImages are at capacity. Nothing was charged.
502 or 503images_unavailableImages are unreachable, timed out or paused. Nothing was charged.
curl https://api.kaer.ai/v1/images \
  -H "Authorization: Bearer $KAER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "sourdough bread", "count": 20, "reusable": true}'

Choosing a lane

Every lane takes the same request and returns the same shape. Pick by how far the answer has to come from.

LaneSearchesSend it forPer 1,000
webKaer's index alone.High volume on known topics, at the lowest price.$0.50
fastThe index racing the live web.Pages the index may not hold yet.$1.00
deepEvery lane, re-ranked, with each page's text.Grounding a model in the source, not a snippet.$3.00
autoKaer picks a lane per query.When you would rather not choose.As served

On deep, each result also carries text (the page's main text, up to 4,000 characters), text_truncated and text_source: page, or snippet when a site would not serve the page.

auto holds the deep price while it runs, then settles at the lane that answered. served_lane names it, so you always know what you paid for.

Use from assistants

Kaer's MCP server lets Claude, ChatGPT, Cursor and other MCP clients ask Kaer, and Kaer's web search runs on this index. It is billed to your Kaer account, not to an API platform balance.

It takes a Kaer account key (kaer_sk_…, made in the Kaer app), not a kaer_live_ API key:

Claude Code
claude mcp add --transport http kaer https://app.kaer.ai/api/mcp \
  --header "Authorization: Bearer kaer_sk_…"

Every client, step by step: Connect Claude and ChatGPT over MCP.

Pricing

Search is billed per query, not per result: a query costs the same with 1 result or 25. The price is set by the lane that served it.

LanePer queryPer 1,000Per 100,000
web$0.0005$0.50$50.00
fast$0.001$1.00$100.00
deep$0.003$3.00$300.00
auto$0.0005 to $0.003As servedAs served
Images (POST /v1/images)$0.002 per call$2.00$200.00

Rules

  • A served query is billed whatever num_results is and however many results come back, an empty list included.
  • auto holds the deep price ($0.003) while it runs, then settles at the lane that served it; served_lane names it. If the lane cannot be read, the query is billed as deep.
  • In a batch, every served row is billed like a single search, duplicates included. Failed rows are free. The hold is the dearest lane price times the number of rows.
  • Queries that fail or time out are not charged.
  • Free credit pays for search and images on Starter and up. Search is not open on the Free tier.

Worked examples

At today's prices
10,000 web searches10,000 × $0.0005$5.00
10,000 fast searches10,000 × $0.001$10.00
10,000 deep searches10,000 × $0.003$30.00
100 auto searches: 60 served by web, 30 by fast, 10 by deep60 × $0.0005 + 30 × $0.001 + 10 × $0.003$0.09
A batch of 50 fast queries, 2 rows failed48 × $0.001 (hold while running: 50 × $0.001)$0.048
1,000 image calls1,000 × $0.002$2.00

Errors

Errors are OpenAI-shaped, so any OpenAI client shows them sensibly:

JSON
{
  "error": {
    "message": "The model 'gpt-9' does not exist on api.kaer.ai.",
    "type": "invalid_request_error",
    "param": null,
    "code": "model_not_found"
  }
}

type is the OpenAI class; code is the precise reason, so branch on code. A call that fails with an error status is not charged; a stream that breaks after it started is charged for what it sent. The maps service's own errors keep their shape, see Maps.

Keys and access

CodeStatusMeaningWhat to do
missing_api_key401No key in Authorization: Bearer or x-api-key.Send your key.
invalid_api_key401The key is malformed, unknown, revoked or expired.Check the key, or create a new one.
account_inactive403The account is suspended or closed, or owes a reversed payment.If the message mentions a balance owed, add credit. Otherwise write to [email protected].
ip_not_allowed403The caller's IP is not on the key's allow-list.Add the address or range to the key, or call from an allowed host.
insufficient_scope403The key does not have the endpoint's scope.Add the scope to the key in the dashboard.
tier_required403Your tier does not include this service yet.Search, images and maps open at Starter ($5 paid). Mail opens at Build.
unknown_endpoint404No such endpoint under /v1.Check the path against this page.
not_found404The path is not part of the API.Call https://api.kaer.ai/v1/....

Balance and spend

CodeStatusMeaningWhat to do
insufficient_credits402Your balance cannot cover the call's hold. Free credit never pays for mail.Top up, turn on automatic top-up, or lower max_tokens to shrink the hold.
monthly_limit_reached402This month's spend plus open holds would pass your monthly cap (your budget or your tier's).Raise your budget in Settings, move up a tier, or wait for the new month (UTC).
key_budget_exceeded402The key's monthly budget is used up. Open holds count.Raise or clear the key's budget.
payment_required402A refunded or disputed payment left a balance owed.Add credit to clear it.

Rate limits

CodeStatusMeaningWhat to do
rate_limit_exceeded429Too many requests this minute for the key or the account, or too many invalid keys from your address.Wait for Retry-After, then back off. See Rate limits and tiers.
too_many_streams429Your account already has its tier's maximum of open streams.Wait for a stream to finish.
key_shared429A Free tier key was used from too many networks within an hour.Keep keys on your server. A top-up moves you off the Free tier.
mail_rate_limit429Messages in the last minute reached your tier's cap.Wait 60 seconds (Retry-After).
mail_daily_limit429Recipients today (UTC) would pass your tier's cap.Wait until 00:00 UTC (Retry-After) or move up a tier.

Request

CodeStatusMeaningWhat to do
invalid_json400The body is not valid JSON. Images also needs a JSON object.Send a JSON object with Content-Type: application/json.
body_too_large413The body is over the endpoint's limit: chat 1 MB, search 16 KB, batch 64 KB, images 8 KB, mail 512 KB.Send less.
query_required400query is missing or blank.Send a query.
query_too_long400query is over 512 characters (search) or 300 (images).Shorten it.
invalid_lane400lane is not web, fast, deep or auto.Use one of the four lanes.
invalid_num_results400num_results is not a number.Send an integer from 1 to 25.
invalid_domains400include_domains or exclude_domains is not an array of up to 20 host names.Send arrays of host names.
queries_required400queries is missing or empty.Send an array of query strings.
batch_too_large400More queries than your tier's batch size.Split the batch, or move up a tier.
invalid_query400A batch query is blank or over 512 characters.Fix or drop that query.
invalid_count400count is not an integer from 1 to 30.Send an integer from 1 to 30.
invalid_reusable400reusable is not true or false.Send a boolean.

Service

CodeStatusMeaningWhat to do
search_failed502The search service failed. Nothing was charged.Retry.
search_busy503Search is at capacity. Nothing was charged.Retry in a few seconds.
search_unavailable502 or 503Search is unreachable, timed out or paused. Nothing was charged.Retry later.
images_failed502The picture search failed. Nothing was charged.Retry.
images_busy503Images are at capacity. Nothing was charged.Retry in a few seconds.
images_unavailable502 or 503Images are unreachable, timed out or paused. Nothing was charged.Retry later.

Rate limits and tiers

Your tier is earned by money paid in (net of refunds) and time since your first payment, never by spend.

TierReq/min per keyReq/min per accountOpen streamsMonthly cap
Free20302$10
Starter601205$100
Build30060020$1,000
Scale1,0002,00050$5,000
Pro3,0006,000100$25,000
EnterpriseCustomCustomCustomCustom

How tiers rise

TierUnlockServicesBatch sizeActive keys
FreeSign upModelsNone3
Starter$5 paidModels, Search, Maps1010
Build$50 paid and 7 days since your first paymentModels, Search, Maps, Mail5025
Scale$250 paid and 14 days since your first paymentModels, Search, Maps, Mail10050
Pro$1,000 paid and 30 days since your first paymentModels, Search, Maps, Mail100100
EnterpriseContract with KaerAllCustomCustom

How limits apply

  • Requests are counted in fixed 60-second windows, per key and per account; whichever runs out first answers 429 rate_limit_exceeded with Retry-After.
  • An edge gateway also caps each key at 12,000 requests a minute (bursts up to 2000), so the per-key limit is the lower of that and your tier's figure.
  • Open streams are counted per account; over the limit, 429 too_many_streams.
  • The monthly cap counts settled spend plus open holds in the calendar month (UTC). Your own budget in Settings can only lower it.
  • The dashboard playground has its own limit of 20 calls a minute.
  • Restricted accounts run at Free tier limits.

On the Free tier only models are open and spend is capped at $10 a month, even with $10 of free credit. A first top-up of $5 moves you to Starter at once.

Mail caps are in Mail; top-up limits in Billing.