> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tako.com/llms.txt
> Use this file to discover all available pages before exploring further.

# For Your Coding Agent

> The build reference for coding agents integrating Tako's agents. Self-contained and copy-paste-ready — dispatch, poll, and stream a Search Agent or Answer Agent run; every request parameter; the run and result shapes; structured outputs; and runnable cURL, Python, and TypeScript examples. Use this page to generate a working agent integration.

This page is a complete, self-contained reference for building against **Tako's agents** — the **Search Agent** (schema-defined structured data + cards) and the **Answer Agent** (a synthesized, cited answer + cards). Every example runs after you supply an API key, every parameter is grounded in the live API, and the common mistakes are called out explicitly. For narrative introductions see the [Agents overview](/documentation/integrating-tako/agent/overview), [Search Agent](/documentation/integrating-tako/agent/search), and [Answer Agent](/documentation/integrating-tako/agent/answer).

## The two agents

Both agents share one lifecycle — dispatch a run, then poll or stream to a terminal status — and one request shape. They differ only in what the result carries.

| Agent            | Endpoint                | Returns                                                                                      |
| ---------------- | ----------------------- | -------------------------------------------------------------------------------------------- |
| **Search Agent** | `/v1/agent/search/runs` | Schema-defined `structured_output` (via `output_schema`) + `cards` + `answer` + `citations`. |
| **Answer Agent** | `/v1/agent/answer/runs` | A synthesized `answer` + `citations` + `cards`. No structured output, ever.                  |

Reach for an agent when a question needs **figuring out** rather than retrieving a known value — resolving a cohort ("which companies match…"), ranking or filtering a set by criteria, or multi-hop aggregation. For a specific, known value, time series, or direct comparison, use one-shot [Search](/documentation/integrating-tako/search/for-coding-agent) or [Answer](/documentation/integrating-tako/answer/for-coding-agent) instead — one fast, synchronous call.

A run is **asynchronous and long-running** (it can take minutes): you **dispatch** a run, then **poll** it — or **stream** it — until it reaches a terminal status.

