Describe the page you want
Write the query as a description of the page that would answer the question. Search is semantic, so describe the page in plain language instead of stacking keywords.
Name the kind of page you want: documentation, postmortem, release notes, comparison.
Split a broad question into parallel searches
Run one narrow search per aspect of a broad question instead of one wide query. Cap how many run at once so you stay under your rate limit, 10 requests per second by default. This sample runs three searches, at most two at a time, and prints one citable block per query:Example output · results vary
Narrow with filters, not extra words
Usesite to restrict results to one site and published_after to get recent pages. A filter removes every page that doesn’t match it. If you set published_after, the results also leave out pages with no known publish date.
This sample keeps pages on nodejs.org published in the last 30 days:
nodejs.org:
Example output · results vary
published_after, not query_time. query_time sets only an upper bound: "7d" searches the index as it stood 7 days ago and excludes everything newer. See Date and time filters for every accepted format.
Choose the mode
Leavemode at its default, pro, unless response time matters more than depth.
Each mode is billed as its own SKU. See Credits.
In SDK 0.1.x, the Python and TypeScript clients accept only
pro. This request calls the HTTP API with realtime:
curl
mode field names the mode that served the request:
Example output · results vary
Control context size
Set the size of a search response in the request, then set the size of the prompt block when you render it. If you set neither, you get 10 results whose snippets often run to several thousand characters each.to_context() adds whole results in rank order and stops before the result that would pass max_chars. Call cited() with the same limits to get the results that the block kept.
This sample asks for 5 short results and renders them into a block of at most 1,200 characters:
Example output · results vary
Read snippets first, then fetch
Answer from the search snippets when you can. Eachsnippet is page text chosen for the query, so it often holds the answer. Fetch a page only if its snippet falls short, you need exact wording, or the user names a URL.
When you fetch, ask for the part you need. prompt makes a model read the page and return only its answer in content. max_chars caps the characters of content, which defaults to 50000. SDK 0.1.x sends neither, so this request calls the HTTP API:
curl
content holds two sentences instead of the whole page:
Example output · results vary
live and what each fetch costs.
Group an agent run with a session ID
Send the same session ID on every search and fetch in one agent run, so Keenable can group the run’s requests. Name your app on every request too, so Keenable attributes the traffic to your product.
If you send both the header and the
session_id parameter, the header wins. The keyless /public endpoints require X-Keenable-Title. The SDKs send Keenable SDK unless you set the client source.
This request tags one search with an app name and a session ID:
curl
session_id as a tool argument. See MCP.
Handle 429 and 402
On429, wait for the time in the Retry-After header, then retry the same request. See 429 Rate limit exceeded and Rate limits.
On 402, stop: your organization is out of credits, and every retry returns 402 until someone adds credits. See 402 Out of credits.
Next steps
- Search and Fetch: cost and key parameters.
- Search API reference and Fetch API reference: every parameter and response field.
- SDKs: configuration, methods and error classes.