Skip to main content
This service runs a web research agent for you. You send a question; the agent searches the web with Keenable, reads the pages it needs, and returns an answer with the URLs it cites — as text, or as JSON that matches a schema you give it. Every tool call and message the agent used is saved, so you can read how it got to the answer. It is a separate service from the Keenable MCP server, at its own URL:
There are three ways to call it: The runs API follows the shape of the Exa Agent API, so a client written for it needs few changes. It does not take input, dataSources or budget, and has no stop endpoint — use cancel. The run error field and the trajectory are additions.

Authentication

Send your Keenable API key in X-API-Key, or as Authorization: Bearer <key>, on every request. Create a key in the console — see authentication.
The agent’s web searches and page fetches are billed to that key, the same as if you made them yourself (see credits). Each run belongs to your organization; other organizations get 404 for it.

Answers

POST /v1/answer starts a run and waits for the answer:
The request and response of this endpoint use snake_case keys. Unknown fields get 400. The response has: With a schema:
With "stream": true, the response is a stream of data: items, each with a type: Without streaming, a run that does not complete returns 502; see errors.

Runs

POST /agent/runs starts a run and returns 202 with the run at once, with status queued:
The runs API uses camelCase keys. Unknown fields get 400. Poll the run until status is completed, failed or cancelled:
While a run is in progress, usage and costDollars show its state at the last saved step.

Continue a run

Pass previousRunId to ask a follow-up question. The run must be yours and completed:

Manage runs

Stream events

Add Accept: text/event-stream to the POST to get events as they happen:
When no event comes for 15 seconds, the stream sends a : keep-alive comment. If the service restarts during a run, the run continues: the stream sends agent_run.started again, and tool calls from the step that did not end can come again. GET /agent/runs/{id}/events returns the saved events as {"object": "list", "data": [{"id", "event", "data", "createdAt"}], "hasMore", "nextCursor"}. With Accept: text/event-stream, it sends the saved events and closes; it does not wait for new ones. Send Last-Event-ID: <id> to get only the events after that one.

Effort

Effort sets the maximum number of steps, the model, and how much the model reasons before each reply. A step is one model call plus up to 5 parallel tool calls. When the steps run out, the agent answers with what it found and the run’s stopReason is budget_reached. All three interfaces take the same values, and the default is auto. xhigh is no longer a level. A request with xhigh runs as high. The large model costs more per token, so costDollars.agentCompute is higher from high up. Searches and fetches cost the same at every effort level.

MCP

The MCP server is at https://agents.keenable.ai/mcp (streamable HTTP). It has three tools: agent_run returns at once. Call agent_get until the run ends.

Limits

Each organization can have 20 runs queued or running at a time. A new run past that limit gets 429; retry when one of your runs ends. The MCP agent_run tool returns the limit as a tool error.

Errors

Every error body has the same shape:
A request that fails validation also has error.detail, a list of {"loc", "msg", "type"}. A failed run has error: {"code", "message", "retryable"}. The agent_run.failed event and the streamed error item carry the same fields. If retryable is true, the same request can succeed later. GET /health needs no key. The OpenAPI schema is at /docs.