* **Base URL:** `https://tako.com/api/`
* **Auth:** send your API key in the **`X-API-Key`** request header (not a bearer token). Create a key in the [Tako console](https://tako.com/console/api-keys).
* **Dispatch:** `POST /v1/agent/{search,answer}/runs` → **`202`** with a run object (`status: "queued"`).
* **Poll:** `GET /v1/agent/{search,answer}/runs/{run_id}` until `status` is `completed` or `failed`.
* **List:** `GET /v1/agent/{search,answer}/runs` returns the caller's runs, newest first.

<Warning>
  **Match the path to the agent.** A run dispatched at `/v1/agent/search/runs` must be polled at `/v1/agent/search/runs/{run_id}` — the search and answer collections are separate. A `thread_id` is likewise pinned to one product; a follow-up must use the same agent.
</Warning>

## Install

```bash theme={null}
pip install tako-sdk      # Python
npm install tako-sdk      # TypeScript / JavaScript
```

## Dispatch and poll

Dispatch returns immediately with a `run_id`; poll until the run is terminal. The example uses the **Search Agent** — the **Answer Agent** is identical with `client.agent.answer.*` and the `/v1/agent/answer/runs` path.

<CodeGroup>
  ```bash cURL theme={null}
  # 1. Dispatch — returns 202 with {"run_id": "...", "status": "queued"}.
  curl -X POST https://tako.com/api/v1/agent/search/runs \
    -H "X-API-Key: $TAKO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "Largest US semiconductor companies by 2024 revenue and YoY growth"
    }'

  # 2. Poll with the run_id until status is "completed" or "failed".
  curl https://tako.com/api/v1/agent/search/runs/RUN_ID \
    -H "X-API-Key: $TAKO_API_KEY"
  ```

  ```python Python theme={null}
  import os
  import time
  from tako import Configuration
  from tako.lib import Tako
  from tako.models.search_agent_run_request import SearchAgentRunRequest

  config = Configuration()
  config.api_key["apiKey"] = os.environ["TAKO_API_KEY"]
  client = Tako(config)

  run = client.agent.search.run(
      SearchAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue and YoY growth")
  )

  # Runs can take minutes — poll on a short interval until terminal.
  while run.status not in ("completed", "failed"):
      time.sleep(3)
      run = client.agent.search.get(run.run_id)

  if run.status == "completed":
      print(run.result.answer)
      for card in run.result.cards or []:
          print(card.title, card.webpage_url)
  ```

  ```typescript TypeScript theme={null}
  import { Tako } from "tako-sdk";

  const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! });

  let run = await tako.agent.search.run({
    query: "Largest US semiconductor companies by 2024 revenue and YoY growth",
  });

  // Runs can take minutes — poll on a short interval until terminal.
  while (run.status !== "completed" && run.status !== "failed") {
    await new Promise((r) => setTimeout(r, 3000));
    run = await tako.agent.search.get(run.run_id);
  }

  if (run.status === "completed") {
    console.log(run.result?.answer);
    for (const card of run.result?.cards ?? []) {
      console.log(card.title, card.webpage_url);
    }
  }
  ```
</CodeGroup>

## Stream live progress (SSE)

To show progress while an agent works, stream the run over Server-Sent Events instead of polling. Send `Accept: text/event-stream` on dispatch (or on the poll endpoint to resume). `client.agent.search.stream()` / `client.agent.answer.stream()` yield per-product envelope events (`SearchAgentStreamEnvelope` / `AnswerAgentStreamEnvelope`); the terminal `agent_result` event carries the same result you'd get from polling.

<CodeGroup>
  ```python Python theme={null}
  import os
  from tako import Configuration
  from tako.lib import Tako
  from tako.models.search_agent_run_request import SearchAgentRunRequest

  config = Configuration()
  config.api_key["apiKey"] = os.environ["TAKO_API_KEY"]
  client = Tako(config)

  req = SearchAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue and YoY growth")

  with client.agent.search.stream(req) as stream:
      for event in stream:
          block = event.block.actual_instance
          print(f"seq={event.seq} kind={block.kind}")
          if block.kind == "agent_result":
              print(block.data.answer)
      # The stream ends at stream_done. If it ended without a terminal result
      # (but produced at least one event, so run_id is known), poll for status:
      if stream.result is None and stream.run_id is not None:
          run = client.agent.search.get(stream.run_id)
          print(run.status)
  ```

  ```bash cURL theme={null}
  curl -X POST https://tako.com/api/v1/agent/search/runs \
    -H "X-API-Key: $TAKO_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Accept: text/event-stream" \
    -d '{"query": "Largest US semiconductor companies by 2024 revenue and YoY growth"}'
  ```
</CodeGroup>

Each stream envelope carries five fields:

| Field       | Type           | Description                                                  |
| ----------- | -------------- | ------------------------------------------------------------ |
| `seq`       | integer        | Monotonic resume cursor (non-contiguous — see note below).   |
| `run_id`    | string         | The run this event belongs to.                               |
| `thread_id` | string \| null | The thread the run belongs to.                               |
| `category`  | string         | Coarse event class: `content`, `activity`, or `control`.     |
| `block`     | object         | The event payload; its `kind` field selects the shape below. |

### Stream event kinds

`block.kind` is one of thirteen public kinds. Fields marked *(opt)* may be absent.

| `kind`                 | Fields                                                                                   |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| `status`               | `message`, `parent_id` *(opt)*                                                           |
| `tool_call`            | `id`, `tool`, `status_message` *(opt)*, `parent_id` *(opt)*, `done` (bool)               |
| `tool_result`          | `id`, `tool`, `elapsed_ms` (int), `link` *(opt)*, `parent_id` *(opt)*                    |
| `tool_error`           | `id`, `tool`, `error`, `parent_id` *(opt)*                                               |
| `tool_retry`           | `id`, `tool`, `error`, `elapsed_ms` (int), `parent_id` *(opt)*                           |
| `subagent`             | `agent_id`, `subagent_type`, `event` (`"dispatch"` \| `"complete"`), `parent_id` *(opt)* |
| `reasoning`            | `id`, `delta` (text chunk), `done` (bool)                                                |
| `text`                 | `id`, `delta` (text chunk), `done` (bool)                                                |
| `data_pipeline_answer` | `id`, `chart_refs` (array of strings)                                                    |
| `agent_result`         | `id`, `data` (a `SearchAgentResult` or `AnswerAgentResult` — see below)                  |
| `heartbeat`            | *(no fields — keep-alive)*                                                               |
| `stream_reset`         | *(no fields — discard buffered state and resume)*                                        |
| `stream_done`          | *(no fields — terminal; the stream ends)*                                                |

The stream terminates at `stream_done`. The structured result arrives in the terminal `agent_result` event, whose `data` is the per-product result — the same result you'd get from polling.

<Note>
  `seq` is a monotonic resume cursor, but values are **not** contiguous — the public stream omits internal events, so gaps are expected. If a stream drops, reconnect and pass the last `seq` you saw as `starting_after` (or the `Last-Event-ID` header) when reading `GET /v1/agent/{search,answer}/runs/{run_id}` with `Accept: text/event-stream`.
</Note>

## Request parameters

Both dispatch bodies share these parameters. Only `query` is required.

| Parameter         | Type                         | Default           | Description                                                                                                                                                   |
| ----------------- | ---------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query`           | string                       | — *(required)*    | The research question.                                                                                                                                        |
| `thread_id`       | string (uuid) \| null        | none → new thread | Continue a prior conversation — pass an earlier run's `thread_id`. Pinned to one product and one `source_indexes`.                                            |
| `effort`          | `"medium"`                   | `medium`          | The agents run a **single effort tier**; only `"medium"` is currently accepted.                                                                               |
| `source_indexes`  | array of `"data"` \| `"web"` | `["data", "web"]` | **Restrict** which sources the agent researches. A flat **array**, not the `sources` object used by one-shot Search/Answer. Legacy `"tako"` maps to `"data"`. |
| `locale`          | string                       | `en-US`           | A non-English locale makes the agent answer in that language.                                                                                                 |
| `timezone`        | string \| null               | none              | IANA timezone; affects card image rendering only, not the data.                                                                                               |
| `output_settings` | object \| null               | none              | `image_dark_mode` (bool, default dark).                                                                                                                       |

The **Search Agent** accepts two additional parameters:

| Parameter       | Type           | Default | Description                                                                                                                                                                             |
| --------------- | -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cards`         | boolean        | `true`  | Whether the agent may build visualization cards. Permissive: `true` means it *may*, not that it *must* (a completed run with no cards is legitimate); `false` suppresses card building. |
| `output_schema` | object \| null | none    | JSON Schema for [structured output](#structured-output-search-agent).                                                                                                                   |

The **Answer Agent** takes **no** `cards` or `output_schema` — its contract is frozen to `answer` + `cards` + `citations` + `metadata`.

<Warning>
  **Sources differ from one-shot Search/Answer.** The agents take `source_indexes` — a flat array that *restricts* sources (`["data"]`, `["web"]`, or both). One-shot Search and Answer take a `sources` **object** (`{ "data": {} }`). Don't pass one shape where the other is expected.
</Warning>

<Tip>
  **Use both sources.** `source_indexes` defaults to `["data", "web"]` — both. Leave it off so the agent researches Tako's curated data *and* the live web; set it only to deliberately *restrict* to one index.
</Tip>

```python theme={null}
# Continue a thread, and restrict to Tako's curated data:
follow_up = client.agent.search.run(
    SearchAgentRunRequest(
        query="Now break the leader's revenue down by segment",
        thread_id=run.thread_id,
        source_indexes=["data"],
    )
)
```

## Structured output (Search Agent)

Pass an `output_schema` (JSON Schema) to shape the response into machine-usable JSON in `result.structured_output`. Two kinds of field:

* **Synthesized fields** — ordinary schema properties the agent writes.
* **Dataset slots** — a property marked `"x-tako-dataset": true`, filled with the **exact retrieved rows** as a `TakoDataset`. A slot node carries only `x-tako-dataset`, an optional `description`, and an optional `columns` hint — no `type`.

Supported subset: `object` / `array` / `string` / `number` / `integer` / `boolean`, plus `enum`, `required`, `description`. Caps: **16 KB, depth 5, 64 properties, 4 dataset slots**. A schema outside these bounds returns `400 output_schema_invalid`.

<CodeGroup>
  ```python Python theme={null}
  import jsonschema
  from tako.lib import Tako, TakoDatasetView, derive_response_schema
  from tako.models.search_agent_run_request import SearchAgentRunRequest

  schema = {
      "type": "object",
      "properties": {
          "summary": {"type": "string", "description": "One-sentence summary."},
          "companies": {"x-tako-dataset": True, "columns": ["company", "revenue_usd", "yoy_growth_pct"]},
      },
      "required": ["summary"],
  }

  run = client.agent.search.run(
      SearchAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue", output_schema=schema)
  )
  run = client.agent.search.get(run.run_id)  # poll to a terminal status

  if run.result and run.result.structured_output:
      jsonschema.validate(run.result.structured_output, derive_response_schema(schema))
      view = TakoDatasetView(run.result.structured_output["companies"])
      print(view.records)          # list[dict], one per row
      print(view.to_dataframe())   # typed pandas DataFrame (needs tako-sdk[pandas])
  ```

  ```typescript TypeScript theme={null}
  import { Tako, TakoDatasetView, deriveResponseSchema } from "tako-sdk";

  const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! });

  const schema = {
    type: "object",
    properties: {
      summary: { type: "string" },
      companies: { "x-tako-dataset": true, columns: ["company", "revenue_usd", "yoy_growth_pct"] },
    },
    required: ["summary"],
  };

  const dispatched = await tako.agent.search.run({ query: "...", output_schema: schema });
  const run = await tako.agent.search.get(dispatched.run_id); // poll to a terminal status

  const output = run.result?.structured_output;
  if (output) {
    const view = new TakoDatasetView(output.companies);
    console.log(view.records);
  }
  ```
</CodeGroup>

When `output_schema` is supplied, the result adds:

| Field                         | Description                                                                                                          |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `structured_output`           | The caller-shaped JSON. Dataset slots hold a `TakoDataset` envelope of exact rows, or `null` when honestly unfilled. |
| `structured_output_status`    | `complete` \| `partial` \| `failed`.                                                                                 |
| `unfilled_fields`             | Dot-separated paths of honestly-unfilled dataset slots; present iff status is `partial`.                             |
| `structured_output_citations` | Field-path → citation-index map, joining the same `[n]` registry as the answer.                                      |
| `structured_output_error`     | An `ErrorObject` explaining why structured output failed; present iff status is `failed`.                            |

## Run lifecycle and result shape

Dispatch and poll both return the run object (`SearchAgentRun` or `AnswerAgentRun`):

```json theme={null}
{
  "run_id": "run_8sKd2m1f0aQ",
  "object": "agent.run",
  "thread_id": "1f0e9c34-2b7a-4f0a-9c2e-7b1a2c3d4e5f",
  "status": "completed",
  "created_at": "2026-06-22T18:04:11+00:00",
  "completed_at": "2026-06-22T18:06:48+00:00",
  "result": {
    "answer": "Nvidia led with about $60.9B in FY2024 revenue (+126% YoY) [1].",
    "cards": [
      {
        "card_id": "mA-C6b48zxyU3G33JJdO",
        "title": "US Semiconductor Revenue, FY2024",
        "webpage_url": "https://tako.com/card/mA-C6b48zxyU3G33JJdO/",
        "image_url": "https://tako.com/api/v1/image/mA-C6b48zxyU3G33JJdO/",
        "embed_url": "https://tako.com/embed/mA-C6b48zxyU3G33JJdO/",
        "source_indexes": ["data"]
      }
    ],
    "citations": [
      { "index": 1, "title": "US Semiconductor Revenue, FY2024", "source_name": "Company Filings (10-K)", "source_index": "data" }
    ],
    "structured_output": null,
    "request_id": "f20f965b-6bbd-40df-b50b-a8861f34df24"
  },
  "error": null
}
```

| Field       | Type           | Description                                                             |
| ----------- | -------------- | ----------------------------------------------------------------------- |
| `run_id`    | string         | The run identifier — poll with it.                                      |
| `status`    | string         | `queued` → `running` → `completed` \| `failed`.                         |
| `thread_id` | string         | The thread this run belongs to — reuse it to continue the conversation. |
| `result`    | object \| null | Populated when `status` is `completed`.                                 |
| `error`     | object \| null | An `ErrorObject` (`code`, `message`) when `status` is `failed`.         |
| `usage`     | object \| null | Credit/token usage for the run.                                         |

The `result` shape depends on the agent:

* **`SearchAgentResult`** — `answer` (markdown with `[n]` markers), `cards[]`, `citations[]`, `metadata`, `request_id`, plus the `structured_output*` fields above when `output_schema` was supplied.
* **`AnswerAgentResult`** — `answer`, `cards[]`, `citations[]`, `metadata`, `request_id`. **No `structured_output`, no inline data, no `web_results`** — ever. Prose-only (empty `cards`) is legitimate.

`citations` is a single top-level registry the answer's `[n]` markers join. Each entry has `index` and `title`; the agents populate `source_index` (`data` | `web`), and the Search Agent additionally fills `excerpt` / `publish_date` for web sources.

<Note>
  There is **no `web_results` field**. Web and Tako sources alike land in the unified top-level `citations` registry.
</Note>

## List past runs

`GET /v1/agent/{search,answer}/runs` returns the caller's runs for that agent, newest first, as a list envelope: `{ "object": "list", "data": [ … ], "has_more": bool, "next_cursor": string | null }`. Each item is a trimmed run summary (`run_id`, `status`, `created_at`, `completed_at`, `thread_id`, `usage`) — fetch full detail via the poll endpoint. Paginate with `cursor` (the prior response's `next_cursor`) and `limit` (default 20, max 100).

```bash theme={null}
curl "https://tako.com/api/v1/agent/search/runs?limit=20" \
  -H "X-API-Key: $TAKO_API_KEY"
```

## When to use which

* **Search Agent** — when you want **machine-usable structured data** out. Define an `output_schema`; get synthesized fields plus dataset slots of exact rows, alongside cards. Ideal for feeding a database, table, or downstream code.
* **Answer Agent** — when you want a **written, citation-backed answer** synthesized from everything the agent found, with the cards that support it. Ideal for a chat reply or a briefing.
* **Neither** — for a specific, known value, time series, or direct comparison, don't dispatch a run. One-shot [Answer](/documentation/integrating-tako/answer/for-coding-agent) (prose) or [Search](/documentation/integrating-tako/search/for-coding-agent) (raw cards) returns it in one fast, synchronous call.

## Common mistakes

<Warning>
  Avoid these — they are the patterns coding agents most often get wrong.
</Warning>

| Wrong                                                | Correct                                                                                    |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `Authorization: Bearer $KEY`                         | `X-API-Key: $KEY`                                                                          |
| Expecting `200` from dispatch                        | Dispatch returns **`202`** with `status: "queued"` — then poll                             |
| Reading `result` immediately                         | `result` is `null` until `status` is `completed` — poll to a terminal status               |
| Polling a search run at `/v1/agent/answer/runs/{id}` | Poll at the **same** collection you dispatched to (`/v1/agent/search/runs/{id}`)           |
| `"source_indexes": { "data": {} }` (object)          | `"source_indexes": ["data"]` (array — agents, not one-shot Search/Answer)                  |
| `"effort": "high"` or `"low"`                        | Only `"medium"` is accepted for the agents                                                 |
| Reading `result.web_results`                         | Use `result.citations` — there is no `web_results` field                                   |
| Sending `output_schema` to the Answer Agent          | `output_schema` is **Search Agent only**; the Answer Agent never returns structured output |
| Treating `seq` as contiguous                         | `seq` is a resume cursor only; gaps are expected                                           |
| Dispatching into a busy `thread_id`                  | A thread allows one in-flight run — a second dispatch returns `409`                        |
| Continuing a thread with the other agent             | A `thread_id` is pinned to one product — a mismatch returns `409 thread_product_mismatch`  |

## Errors

Failures return an `ErrorObject` body: `{ "code": "...", "message": "..." }`.

| Status | Meaning                                                                                                                                                                                             | Fix                                                                                                                |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `400`  | Malformed request (bad JSON, invalid parameter, or `output_schema_invalid`).                                                                                                                        | Validate the body against the parameters above.                                                                    |
| `401`  | Missing or invalid API key.                                                                                                                                                                         | Send a valid key in the `X-API-Key` header.                                                                        |
| `402`  | Out of credits (insufficient balance).                                                                                                                                                              | Top up credits — see [Credits & billing](/documentation/additional-resources/credits-and-billing#running-out-402). |
| `404`  | Unknown `run_id`, or a `thread_id` you don't own.                                                                                                                                                   | Use a `run_id` returned by dispatch.                                                                               |
| `409`  | Conflict — `conflict` (thread has an in-flight run), `thread_product_mismatch` (thread started on the other agent), or `source_indexes_mismatch` (a follow-up changed the thread's pinned sources). | Wait for the in-flight run, use the matching agent, or keep `source_indexes` consistent within a thread.           |
| `500`  | Server error.                                                                                                                                                                                       | Retry; if it persists, contact support with the `run_id`.                                                          |
