# Dispatch an Answer Agent run Source: https://docs.tako.com/api-reference/agent-answer-dispatch POST /v1/agent/answer/runs Dispatch an Answer Agent run — deep research returning a synthesized, cited answer The **Answer Agent** researches a natural-language question across Tako's knowledge and the live web, then returns a synthesized, opinionated `answer` (markdown) with the `citations` and `cards` that back it. Use it for open-ended questions that need reasoning across many entities. See the **[Answer Agent guide](/documentation/integrating-tako/agent/answer)** for examples, citations, streaming, threads, and sources. ## Notes * To authenticate, send your [Tako API key](https://tako.com/console/api-keys) in the `X-API-Key` header. Store it as an environment variable rather than hardcoding it. * The agent is **asynchronous**: this endpoint returns `202` with an `AnswerAgentRun` whose `status` is `queued`. Poll [Poll an Answer Agent run](/api-reference/agent-answer-poll) until `status` is `completed` or `failed`. * To stream progress live instead of polling, send `Accept: text/event-stream`. The response is then a Server-Sent Events stream of `AnswerAgentStreamEnvelope` events. See the [coding-agent reference](/documentation/integrating-tako/agent/for-coding-agent#stream-live-progress-sse). * **Frozen contract:** the Answer Agent never accepts an `output_schema` and never returns structured output or inline data — the result is `answer` + `cards` + `citations` + `metadata`. For structured data, use the [Retrieval Agent](/api-reference/agent-retrieval-dispatch). ## Choosing sources `source_indexes` is optional and defaults to `["data", "web"]` — both. Pass it only to **restrict**: `["data"]` (curated knowledge graph only) or `["web"]` (open web only). The legacy value `"tako"` is a synonym for `"data"`. ## Continuing a thread Pass the `thread_id` from a prior run to ask a follow-up in the same conversation. Omit it to start a new thread. A thread is pinned to the Answer Agent and to one set of `source_indexes`. # List Answer Agent runs Source: https://docs.tako.com/api-reference/agent-answer-list GET /v1/agent/answer/runs List the caller's Answer Agent runs, newest first ## Notes * Returns the authenticated caller's Answer Agent runs, newest first, as a list envelope: `{ "object": "list", "data": [ … ], "has_more": bool, "next_cursor": string | null }`. * Each item is a trimmed `AnswerAgentRunSummary` (`run_id`, `status`, `created_at`, `completed_at`, `thread_id`, `usage`) — fetch full detail via [Poll an Answer Agent run](/api-reference/agent-answer-poll). * Paginate with `cursor` (a prior response's `next_cursor`) and `limit` (default 20, max 100). # Poll an Answer Agent run Source: https://docs.tako.com/api-reference/agent-answer-poll GET /v1/agent/answer/runs/{run_id} Retrieve the current state and result of an Answer Agent run ## Notes * Poll with the `run_id` returned by [Dispatch an Answer Agent run](/api-reference/agent-answer-dispatch). `status` progresses `queued` → `running` → `completed` / `failed`; the `result` (an `AnswerAgentResult`) is populated once `status` is `completed`. * The result carries `answer` (markdown with `[n]` citation markers), `citations` (the registry those markers join), `cards`, and `metadata`. There is no `structured_output` and no `web_results` — a prose-only answer with no cards is legitimate. * To resume or tail the run as a live Server-Sent Events stream, send `Accept: text/event-stream`. Use `starting_after` (or the `Last-Event-ID` header) to replay only events whose `seq` is greater than a value you've already seen. See the [Answer Agent guide](/documentation/integrating-tako/agent/answer). # Dispatch a Company Agent run Source: https://docs.tako.com/api-reference/agent-company-dispatch POST /v1/agent/company/runs Dispatch a Company Agent run, which builds lists of companies and the people at them The **Company Agent** builds lists of companies and the people at them, from company registries, licensed company and people data, and the web. It returns the lists as cards with structured rows, plus a cited `answer`. See the **[Company Agent guide](/documentation/integrating-tako/agent/company)** for examples, effort tiers, contacts and threads. ## Notes * To authenticate, send your [Tako API key](https://tako.com/console/api-keys) in the `X-API-Key` header. Store it as an environment variable rather than hardcoding it. * The agent is **asynchronous**: this endpoint returns `202` with a `CompanyAgentRun` whose `status` is `queued`. Poll [Poll a Company Agent run](/api-reference/agent-company-poll) until `status` is `completed` or `failed`. * To stream progress live instead of polling, send `Accept: text/event-stream`. The response is then a Server-Sent Events stream of `RetrievalAgentStreamEnvelope` events. * The Company Agent doesn't take an `output_schema`. A request that sends one returns `400`. To get a list's rows, export its card through [Contents](/api-reference/contents). ## Effort `effort` is `low`, `medium` (the default) or `high`. `low` answers from company registries and the web only. It screens companies by status, incorporation date, location and industry, and it can't rank or filter by financial metrics. `medium` and `high` can, and `high` takes longer. A thread keeps its effort: a follow-up with a different `effort` returns `409 effort_mismatch`. ## Contacts People lists carry contact details as withheld [premium columns](/documentation/integrating-tako/contents/for-coding-agent#premium-columns). Export the card through [Contents](/api-reference/contents) with `premium` set to `true` to reveal them. ## Continuing a thread Pass the `thread_id` from a prior run to ask a follow-up in the same conversation. Omit it to start a new thread. A thread is pinned to the Company Agent, one set of `source_indexes` and one `effort`. # List Company Agent runs Source: https://docs.tako.com/api-reference/agent-company-list GET /v1/agent/company/runs List the caller's Company Agent runs, newest first ## Notes * Returns the authenticated caller's Company Agent runs, 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 with [Poll a Company Agent run](/api-reference/agent-company-poll). * Paginate with `cursor` (a prior response's `next_cursor`) and `limit` (default 20, max 100). # Poll a Company Agent run Source: https://docs.tako.com/api-reference/agent-company-poll GET /v1/agent/company/runs/{run_id} Retrieve the current state and result of a Company Agent run ## Notes * Poll with the `run_id` returned by [Dispatch a Company Agent run](/api-reference/agent-company-dispatch). `status` progresses `queued` → `running` → `completed` / `failed`. The `result` is populated once `status` is `completed`. * The result carries `answer`, `cards` and `citations`. Each list is a card: export it through [Contents](/api-reference/contents) to get its rows, with `premium` set to `true` to reveal its contacts. * To resume or tail the run as a live Server-Sent Events stream, send `Accept: text/event-stream`. Use `starting_after` (or the `Last-Event-ID` header) to replay only events whose `seq` is greater than a value you've already seen. # Dispatch a Retrieval Agent run Source: https://docs.tako.com/api-reference/agent-retrieval-dispatch POST /v1/agent/retrieval/runs Dispatch a Retrieval Agent run — agentic data retrieval returning structured outputs and cards The **Retrieval Agent** researches a natural-language question across Tako's knowledge and the live web, then returns schema-defined structured data (`structured_output`) plus visualization `cards` and a cited `answer`. Use it for questions that need real retrieval work — resolving a cohort, ranking or filtering a set, multi-hop aggregation. See the **[Retrieval Agent guide](/documentation/integrating-tako/agent/retrieval)** for examples, structured outputs, streaming, threads, and sources. ## Notes * To authenticate, send your [Tako API key](https://tako.com/console/api-keys) in the `X-API-Key` header. Store it as an environment variable rather than hardcoding it. * The agent is **asynchronous**: this endpoint returns `202` with a `RetrievalAgentRun` whose `status` is `queued`. Poll [Poll a Retrieval Agent run](/api-reference/agent-retrieval-poll) until `status` is `completed` or `failed`. * To stream progress live instead of polling, send `Accept: text/event-stream`. The response is then a Server-Sent Events stream of `RetrievalAgentStreamEnvelope` events. See the [coding-agent reference](/documentation/integrating-tako/agent/for-coding-agent#stream-live-progress-sse). ## Structured output Pass an `output_schema` (JSON Schema) to get machine-usable JSON in `result.structured_output`. Mark a property with `"x-tako-dataset": true` to request a **dataset slot**, filled with exact retrieved rows. Supported subset: object/array/string/number/integer/boolean, enum, required, description. Caps: 16 KB, depth 5, 64 properties, 4 dataset slots — violations return `400 output_schema_invalid`. ## Choosing sources `source_indexes` is optional and defaults to `["data", "web"]` — both. Pass it only to **restrict**: `["data"]` (curated knowledge graph only) or `["web"]` (open web only). The legacy value `"tako"` is a synonym for `"data"`. ## Continuing a thread Pass the `thread_id` from a prior run to ask a follow-up in the same conversation. Omit it to start a new thread. A thread is pinned to the Retrieval Agent and to one set of `source_indexes`. # List Retrieval Agent runs Source: https://docs.tako.com/api-reference/agent-retrieval-list GET /v1/agent/retrieval/runs List the caller's Retrieval Agent runs, newest first ## Notes * Returns the authenticated caller's Retrieval Agent runs, newest first, as a list envelope: `{ "object": "list", "data": [ … ], "has_more": bool, "next_cursor": string | null }`. * Each item is a trimmed `RetrievalAgentRunSummary` (`run_id`, `status`, `created_at`, `completed_at`, `thread_id`, `usage`) — fetch full detail via [Poll a Retrieval Agent run](/api-reference/agent-retrieval-poll). * Paginate with `cursor` (a prior response's `next_cursor`) and `limit` (default 20, max 100). # Poll a Retrieval Agent run Source: https://docs.tako.com/api-reference/agent-retrieval-poll GET /v1/agent/retrieval/runs/{run_id} Retrieve the current state and result of a Retrieval Agent run ## Notes * Poll with the `run_id` returned by [Dispatch a Retrieval Agent run](/api-reference/agent-retrieval-dispatch). `status` progresses `queued` → `running` → `completed` / `failed`; the `result` (a `RetrievalAgentResult`) is populated once `status` is `completed`. * The result carries `answer`, `cards`, `citations`, and — when the request supplied an `output_schema` — the `structured_output*` fields. There is no `web_results` field; web and Tako sources alike land in the top-level `citations` registry. * To resume or tail the run as a live Server-Sent Events stream, send `Accept: text/event-stream`. Use `starting_after` (or the `Last-Event-ID` header) to replay only events whose `seq` is greater than a value you've already seen. See the [Retrieval Agent guide](/documentation/integrating-tako/agent/retrieval). # Answer Source: https://docs.tako.com/api-reference/answer POST /v1/answer Get a written answer grounded in Tako knowledge cards and the web ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Answer retrieves the same results as [Search](/api-reference/search-v3) and adds a written `answer` grounded in them. It takes the **same request body** as Search — the response adds the synthesized `answer` alongside the cards and web results. * Use Answer when you want prose to show a user; use [Search](/api-reference/search-v3) when you only need the underlying cards and web results. > [!NOTE] > > **Answer replaces the legacy Grounding endpoint.** New integrations should use `POST /v1/answer`. ## Choosing sources `sources` controls where the answer is grounded — an object whose keys are the sources to ground in. A source is used only if its key is present; omit `sources` to use the default (both sources, 5 results each): * `{ "data": {} }` — grounded in Tako's curated knowledge graph. * `{ "web": {} }` — grounded in the live web. * `{ "data": {}, "web": {} }` — both (same as omitting `sources`). The legacy value `tako` is accepted as a synonym for `data`. Each source takes optional per-source settings: `count` (1–20, default 5) and `include_contents` (inline the underlying data). # Contents Source: https://docs.tako.com/api-reference/contents POST /v1/contents Download the data behind a search result ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Contents downloads the data behind a result returned by [Search](/api-reference/search-v3) or [Answer](/api-reference/answer). Pass the result's URL and Tako returns a short-lived download URL — a CSV of a Tako card's data, or the extracted text of any other web page. * You don't pass a `format`; Tako detects the right content from the URL and the response reports what it produced. > [!NOTE] > > A result is downloadable when it carries a `content` descriptor in the Search or Answer response. Use that result's URL here — a card's `webpage_url` or a web result's `url`. > [!WARNING] > > **Don't read a large export into a model's context.** A card export can hold up to 100,000 rows, and at about 15 tokens per row, 10,000 rows cost about 150,000 tokens. Write the export to a file, on disk or in memory, and have your agent parse it with code. Give the model only the rows or results it needs. To cap the export instead, set `max_rows`. # Graph Node Source: https://docs.tako.com/api-reference/graph-node GET /v1/graph/node/{id} Look up a single node in Tako's data graph by its id ## Notes * **Authenticated, no credits** — send the same `X-API-Key` header as every Tako endpoint. Graph calls consume no credits; rate limits are 180/minute and 10,000/day. * Use graph node to **confirm what a node id actually refers to.** Given an id — from a [Search](/api-reference/search-v3) or [Answer](/api-reference/answer) card, from [Graph Search](/api-reference/graph-search), or from [Graph Related](/api-reference/graph-related) — resolve it here to get the node's full `name`, `aliases`, and `description` and verify it's the entity or metric you expected before you rely on it. * The `id` is the opaque public id Tako returns everywhere it exposes a node. The same id works across all graph endpoints. > [!WARNING] > > **A resolved node describes what Tako knows about that id** — it does not promise that any specific entity + metric combination exists, or that data is available for it. Use [Graph Related](/api-reference/graph-related) to see what a node connects to, and confirm coverage at [Search](/api-reference/search-v3) or [Answer](/api-reference/answer). ## Disambiguation flow The node id is the through-line that ties search results back to the graph. A typical loop: 1. Run a [Search](/api-reference/search-v3) or [Answer](/api-reference/answer) query and read the node ids off the card's `nodes` array. 2. Resolve each id here to confirm the card was built from the entities and metrics you meant — compare `name`, `aliases`, and `label`. The names themselves settle homonyms: `China` the country and `China, TX` are distinct nodes with distinct ids. 3. If a node isn't the one you wanted, resolve the intended one with [Graph Search](/api-reference/graph-search) instead, then re-run the query pinned to that id via `sources.data.node_ids`. ## Path format The path takes the full opaque id, so **URL-encode it** — ids contain `::`. The trailing slash is required; without it Tako responds `301` to the canonical path, which clients only follow when told to (`curl -L`). ```bash theme={null} # ent::china::f9a471b8, percent-encoded curl "https://tako.com/api/v1/graph/node/ent%3A%3Achina%3A%3Af9a471b8/" \ -H "X-API-Key: $TAKO_API_KEY" ``` ## Example response ```json theme={null} { "id": "ent::china::f9a471b8", "type": "entity", "name": "China", "aliases": ["CHN", "China PR", "Chinese"], "label": "GPE" } ``` > [!NOTE] > > A `404` means the id does not resolve to a node — either it's malformed, it uses an unknown prefix, or no such node exists. # Graph Related Source: https://docs.tako.com/api-reference/graph-related GET /v1/graph/related Explore the nodes connected to a metric or entity in Tako's data graph ## Notes * **Authenticated, no credits** — send the same `X-API-Key` header as every Tako endpoint. Graph calls consume no credits; rate limits are 180/minute and 10,000/day. * Start from a `node_id` returned by [Graph Search](/api-reference/graph-search), then use graph related to **find which entity + metric combinations Tako actually covers.** Once you've found a covered combination, query [Search](/api-reference/search-v3) (`/v3/search`) or [Answer](/api-reference/answer) (`/v1/answer`) for it — pinning the ids via `sources.data.node_ids`. * Results in each group are **ranked by popularity, blended with how strongly each node matches `q`** when a filter is provided. On big entities, always pass `q` — unfiltered, a big entity returns hundreds of items. ## Response shape: overview vs. one group The response always includes the resolved `node`. The rest depends on whether you paginate: * **No `relation`** → `relations` holds an ordered **overview**: one group per relation, each `{key, kind, label, items (preview), total, total_capped}`. Named semantic edges come first, then membership, data, and siblings; empty groups are dropped. * **`relation=` set** → `relation` holds a single paginated page for that one group, with `items`, `total`, `total_capped`, and a `next_cursor`. Pass `next_cursor` back as `cursor` to page through. Items live in `relation.items`. Groups by `kind`: | `kind` | Keys | Meaning | | - | - | - | | `related` | `rel:` — e.g. `rel:competes_with`, `rel:subsidiaries_of`, `rel:in_industry` | Named semantic edges — the richest signal for entities. Keys are per-node: read the overview to discover them, then drill. | | `membership` | `part_of`, `members` | Cohort/geo membership (Apple `part_of` FAANG; a league's `members`). | | `data` | `metrics`, `entities` | Table co-reference — the metrics/entities sharing datasets with this node. | | `sibling` | `siblings` | Same-type peers — the class namespace ("Other Companies"), not a curated cohort. | > [!NOTE] > > An unknown `relation` key returns `200` with empty `items`, not an error — so a typo'd key reads as "no relation." Keys are per-node: read the overview to discover them, then drill. ## Narrowing with `q` The optional `q` is a case-insensitive substring filter on related nodes' names/aliases. It filters **every group** of the overview, or the single paginated group when `relation` is set: ```bash theme={null} # Metrics available for an entity, matching "gdp" curl "https://tako.com/api/v1/graph/related?node_id=$NODE_ID&relation=metrics&q=gdp" \ -H "X-API-Key: $TAKO_API_KEY" ``` `label`/`infer_label` boosts work as on [Graph Search](/api-reference/graph-search) and apply within each group; inference runs only when `q` is set. ## Capped totals `total` stops counting at **250**: `total_capped: true` means the true count is at least `total` — render it as "250+". Pagination also ends at the cap, so narrow with `q` to reach the tail. > [!NOTE] > > A `404` means the `node_id` does not resolve. A node with **no relations** returns `200` with empty groups — that's the normal "nothing connected" outcome, not an error. ## Example response (overview) ```json theme={null} { "node": { "id": "ent::nvidia_corporation::5ea55992", "type": "entity", "name": "NVIDIA Corporation", "subtype": "Companies", "label": "ORG" }, "relations": [ { "key": "rel:competes_with", "kind": "related", "label": "Competes with", "items": [/* GraphNode previews */], "total": 179, "total_capped": false }, { "key": "rel:subsidiaries_of", "kind": "related", "label": "Subsidiaries of", "items": [/* ... */], "total": 24, "total_capped": false }, { "key": "part_of", "kind": "membership", "label": "Part of", "items": [/* ... */], "total": 2, "total_capped": false }, { "key": "metrics", "kind": "data", "label": "Related Metrics", "items": [/* ... */], "total": 250, "total_capped": true }, { "key": "entities", "kind": "data", "label": "Entities", "items": [/* ... */], "total": 62, "total_capped": false }, { "key": "siblings", "kind": "sibling", "label": "Other Companies", "items": [/* ... */], "total": 250, "total_capped": true } ] } ``` # Graph Search Source: https://docs.tako.com/api-reference/graph-search GET /v1/graph/search Resolve a metric or entity in Tako's data graph by name ## Notes * **Authenticated, no credits** — send the same `X-API-Key` header as every Tako endpoint. Graph calls consume no credits; rate limits are 180/minute and 10,000/day. * Use graph search to **discover and confirm what data exists before you query it.** Resolve the metric and the entity you care about here, then ask [Search](/api-reference/search-v3) (`/v3/search`) or [Answer](/api-reference/answer) (`/v1/answer`) for that specific entity + metric combination — and pin the resolved ids via `sources.data.node_ids`. * Each result is a `GraphNode` whose `id` you can pass straight to [Graph Related](/api-reference/graph-related) to explore what it connects to. > [!WARNING] > > **A returned metric or entity is a guide, not a guarantee** — it does not promise that a specific entity + metric combination exists, or that data is available for it. Use it to steer what you ask [Search](/api-reference/search-v3) or [Answer](/api-reference/answer) for, and confirm coverage there. ## Scoping with `types` `types` is a comma-separated list of facets to return. Omit it to search both: * `metric` — a measure (e.g. "GDP", "stock price"). * `entity` — a thing the metric is tracked for (e.g. a country, a company). ## Steering the ranking with `label` Many names are homonyms — "Air China" contains a country's name, so the airline competes with China itself in the results. Pass `label` to bias the ranking toward one entity category. It is a **boost, not a filter** — matching nodes rank higher, but off-label nodes still return: ```bash theme={null} # The airline, not the country curl "https://tako.com/api/v1/graph/search?q=air%20china&label=ORG" \ -H "X-API-Key: $TAKO_API_KEY" ``` * Values: `PERSON`, `ORG`, `GPE`, `LOC`, `PRODUCT`, `EVENT`, `LANGUAGE`, `MONEY`, `METRIC`, `STOCK_TICKER`, `WEBSITE`. Sports teams are `ORG`. An unknown value is a `400`. * **Auto-inference is on by default** (`infer_label=true`): with no explicit `label`, Tako detects the entities in `q` and applies the matching boost. The labels it found come back in `inferred_labels` — an empty list means inference ran and found nothing; the key is absent when inference didn't run. Disable with `infer_label=false` (also `0`/`no`/`off`); passing an explicit `label` also disables inference. * Each node reports its own `label` and `subtype` (e.g. `Countries`, `Companies`) in the response — read them to pick the right node rather than assuming the list is filtered. > [!NOTE] > > A `q` shorter than 2 characters returns an empty `results` list (`200`, not an error), and `limit` above 50 is clamped rather than rejected. ## Example response The response to the `label=ORG` call above — the airline now outranks the country: ```json theme={null} { "results": [ { "id": "ent::air_china_limited::6af29d2b", "type": "entity", "name": "Air China Limited", "aliases": ["Air China", "Air China Ltd.", "CCA"], "description": "Air China Limited, together with its subsidiaries, provides air passenger, air cargo, and airline-related services…", "subtype": "Companies", "label": "ORG" } ] } ``` Results are ordered by a relevance + popularity blend; fields that are null are omitted from the response. # List monitor types Source: https://docs.tako.com/api-reference/monitor-types GET /v1/monitor_types Every monitor type you can create, with the JSON Schema its parameters must satisfy > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Start here. Tako validates a monitor's `parameters` against its type's schema, so read the type before you call [Create a monitor](/api-reference/monitors-create). This response is the authoritative list, and a type added since this page was written appears only in the response. * Each item returns `parameters_schema`, the JSON Schema that `parameters` must satisfy; `description`, what the type watches and when it fires; `lookback_seconds`, how far back an occurrence may have happened and still fire; `subject_schema`, the shape of the resolved subject the type returns; and `fire_once_only`. * `firing_enabled` reports whether this environment evaluates monitors. While it's `false`, [Create a monitor](/api-reference/monitors-create) still accepts a monitor and the monitor stays `active`, but nothing evaluates it and nothing fires. ## The registered types Type names are namespaced `domain.subtype`. | Type | Fires when | | - | - | | `stocks.pct_change` | A stock's move over your `window` reaches `threshold` percent in `direction`. Tako measures the move from the reference close at the start of the window, not from the price when you created the monitor. | | `stocks.crosses` | A stock's price closes on the far side of your `level` from the prior session's close. A close that lands exactly on the level hasn't passed it. | | `stocks.new_52w_extreme` | A stock passes the highest or lowest daily close of the 365 days before the current session, in `direction`. The price has to pass the extreme; matching it doesn't fire. | | `crypto.crosses` | A crypto asset's price in USD reaches your `level` in `direction`. It fires once. If the latest snapshot has already reached the level, the create fails, so the monitor always arms on the near side. | | `forex.crosses` | A currency pair's rate reaches your `level` in `direction`. The level is in the quote currency per one unit of the base, so a EUR/USD level of 1.15 is 1.15 US dollars per euro. Tako compares against the closing mid-rate, and the feed runs up to 30 minutes behind. It fires once. If the rate has already reached the level, the create fails. | | `sports.game_start` | A game starts, the first time the feed reports it in play. Tako stamps the firing at the scheduled start, so a monitor you create after that time never fires for that game, even when the real start was delayed. | | `sports.game_result` | A game ends. On a team, it fires only when the result matches your `outcome`, by at least `margin` when you set one. On a whole game, it fires with the winner and accepts neither `outcome` nor `margin`. A tie matches neither outcome. | | `sports.score_event` | A score rises while a game is live or final. On a team, only that team's scoring fires it. On a whole game, either side's does. NBA teams and games aren't accepted. | | `sports.odds_crosses` | A game's betting line at the book you choose reaches your `level` in `direction`. | > [!NOTE] > > `stocks.pct_change` and `stocks.new_52w_extreme` are **state alerts**. If you create one while its condition already holds, the next price update fires it. On a delayed feed, that update arrives as late as the feed's delay. ## Naming a subject Each family names its subject a different way. | Type | Subject fields | | - | - | | `stocks.*` | `ticker`, plus `exchange` as an ISO 10383 MIC when the ticker is listed on more than one exchange. | | `crypto.crosses` | `ticker`, or `coinmarketcap_id` to identify an asset outside the top-ranked set. Tako rejects an asset ranked outside the top 1000. | | `forex.crosses` | `base` and `quote`, each an ISO 4217 currency code, such as `EUR` and `USD`. | | `sports.game_start`, `sports.game_result`, `sports.score_event` | `game_id`, `team_id`, or both. | | `sports.odds_crosses` | `game_id` and `team_id`, both required. | For the three team types, the two ids combine: * `game_id` alone watches the whole game, and a firing reports both sides. * `team_id` alone follows that team's games as they come. * Both together watch that team's side of that one game. Both ids come from [List sports games](/api-reference/sports-games): a row's `id` is the game id, and its `home.id` and `away.id` are team ids. ## What a firing sends Each type sends its own set of `payload` fields to your notification channels. For the fields, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries#payload-fields-by-monitor-type). # Create a monitor Source: https://docs.tako.com/api-reference/monitors-create POST /v1/monitors Create a condition on data that posts to your notification channels when it occurs > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * `type` is the name of a registered monitor type, and Tako validates `parameters` against that type's schema. Call [List monitor types](/api-reference/monitor-types) first, and build `parameters` from the type's `parameters_schema`. * Every `channel_ids` entry must be a channel you own, so [create the channel](/api-reference/notification-channels-create) before the monitor. An id you don't own returns `404`. * `channel_ids` is optional. A monitor with no channels records its firings, and you read them with [List a monitor's firings](/api-reference/monitors-firings). To add channels later, call [Update a monitor](/api-reference/monitors-update). * `name` is your own label. Tako never fills one in, and never reads it. * `fire_once` defaults to `true`, which pauses the monitor after its first firing. To fire on every new occurrence, set it to `false`. A type that reports `fire_once_only` rejects that. * `active_from` defaults to the moment you create the monitor. `active_until` must be in the future and after `active_from`. * You can hold a limited number of active monitors. When you're at that limit, this endpoint returns `400` with `error_type: LIMIT_REACHED`. See [Handle a rejected create](#handle-a-rejected-create). * When the monitor fires, Tako posts to every channel you listed. For the request body, the headers, and how to verify the signature, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). > [!WARNING] > > **A monitor never fires for something that already happened.** A firing needs an occurrence that happens after you create the monitor, so you can't use a monitor to search history. Each type's `lookback_seconds` in [List monitor types](/api-reference/monitor-types) says how far back an occurrence may have happened and still fire, and its `description` says how long its source takes to report one. ## Check the resolved subject The response holds `subject`: the subject the type matched from your parameters, such as the listing a ticker matched. Read it and confirm the monitor watches what you meant. Tako resolves the subject once, when you create the monitor, and never again. [Get a monitor](/api-reference/monitors-get) and [List monitors](/api-reference/monitors-list) return the same `subject`. `parameters` comes back in the form this endpoint accepts, with names as Tako resolved them. A ticker you sent as `aapl` reads back as `AAPL`, with the exchange that Tako picked. ## Handle a rejected create A `400` returns a `MonitorCreateRejected` with an `error_type`: * `AMBIGUOUS_SUBJECT`: your parameters match more than one subject. `candidates` lists every one, best first. Narrow the parameters and retry. * `LIMIT_REACHED`: you're at your active monitor limit. `limit` is your cap, and `error_message` says how to free a slot. Pause or delete a monitor you no longer need, or contact [support@tako.com](mailto:support@tako.com) about an enterprise limit. * `BAD_REQUEST`: anything else. `candidates` is empty and `limit` is `null`. Tako counts the active monitor limit per user, not pooled across an account. A monitor past its `active_until` doesn't count against it. # Delete a monitor Source: https://docs.tako.com/api-reference/monitors-delete DELETE /v1/monitors/{id} Permanently remove a monitor and every firing it recorded > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * A `204` means Tako deleted the monitor and every firing it recorded. There's no undo, and no endpoint returns a deleted monitor's history. * To stop a monitor and keep that history, [pause it](/api-reference/monitors-update) instead. * An `id` you don't own returns `404`. # List a monitor's firings Source: https://docs.tako.com/api-reference/monitors-firings GET /v1/monitors/{id}/firings Page through the occurrences a monitor has fired for, and how each delivery went > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Returns firings newest first. `limit` is 1–100 and defaults to 20. To get the next page, pass a response's `next_cursor` back as `cursor`. * `occurred_at` is when the occurrence happened, and `fired_at` is when Tako fired for it. The gap between them is however long the source took to report the occurrence. For each type's reporting delay, see its `description` in [List monitor types](/api-reference/monitor-types). * `occurrence_key` is what Tako deduplicates on. Tako records one firing per key, so a source that reposts the same occurrence doesn't fire twice. * `payload` holds the values that caused the firing. Tako sends the same object to your channels under `payload`, and its fields depend on the monitor type. For the fields each type sends, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). * `deliveries` records what happened on each channel. After 2 consecutive failed deliveries, Tako pauses the channel. See [Pause or resume a notification channel](/api-reference/notification-channels-update). * A delivery's `state` is `channel_paused` when the channel was paused, so Tako didn't send a request. `failed` means the delivery ended without a `2xx`, including a delivery that expired before Tako sent it. Read `attempts` and `last_status_code` to tell them apart. * `deliveries` covers the last 30 days. An older firing lists none. * Tako removes firings older than the monitor type's `lookback_seconds`. # Get a monitor Source: https://docs.tako.com/api-reference/monitors-get GET /v1/monitors/{id} Read one monitor's configuration, status, and last evaluation > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * An `id` you don't own returns `404`, whether or not that monitor exists for someone else. * `status_reason` says why Tako paused the monitor. It's `null` while the monitor is active, and `null` when you paused it yourself. * `last_evaluated_at` is when Tako last evaluated the monitor, and `last_evaluation_outcome` is how that evaluation ended. Both are `null` until the first evaluation, and the outcome is `null` again after you resume a paused monitor. * `parameters` comes back in the form [Create a monitor](/api-reference/monitors-create) accepts, with names as Tako resolved them. `subject` names what the monitor watches, such as the listing a ticker matched. Tako resolves it once, at create, and never again. * To see what the monitor has fired for, call [List a monitor's firings](/api-reference/monitors-firings). # List monitors Source: https://docs.tako.com/api-reference/monitors-list GET /v1/monitors Page through the monitors belonging to your API key, newest first > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Returns your monitors only, newest first. `limit` is 1–100 and defaults to 20. To get the next page, pass a response's `next_cursor` back as `cursor`. A `null` `next_cursor` means you're on the last page. * `status_reason` says why Tako paused a monitor. It's `null` while the monitor is active, and `null` when you paused it yourself. * `last_evaluation_outcome` is `null` until the first evaluation, and `null` again after you resume a paused monitor, because the pause makes the earlier outcome meaningless. An active monitor with a recent `last_evaluated_at` and no firings is running normally, and waiting for its occurrence. * Each row holds `subject`, the subject the type matched from your parameters when you created the monitor. # Update a monitor Source: https://docs.tako.com/api-reference/monitors-update PATCH /v1/monitors/{id} Pause or resume a monitor, or replace its notification channels > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Send `status`, `channel_ids`, or both. No other field changes after you create a monitor. To change a monitor's type, parameters, or window, [delete it](/api-reference/monitors-delete) and create a new one. * `channel_ids` replaces the monitor's channels with the list you send. An empty list keeps the monitor recording firings, and delivers them to no channel. Every id must be a channel you own. * If the monitor's `active_until` has already passed, resuming returns `400`, because the monitor is expired rather than paused. Create a new monitor with a new window. * Resuming sets `last_evaluation_outcome` back to `null`, because the pause makes the earlier outcome meaningless. * If resuming would put you over your active monitor limit, this endpoint returns `400` with `error_type: LIMIT_REACHED` and a `limit` giving your cap. Pause or delete another monitor first. A monitor's own paused slot never blocks its resume. * Pausing keeps the monitor and its firings. When you want to keep the history, pause the monitor instead of [deleting it](/api-reference/monitors-delete). # Create a notification channel Source: https://docs.tako.com/api-reference/notification-channels-create POST /v1/notification_channels Register an https destination that monitors post to when they fire > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Create the channel before the monitor that uses it. [Create a monitor](/api-reference/monitors-create) returns `404` for a `channel_ids` entry you don't own. * `url` must use `https`, on port 443 or 8443, and must resolve to a public address. See [What Tako accepts as a URL](/documentation/integrating-tako/monitors/webhook-deliveries#what-tako-accepts-as-a-url). * A channel starts `active`. After 2 consecutive failed deliveries, Tako pauses it. See [Pause or resume a notification channel](/api-reference/notification-channels-update). * You can hold a limited number of channels, counted per user rather than pooled across an account. When you're at that limit, this endpoint returns `400` with `error_type: LIMIT_REACHED` and a `limit` giving your cap. Delete a channel you no longer need, or contact [support@tako.com](mailto:support@tako.com) about an enterprise limit. * For the request Tako posts to this channel, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). > [!WARNING] > > **Store `secret` when you read the `201` response.** No later request returns it. Tako signs every delivery to this channel with it, so if you lose it, delete the channel and create a new one. ## Choosing a `kind` `kind` decides how Tako shapes the request body it posts: * `webhook`, the default: nested JSON that namespaces the monitor, the firing, and the payload. * `slack_workflow`: a flat body of strings, for a Slack Workflow Builder trigger, which rejects nested values. For both shapes, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). # Delete a notification channel Source: https://docs.tako.com/api-reference/notification-channels-delete DELETE /v1/notification_channels/{id} Permanently remove a notification channel and its signing secret > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * A `204` means Tako deleted the channel and its signing secret. There's no undo, and a new channel to the same URL gets a new secret. * **A monitor that posted only to this channel keeps running with no destination.** It still evaluates and records firings, and those firings reach no one. To find the monitors left without a channel, call [List monitors](/api-reference/monitors-list), then [pause](/api-reference/monitors-update) or delete the ones you no longer want. * To stop deliveries and keep the channel and its secret, [pause the channel](/api-reference/notification-channels-update) instead. * An `id` you don't own returns `404`. # Get a notification channel Source: https://docs.tako.com/api-reference/notification-channels-get GET /v1/notification_channels/{id} Read one notification channel's destination and delivery status > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * An `id` you don't own returns `404`. * This response leaves out `secret`, and so does every request except [create](/api-reference/notification-channels-create). Use this endpoint to check where a channel points and whether it's still delivering. * `status_reason` is `delivery_failures` when Tako paused the channel after 2 consecutive failed deliveries, and `null` when the channel is active or when you paused it yourself. For what counts as a failed delivery, see [How Tako retries](/documentation/integrating-tako/monitors/webhook-deliveries#how-tako-retries). # List notification channels Source: https://docs.tako.com/api-reference/notification-channels-list GET /v1/notification_channels Page through the notification channels belonging to your API key, newest first > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Returns your channels only, newest first. `limit` is 1–100 and defaults to 20. To get the next page, pass a response's `next_cursor` back as `cursor`. * No row returns `secret`. Tako returns a channel's signing secret once, from [Create a notification channel](/api-reference/notification-channels-create). * A `status` of `paused` with `status_reason: delivery_failures` means Tako paused the channel after 2 consecutive failed deliveries. A `paused` channel with a `null` `status_reason` is one you paused yourself. For what counts as a failed delivery, see [How Tako retries](/documentation/integrating-tako/monitors/webhook-deliveries#how-tako-retries). # Pause or resume a notification channel Source: https://docs.tako.com/api-reference/notification-channels-update PATCH /v1/notification_channels/{id} Stop or restart deliveries to a notification channel > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * `status` is the only field this endpoint takes. Set `paused` to stop deliveries, or `active` to resume them. * After 2 consecutive failed deliveries, Tako pauses the channel and sets `status_reason` to `delivery_failures`. Fix the destination, then resume the channel, which clears the consecutive-failure count. * Pausing a channel doesn't pause the monitors that post to it. They keep evaluating and recording firings, and those firings don't reach this destination. To stop a monitor, [pause the monitor](/api-reference/monitors-update). * For what counts as a failed delivery, see [How Tako retries](/documentation/integrating-tako/monitors/webhook-deliveries#how-tako-retries). # OpenAPI Specification Source: https://docs.tako.com/api-reference/openapi-spec The OpenAPI specification for the Tako API. The full OpenAPI specification for the Tako API is available as YAML: [openapi.yaml](/api-reference/openapi.yaml) # Search Source: https://docs.tako.com/api-reference/search-v3 POST /v3/search Search Tako knowledge cards and the web with a natural language query ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Search returns Tako knowledge cards and web results for a natural language query — the results themselves, with no written answer on top. Use it when you want to render or post-process the cards and web results directly. * Want a written answer over the same results? Use [Answer](/api-reference/answer) — it takes the same request body and adds a synthesized `answer`. ## Choosing sources `sources` controls where results come from — an object whose keys are the sources to search. A source is searched only if its key is present; omit `sources` to use the default (both sources, 5 results each): * `{ "data": {} }` — Tako knowledge cards from the curated knowledge graph. * `{ "web": {} }` — web results. * `{ "data": {}, "web": {} }` — both (same as omitting `sources`). The legacy value `tako` is accepted as a synonym for `data`. Each source takes optional per-source settings: `count` (1–20, default 5) and `include_contents` (inline the underlying data). > [!NOTE] > > A result whose underlying data is downloadable includes a `content` descriptor. Pass that result's URL to [Contents](/api-reference/contents) to download it — a CSV for a Tako card, or extracted text for a web page. # List sports games Source: https://docs.tako.com/api-reference/sports-games GET /v1/sports/games Find the game and team ids that sports monitors take as subjects > [!WARNING] > > **The sports endpoints are experimental.** They supply monitor subjects, and their shapes can change without a deprecation window. ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * This endpoint is the subject lookup for sports monitors. Find the game or the team here, then pass its id to [Create a monitor](/api-reference/monitors-create). * Games come back soonest first. `limit` defaults to 100 and maxes at 500. To get the next page, pass a response's `next_cursor` back as `cursor`. * Filter with `league`, a league id such as `NBA` or `EPL`, which is case-insensitive, and with `team`, a team id, which matches games where the team plays on either side. * Rate limits are 60 requests a minute and 5,000 a day, per user. * `home` and `away` are `null` for events without teams, such as F1 and NASCAR. `title` is the only label those rows carry. ## Which id goes where | Field | Pass it as | To | | - | - | - | | `id` | `game_id` | Any sports monitor. `sports.odds_crosses` requires it. | | `home.id`, `away.id` | `team_id` | Any sports monitor. `sports.odds_crosses` requires it. | `sports.game_start`, `sports.game_result`, and `sports.score_event` take either id, or both: `game_id` alone watches the whole game, `team_id` alone follows that team's games, and both together watch that team's side of that one game. For the full rule, see [Naming a subject](/api-reference/monitor-types#naming-a-subject). > [!WARNING] > > **Only a `live_event` row's `id` works as a `game_id`.** A row whose `source` is `schedule_table` is an FBS or FCS football game that only the football schedule table records. Its `id` belongs to that table, no live document exists for it, and no monitor accepts it. Only `live_event` rows can carry odds. ## Setting the window `from` and `to` are ISO 8601, and Tako reads a naive value as UTC. They default to one day back and seven days ahead, which is the whole range the schedule covers. A window outside that range returns `400`. `status` defaults to `scheduled,live`, because you can't monitor a finished game. To include the games that finished inside the one-day tail, pass `final`. ## Reading `odds_available` `odds_available` is `true` when the game document already holds a market. Prematch odds arrive inside a per-sport window, so a game further out reports `false` until that window opens. You can create a monitor either way, so a `false` here is no reason to wait. # Thin-Viz: Create Card Source: https://docs.tako.com/api-reference/thinviz-direct-create POST /v1/thin_viz/create/ Create an embeddable card directly from component configurations ## Notes * To authenticate, you'll need a [Tako API key](https://tako.com/console/api-keys). It's best practice to store it as an environment variable to avoid hardcoding sensitive credentials in your code. * Creates a card in a single API call with full component configurations. No schema step is required. * The response includes `embed_url`, `image_url`, and `webpage_url` for embedding the card. * If no `title` is provided, it falls back to the title from the first `header` component, or defaults to "Card". * See the [Thin-Viz Guide](/documentation/integrating-tako/chart-creation/overview) for full examples and available component types. * **For large datasets (>1MB):** Use gzip compression to send payloads up to 10x larger. * **Preview images for ZDR cards:** Set `image_ttl_minutes` (1–1440) to make a temporary preview image available via `image_url`. The image can be downloaded on demand with query params (`dark_mode`, `width`, `height`, `hideFooter`) until the TTL expires, after which the endpoint returns `410 Gone`. # Credits & billing Source: https://docs.tako.com/documentation/additional-resources/credits-and-billing How Tako meters API usage — credits, the per-item cost field, pay-as-you-go billing, and what a 402 means. Every Tako API call consumes **credits**. This page explains how usage is metered, how to read the `cost` fields on responses, and what happens when you run out. For current per-endpoint rates, see the [Tako pricing page](https://tako.com/pricing). ## Credits Tako meters API usage in credits: each request to Search, Answer, the Agent, and Contents deducts credits when it is served. Billing is **pay-as-you-go**: your account holds a credit balance, and each call draws down that balance based on the endpoint and the work it performs (for example, a `deep`-effort Search costs more than the default call). ## The `cost` field Some responses report a **`cost`** in US dollars so you can see what a piece of work cost: * **Search / Answer** — each downloadable card carries a `content` descriptor with a `cost` (USD) for that card's inlined content. * **Contents** — each `ContentItem` carries a `cost` (USD) for that download. Retrieving card data via inline contents or the Contents API incurs a data surcharge. A card CSV export bills a flat `$0.001` per export plus a per-row rate that starts at `$0.01` per 1,000 rows and is higher for some licensed sources; web-page text extraction bills `$0.001` per page. Revealing premium columns, such as the contact details on a people list, adds a charge per distinct person or company revealed. Read the exact per-card rate from `export_pricing` before you fetch, and see [Contents pricing](/documentation/integrating-tako/contents/pricing) for the formula and worked examples. A `cost` of `0.0` is rare: it means the item was genuinely free — an empty payload, or an account whose row charges are waived. A card export you actually receive rows from is never `0.0`, because the per-export baseline always applies. These fields report the USD cost of the underlying work; your credit balance is drawn down accordingly. > [!NOTE] > > When your account is billed pay-as-you-go, the request total is > **`usage.total_cost_usd`** — always present on a metered response, and always > the sum of the components beside it. `usage.data` is only the inline-data > component and is omitted entirely when a request inlined none. On accounts > that are not metered per request the `usage` key is **absent** (not `null`); > sum the per-item `cost` values instead. ## Running out (`402`) When your account is out of credits, API calls return **`402`** (insufficient balance). Top up your balance from your [Tako account](https://tako.com/console), then retry — requests succeed again once your balance is positive. # Quickstart Source: https://docs.tako.com/documentation/getting-started/quickstart Get a Tako API key and build with Tako's four core APIs — Agent, Answer, Search, and Contents — over real-time, source-grounded data. [Why do developers use Tako? →](/documentation/getting-started/what-is-tako/overview) > [!NOTE] > > For AI agents: Use [llms.txt](https://docs.tako.com/llms.txt) for a full index of all documentation. ## Add Tako to your agent The fastest way to build with Tako. Paste one prompt into your coding agent to wire Tako into an existing project, or create a new Tako agent from the console. **Build a Tako agent from scratch** > **[Create a Tako-powered agent](https://tako.com/console/build)** > > Follow the agent quickstart in the console. It generates a prompt for your coding agent in seconds to build with the Tako APIs. **Add to your agent** Add Tako to an existing agent Paste this prompt into Claude Code, Cursor, or any coding agent. It reads the docs, asks you the right questions, evaluates where Tako fits in your stack, and wires it in with best practices. ```text theme={null} I want to integrate Tako into this project. Tako serves live financial, macroeconomic, sports, and company data as citation-backed answers and interactive charts. Its surfaces: Search (ranked knowledge cards with charts), Answer (one grounded, written answer), Contents (full data rows / page text), a deep-research Agent, a Data Graph (free coverage checks), and chart creation from my own data. Step 1: Read the docs before changing anything. - Fetch https://docs.tako.com/llms.txt. It indexes every documentation page. - From that index, read the quickstart plus the pages that match my stack and use case: the integration guide for my framework (OpenAI Responses, Anthropic tool calling, Vercel AI SDK, OpenAI-compatible), the MCP server page, and the relevant per-feature "for coding agents" references. Step 2: Ask me these questions one at a time and wait for my answers. 1. What does this project/agent do, and what kinds of data questions should Tako handle for it? (financial, macro, sports, company, web traffic…) 2. What framework or runtime is it built on? (Vercel AI SDK, OpenAI Agents SDK / Responses API, Anthropic API, LangChain, raw MCP client, plain backend, something else) 3. Quick answers or deep research? Single grounded answers and searches are one call; the Agent API dispatches long-running multi-step research. 4. Is a search or data tool already wired in (Exa, Tavily, Brave, built-in web search)? Should Tako replace it or run alongside it? 5. Do I need the data itself (full rows, CSV export via Contents) or charts built from my own data? 6. Where do secrets live in this project? Assume the key is TAKO_API_KEY unless I say otherwise. Step 3: Evaluate and propose before writing code. - Recommend the integration surface that fits my answers, and say why: the hosted MCP server (https://mcp.tako.com/mcp: zero glue code, connection-level auth, opt-in tools via ?tools=) vs. the Python/TypeScript SDKs or REST API (typed control, streaming, retries) vs. a provider-native tool-calling guide from the docs. Prefer the simplest surface that covers the use case. - Show me a short plan first (files to change, tools to expose, prompts to update) and wait for my OK. Step 4: Integrate with best practices. - Wire Tako in the idiomatic way for my framework. Never hardcode the key. Read it from the environment. - Add tool-choice guidance to the agent's system prompt: Answer for one specific data question that needs a conclusion; Search fanned out in parallel, one entity + one metric per query, when the agent needs the evidence; the Data Graph / available-data check as a free pre-check of what coverage exists; Contents for the full rows behind an exportable result; the Agent API for questions that need figuring out. - Do not remove or rename existing tools unless I chose to replace one. If Tako replaces a provider, migrate its call sites and prompts, and tell me exactly what changed. Step 5: Verify. - Make one free coverage-check call (available data / Data Graph) and one real search, show me the results, and summarize what you integrated and why. If TAKO_API_KEY is not set, tell me to sign up at https://tako.com (free credits included) and create a key at https://tako.com/console/api-keys. ``` ## Installation Tako ships official SDKs for **Python** and **TypeScript / JavaScript**. Every endpoint is also a single `POST`, so any other language works over the [REST API](#make-your-first-request) directly. ```bash Python theme={null} pip install tako-sdk ``` ```bash TypeScript / JavaScript theme={null} npm install tako-sdk ``` ```bash Vercel AI SDK theme={null} npm install @takoviz/ai-sdk ai @ai-sdk/openai ``` ```bash MCP theme={null} https://mcp.tako.com/mcp ``` For one-click MCP setup in Cursor, VS Code, and Kiro, see the [MCP server](/documentation/integrations/mcp-server) page. For ChatGPT and Codex, install from the [OpenAI plugin directory](/documentation/integrations/chatgpt). ## Get your API key **1. Sign up for free credits** New to Tako? [Sign up](https://tako.com) and get free credits to start building. **2. Create an API key** Create an API key from your [Tako console](https://tako.com/console/api-keys). **3. Store it as an environment variable** Store it as an environment variable — for example `TAKO_API_KEY` — rather than hardcoding it. ## Pick the right tool Each API is a single call over the same curated, real-time data. Here's what each one is for. > **[Answer](/documentation/integrating-tako/answer/overview)** > > You want the question answered, not the data. > > `"Should I wear a jacket in New York this week?"` > > **Returns** — a written answer, with the sources behind it. > **[Search](/documentation/integrating-tako/search/overview)** > > You want the data itself, and you'll do the rest. > > `"Nvidia quarterly revenue"` > > **Returns** — knowledge cards and web results, no LLM in the loop. > **[Contents](/documentation/integrating-tako/contents/overview)** > > You have a result already, and you need what's under it. > > `"https://tako.com/card/…"` > > **Returns** — that card's rows as CSV, or a web page as clean text. > **[Agent](/documentation/integrating-tako/agent/overview)** > > The question needs real work before anything can answer it. > > `"Which of the 20 largest US semiconductor companies grew revenue fastest since 2015?"` > > **Returns** — a cited brief from an asynchronous research job. Already have your own data? **[Thin-Viz](/documentation/integrating-tako/chart-creation/overview)** turns it into interactive, embeddable Tako cards. Want to see what data is available? The **[Data Graph](/documentation/integrating-tako/data-graph/overview)** lets you explore the entities and metrics Tako covers, then pin exactly what you find into Search. It consumes no credits, so it's the cheapest way to find out what Tako has before you query it. ## Make your first request Pick the use case closest to what you're building. Each is a single call over the same real-time data — the code and the live result below both change to match. **Stock prices** **Search** returns fast, structured knowledge cards for a query — no LLM in the loop. Ideal for pulling live prices you render or compute on yourself. > **Code** > > ```bash cURL theme={null} > curl -X POST https://tako.com/api/v3/search \ > -H "X-API-Key: $TAKO_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{ > "query": "What are today'\''s top performing stocks?" > }' > ``` > > ```python Python theme={null} > import os > from tako import Configuration, SearchRequest > from tako.lib import Tako > > config = Configuration() > config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] > client = Tako(config) > > results = client.search(SearchRequest(query="What are today's top performing stocks?")) > for card in results.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! }); > > const results = await tako.search({ query: "What are today's top performing stocks?" }); > for (const card of results.cards ?? []) { > console.log(card.title, card.webpage_url); > } > ``` > **Result** > > A knowledge card — a full data series with its sources, ready to render or compute on. > > [https://tako.com/embed/Egb55l06wATm44FH0yQk/?dark\_mode=false](https://tako.com/embed/Egb55l06wATm44FH0yQk/?dark_mode=false) **Sports** **Answer** returns one written, source-attributed answer plus the Tako cards that back it — in a single call, no retrieval to wire up. > **Code** > > ```bash cURL theme={null} > curl -X POST https://tako.com/api/v1/answer \ > -H "X-API-Key: $TAKO_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{ > "query": "How has Luka Dončić'\''s points per game trended this season?" > }' > ``` > > ```python Python theme={null} > import os > from tako import Configuration, SearchRequest > from tako.lib import Tako > > config = Configuration() > config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] > client = Tako(config) > > response = client.answer(SearchRequest(query="How has Luka Dončić's points per game trended this season?")) > print(response.answer) > for card in response.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! }); > > const response = await tako.answer({ query: "How has Luka Dončić's points per game trended this season?" }); > console.log(response.answer); > for (const card of response.cards ?? []) { > console.log(card.title, card.webpage_url); > } > ``` > **Result** > > **Answer:** Luka Dončić is averaging 33.5 points per game this season. His monthly PPG has fluctuated — 38.2 in October, 32.5 in November, 31.0 in December, 34.0 in January, 27.0 in February, and back up to 37.5 in March. > > [https://tako.com/embed/-Bau8k8h5Kzag3zivqMG/?dark\_mode=false](https://tako.com/embed/-Bau8k8h5Kzag3zivqMG/?dark_mode=false) **Research** The **[Answer Agent](/documentation/integrating-tako/agent/answer)** is an asynchronous deep-research agent for multi-step questions — resolving a cohort, then ranking it across many entities — returning cited prose. Dispatch a run, then poll until it's done. Want the cards rather than the write-up? Use the **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** (`/v1/agent/retrieval/runs`) instead. > **Code** > > ```bash cURL theme={null} > # 1. Dispatch — returns 202 with {"run_id": "...", "status": "queued"}. > curl -X POST https://tako.com/api/v1/agent/answer/runs \ > -H "X-API-Key: $TAKO_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{ > "query": "Which of the Magnificent Seven tech companies has grown revenue the fastest over the past five years?" > }' > > # 2. Poll with the run_id until status is "completed" or "failed". > curl https://tako.com/api/v1/agent/answer/runs/RUN_ID \ > -H "X-API-Key: $TAKO_API_KEY" > ``` > > ```python Python theme={null} > import os > import time > from tako import Configuration, AnswerAgentRunRequest > from tako.lib import Tako > > config = Configuration() > config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] > client = Tako(config) > > run = client.agent.answer.run( > AnswerAgentRunRequest(query="Which of the Magnificent Seven tech companies has grown revenue the fastest over the past five years?") > ) > > # Runs can take minutes — poll on a short interval until terminal. > while run.status not in ("completed", "failed"): > time.sleep(3) > run = client.agent.answer.get(run.run_id) > > print(run.result.answer) > ``` > > ```typescript TypeScript theme={null} > import { Tako } from "tako-sdk"; > > const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! }); > > let run = await tako.agent.answer.run({ > query: "Which of the Magnificent Seven tech companies has grown revenue the fastest over the past five years?", > }); > > // 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.answer.get(run.run_id); > } > > console.log(run.result?.answer); > ``` > **Result** > > **Answer:** NVIDIA grew revenue the fastest over the past five years — from \$16.7B in 2020 to \$215.9B in 2025, a five-year CAGR of 66.9%. The rest of the Magnificent Seven trailed well behind: Tesla at 24.6%, Meta at 18.5%, Alphabet at 17.2%, Microsoft at 14.5%, Amazon at 13.2%, and Apple at 8.7%. > > [https://tako.com/embed/U\_ahQOm3DZ4WJEH4SSDv/?dark\_mode=false](https://tako.com/embed/U_ahQOm3DZ4WJEH4SSDv/?dark_mode=false) **Build an agent** Prime an agent with **exact, source-grounded numbers**: **Search** finds the card, then **Contents** downloads the exact series behind it as CSV — so your agent computes on real values instead of prose. Those two calls are the whole loop. Resolving through the **Data Graph** first is an optional upgrade, shown as step 1 below. It trades one extra call for deterministic retrieval: instead of hoping a text query lands on the right card, you resolve the entity and metric to node ids and pin them, so Search can only return cards built from exactly those nodes. Graph calls consume no credits. > [!NOTE] > > **Want to skip it?** Start at step 2 and send just the query — `{"query": "Palantir federal contract obligations"}` finds this same card without any graph calls. Drop the whole `sources` block when you do: `strict: true` requires a non-empty `node_ids` list and returns a `400` on its own. > **Code** > > ```bash cURL theme={null} > # 1. OPTIONAL: resolve the entity and the metric to graph node ids (no credits). > ENTITY_ID=$(curl -s -G https://tako.com/api/v1/graph/search \ > -H "X-API-Key: $TAKO_API_KEY" \ > --data-urlencode "q=Palantir Technologies" \ > --data-urlencode "types=entity" | jq -r '.results[0].id') > > METRIC_ID=$(curl -s -G https://tako.com/api/v1/graph/search \ > -H "X-API-Key: $TAKO_API_KEY" \ > --data-urlencode "q=federal contract obligations" \ > --data-urlencode "types=metric" | jq -r '.results[0].id') > > # 2. Pin both ids into Search. strict returns only cards matching a pinned node. > CARD_URL=$(curl -s -X POST https://tako.com/api/v3/search \ > -H "X-API-Key: $TAKO_API_KEY" \ > -H "Content-Type: application/json" \ > -d "{ > \"query\": \"Palantir federal contract obligations\", > \"sources\": {\"data\": {\"node_ids\": [\"$ENTITY_ID\", \"$METRIC_ID\"], \"strict\": true}} > }" | jq -r '.cards[0].webpage_url') > > # 3. Contents downloads the exact numbers behind the card. > curl -X POST https://tako.com/api/v1/contents \ > -H "X-API-Key: $TAKO_API_KEY" \ > -H "Content-Type: application/json" \ > -d "{\"url\": \"$CARD_URL\", \"mode\": \"inline\"}" > ``` > > ```python Python theme={null} > import os > from tako import Configuration, SearchRequest, Sources, DataSourceSettings, ContentsRequest, GraphNodeType > from tako.lib import Tako > > config = Configuration() > config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] > client = Tako(config) > > # 1. OPTIONAL: resolve the entity and the metric to graph node ids (no credits). > entity = client.graph_search("Palantir Technologies", types=[GraphNodeType.ENTITY]).results[0] > metric = client.graph_search("federal contract obligations", types=[GraphNodeType.METRIC]).results[0] > > # 2. Pin both ids into Search. strict returns only cards matching a pinned node. > results = client.search( > SearchRequest( > query="Palantir federal contract obligations", > sources=Sources( > data=DataSourceSettings(node_ids=[entity.id, metric.id], strict=True) > ), > ) > ) > card = results.cards[0] > > # 3. Contents downloads the exact numbers behind it — feed the CSV to your agent. > contents = client.contents(ContentsRequest(url=card.webpage_url, mode="inline")) > print(contents.contents[0].data) > ``` > > ```typescript TypeScript theme={null} > import { Tako, GraphNodeType } from "tako-sdk"; > > const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! }); > > // 1. OPTIONAL: resolve the entity and the metric to graph node ids (no credits). > const entity = (await tako.graphSearch({ q: "Palantir Technologies", types: [GraphNodeType.Entity] })).results[0]; > const metric = (await tako.graphSearch({ q: "federal contract obligations", types: [GraphNodeType.Metric] })).results[0]; > > // 2. Pin both ids into Search. strict returns only cards matching a pinned node. > const results = await tako.search({ > query: "Palantir federal contract obligations", > sources: { data: { node_ids: [entity.id, metric.id], strict: true } }, > }); > const card = results.cards![0]; > > // 3. Contents downloads the exact numbers behind it — feed the CSV to your agent. > const contents = await tako.contents({ url: card.webpage_url!, mode: "inline" }); > console.log(contents.contents![0].data); > ``` > [!NOTE] > > If you do pin, resolve the ids at request time as above — never hardcode them. Ids are not durable across knowledge-graph rebuilds; Search silently skips one that no longer resolves, and with `strict: true` that leaves you with no cards. > **Result** > > **Step 1, optional** — **Graph Search** resolves each name to a node: an `ent::` id for the company, an `mt::` id for the metric. Two calls, one `results[0]` each: > > ```json theme={null} > // q=Palantir Technologies&types=entity > {"id": "ent::palantir_technologie::1d41d233", "type": "entity", "name": "Palantir Technologies Inc.", "subtype": "Companies", "label": "ORG"} > > // q=federal contract obligations&types=metric > {"id": "mt::federal_contract_obl::1e937180", "type": "metric", "name": "Federal Contract Obligations", "label": "METRIC"} > ``` > > **Search**, pinned to those two nodes, returns the card they describe — Palantir's federal contract obligations by fiscal year. The plain query returns this same card; pinning is what guarantees it: > > [https://tako.com/embed/IbVGstkzEIUt8nwY4G\_1/?dark\_mode=false](https://tako.com/embed/IbVGstkzEIUt8nwY4G_1/?dark_mode=false) > > **Contents** hands your agent the exact series behind it, as CSV — ready to compute on: > > ```csv theme={null} > Timestamp,Federal Contract Obligations (USD) > 2008-10-01 00:00:00+00:00,135211.06 > 2009-10-01 00:00:00+00:00,4378216.96 > 2010-10-01 00:00:00+00:00,5801023.5 > ... > 2024-10-01 00:00:00+00:00,541197885.14 > 2025-10-01 00:00:00+00:00,1020566208.97 > 2026-10-01 00:00:00+00:00,896657523.63 > ``` ## Next steps > **[Developer Playground](https://tako.com/playground)** > > No setup required. Try a simple query like “Nvidia earnings” or a long-form prompt to see what Tako returns. > **[What is Tako?](/documentation/getting-started/what-is-tako/overview)** > > Learn more about Tako’s key concepts and functionality. # Data Sources Source: https://docs.tako.com/documentation/getting-started/what-is-tako/data-sources Tako generates Knowledge Cards using data from three main sources: * The Tako Index (our structured knowledge graph) * Web Search * Developer-provided data ### 1. Tako Index Tako’s Index is built with structured data we source exclusively from authoritative providers. We define “authoritative” by asking: *“Would people with opposing views still rely on this source to make key financial, strategic, or policy decisions?”* If the answer is yes, we consider that source credible and worth including. Sources range in type and motivation: * **Public & Government Sources** publish data to fulfill open data requirements often enshrined in policy or law (e.g. U.S. Bureau of Labor Statistics for inflation data). * **Think Tanks, NGOs, and Academic Institutions** publish data as part of their public service obligations (e.g. Stockholm International Peace Research Institute for military spending). * **Traditional Data licensors & Information Services Companies** make money by selling raw data and/or sell subscriptions to apps for viewing data (e.g. S\&P Global for earning estimates, Similarweb for web traffic data). * **Non-Traditional Data Providers** generate valuable data as an incidental byproduct of their core business, then publish it to drive awareness (e.g. Redfin for US real estate trends). Every card built from the Tako Index includes citations, methodologies, and As Of times that indicate when the data was last checked. If something looks wrong or unclear, we want to hear from you. **Current coverage:** We currently focus on finance, macroeconomics, geopolitics, sports, weather, and companies and the people at them - but we're expanding. If there's a dataset you want us to support, please let us know. ### 2. Web Search The web offers a vast amount of information. Sometimes, it’s the best available source for answering a knowledge question. But it also includes data that can be biased, outdated, or unreliable. In cases where authoritative data isn’t available or the best fit, a well-qualified web answer can still be better than no answer at all. To handle web-sourced information, Tako generates Citation Cards - a special type of knowledge card. These cards use language models to summarize relevant web content into a concise, shareable format. Each Citation Card includes clear source links and caveats so users can assess credibility and decide how to use the information. You get web sources back two ways: as **web search results** in [Search](/documentation/integrating-tako/search/overview) and [Answer](/documentation/integrating-tako/answer/overview), and as the **full extracted contents of a page** via [Contents](/documentation/integrating-tako/contents/overview). ### 3. Bring Your Own Data — Custom Index (Beta) For enterprise customers, we support building private, custom indexes so you can surface them in your natural language apps and workflows (e.g. internal chatbot). This enables semantic search, analysis, and knowledge card generation over your proprietary data. # Knowledge Cards Source: https://docs.tako.com/documentation/getting-started/what-is-tako/knowledge-cards Knowledge cards are compact, interactive representations of structured information. They’re designed to make complex data easy to understand, embed, and share - ideal for both human users and AI agents. We often refer to them as “atomic units of knowledge” or “mini encyclopedias.” Each card is generated based on the semantic meaning of a user’s prompt and includes the most relevant data we can retrieve. Cards are composed of elements like charts, tables, text, navigation, sourcing, and methodology information. Enterprise customers can theme cards so they look more native in their products. ### Example A query like “AMD vs. Nvidia headcount since 2013” produces a card that pulls Full-Time Employee counts from S\&P and renders a side-by-side time series chart, complete with interactive date selection. [https://tako.com/embed/geYufvSx04pyWX7GpHeR/](https://tako.com/embed/geYufvSx04pyWX7GpHeR/) Tako supports a wide range of card formats like financial line charts, professional sports schedules, and M\&A transaction tables. # Overview Source: https://docs.tako.com/documentation/getting-started/what-is-tako/overview Tako is the knowledge layer for agents, grounding your AI with the most accurate proprietary data and web content. Tako grounds your agent in two kinds of knowledge at once: **proprietary structured data** from trusted providers, and **full web search**. One API returns typed values with named sources when curated data exists, and agent-ready web results and page text for everything else. That combination is the point. A pure data API misses most questions, and a pure web search API makes your agent do the reading. Traditional search APIs stop at a ranked list of links. Your agent still has to open each page, parse it, reconcile numbers that disagree, and hope nothing went stale. That works for reading the news. It breaks down when the answer has to be right. Ask Tako a question and you get a synthesized, source-attributed answer, or dispatch a deep-research agent. Both are grounded in [knowledge cards](/documentation/getting-started/what-is-tako/knowledge-cards): typed data with named sources and methodology, pulled straight from providers across finance, macroeconomics, geopolitics, sports, weather, and companies and the people at them. When the answer lives on the open web instead, the same call covers it: web results in [Search](/documentation/integrating-tako/search/overview), blended into [Answer](/documentation/integrating-tako/answer/overview), and full page text through [Contents](/documentation/integrating-tako/contents/overview). Your agent never crawls a page. > [!TIP] > > **Structured data** has a defined shape: typed fields, values, and units (think rows and columns, or JSON), each with named sources attached, so your model can read it directly. *Unstructured* data is the opposite: raw web pages and text your model has to crawl, parse, and interpret before it can trust a single number. ## Tako vs. traditional search APIs The same question, asked both ways. Example query · Who will win the 2026 FIFA World Cup? Tako curated data + live web [https://tako.com/embed/YR8r8yLaFl-hK0c2KyWw/?dark\_mode=false](https://tako.com/embed/YR8r8yLaFl-hK0c2KyWw/?dark_mode=false) One [knowledge card](/documentation/getting-started/what-is-tako/knowledge-cards): live win probabilities with their named source and methodology. Your agent reads the values directly, or embeds the chart as is. Traditional search & LLM-search APIs raw web text espn.com › soccer › fifa-world-cup 2026 World Cup: Latest odds and favorites to win Updated odds and expert predictions for the 2026 FIFA World Cup, with the leading contenders and dark horses. oddschecker.com › football › world-cup › winner World Cup 2026 Winner Odds & Betting Compare 2026 World Cup outright winner odds across bookmakers, refreshed throughout the tournament. bbc.com › sport › football › world-cup World Cup 2026 predictions: Who will lift the trophy? BBC Sport pundits give their picks for the 2026 World Cup and rate every contender's chances. sportingnews.com › soccer › world-cup-2026 World Cup 2026 favorites: Ranking the contenders Ranking the teams most likely to win the 2026 World Cup, from clear favorites to long shots. forbes.com › betting › world-cup-2026-odds 2026 FIFA World Cup Odds: France and Argentina lead A look at the current title odds for the 2026 World Cup and where the betting value lies. goal.com › en-us › world-cup World Cup 2026: Who are the favorites to win? Everything you need to know about the 2026 World Cup favorites, contenders, and outsiders. *Your agent still has to open each link, parse the page, reconcile numbers that disagree, and hope they're current. Every step is glue code you write, run, and spend tokens on.* | | Tako | Traditional search & LLM-search APIs | | - | - | - | | **What comes back** | Typed, structured data: values, units, and time series | Cleaned web text: links, snippets, or page chunks | | **Source attribution** | A named data provider and methodology, per value | A URL the text was scraped from | | **Coverage** | Proprietary data providers plus full web search | Ranked open web only | | **Output** | An interactive knowledge card, ready to render | Raw text your model still has to parse | | **Post-processing** | None. Data arrives typed and structured | Crawl, parse, dedupe, and reconcile it yourself | | **Correctness** | Authoritative by named source | Ranked by relevance. You verify it | Want the deeper comparison, with benchmarks against the major web search APIs? Read [Tako vs. web search APIs](https://tako.com/tako-vs-web-search-apis/). ## How Tako works > **[Trusted knowledge, directly indexed](/documentation/getting-started/what-is-tako/data-sources)** > > Tako pulls high-quality, structured data across domains like finance, macroeconomics, geopolitics, sports, weather, and companies and the people at them. Much of it changes fast and can't be crawled reliably, so we go straight to the source. > **[Full web search, agent ready](/documentation/integrating-tako/contents/overview)** > > When the answer lives on the open web, Tako searches it for you and returns results and full page text your model can use directly. No crawler, no parser, no glue code. > **[Knowledge cards, interactive and shareable](/documentation/getting-started/what-is-tako/knowledge-cards)** > > Results come back as knowledge cards: visual, interactive representations of the retrieved data. Each card is built from the meaning of your query and drops straight into stories, apps, or workflows. > **[Search, built for analysts and agents](/documentation/integrating-tako/search/overview)** > > Ask in natural language and get the relevant cards back. Embed the engine in your application and your users get real-time, trusted data without leaving it. ## Who it's for > **For users** > > We remove the grunt work required to turn your questions into persuasive, evidence-backed answers. > **For AI developers** > > We make your results more accurate, real-time, citable, and engaging. # Answer Agent Source: https://docs.tako.com/documentation/integrating-tako/agent/answer A deep-research agent that synthesizes multiple sources into an opinionated, cited answer backed by insightful visualizations. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/agent/for-coding-agent) ## Capabilities The **Answer Agent** is a deep-research agent that **synthesizes multiple sources into an opinionated, cited answer backed by insightful visualizations**. Hand it a hard, open-ended question — one you have to *figure out* rather than look up — and it plans and researches across Tako's curated knowledge graph and the live web, then writes a synthesized answer with the cards and citations that back it. > **Synthesized, opinionated answer** > > Not a list of results — a written `answer` (markdown) that reasons across everything it found and takes a position, the way a good analyst would. > **Cited and attributable** > > Every `[n]` marker in the answer joins a top-level `citations` registry covering both Tako and web sources, so each claim is traceable. `metadata` adds the definitions, assumptions, and methodology behind the answer. > **Backed by visualizations** > > The answer ships with the Tako `cards` that support it — interactive knowledge cards ready to embed alongside the prose. > **Asynchronous and long-running** > > Dispatch a run, then poll or stream it to completion (a run can take minutes) — real multi-step reasoning in the background instead of racing a single call. For a specific, known value or a one-line answer over a single retrieval, use one-shot [Answer](/documentation/integrating-tako/answer/overview) instead — it returns in one fast call. For machine-usable structured data rather than prose, use the [Retrieval Agent](/documentation/integrating-tako/agent/retrieval). ## Example [Try in Playground](https://tako.com/console/playground/agent/answer) Dispatch a run, then poll `GET /v1/agent/answer/runs/{run_id}` until `status` is `completed` or `failed`: **cURL** ```bash theme={null} # 1. Dispatch — returns 202 with an AnswerAgentRun: {"run_id": "...", "status": "queued"}. curl -X POST https://tako.com/api/v1/agent/answer/runs \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "How have American Airlines margins held up against fuel shocks?" }' # 2. Poll with the run_id until status is "completed" or "failed". curl https://tako.com/api/v1/agent/answer/runs/RUN_ID \ -H "X-API-Key: YOUR_API_KEY" ``` **Python** ```bash theme={null} pip install tako-sdk ``` ```python theme={null} import os import time from tako import Configuration from tako.lib import Tako from tako.models.answer_agent_run_request import AnswerAgentRunRequest config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) run = client.agent.answer.run( AnswerAgentRunRequest(query="How have American Airlines margins held up against fuel shocks?") ) # Runs can take minutes — poll on a short interval until terminal. while run.status not in ("completed", "failed"): time.sleep(3) run = client.agent.answer.get(run.run_id) if run.status == "completed": print(run.result.answer) for citation in run.result.citations or []: print(f"[{citation.index}]", citation.title, citation.url) ``` > **Example API Response** > > A completed run. `result.answer` is markdown with `[n]` citation markers; `result.citations` is the registry those markers join; `result.cards` (if any) back the answer. Prose-only answers with no cards are legitimate. > > ```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": "American Airlines' operating margin has stayed thinner than its network peers through recent fuel spikes: fuel is roughly a quarter of operating cost, so the 2022 run-up compressed margins into the low single digits before recovering in 2023–24 [1][2].", > "cards": [ > { > "card_id": "mA-C6b48zxyU3G33JJdO", > "title": "American Airlines Operating Margin vs. Jet Fuel Price", > "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": "American Airlines Operating Margin vs. Jet Fuel Price", > "source_name": "Company Filings (10-K)", > "source_index": "data" > } > ], > "metadata": null, > "request_id": "f20f965b-6bbd-40df-b50b-a8861f34df24" > }, > "error": null > } > ``` ## Citations The `answer` is markdown carrying inline `[n]` markers. Each marker joins `result.citations` — a single top-level registry that covers Tako **and** web sources alike. Every citation has an `index` and `title`; the Answer Agent also populates `source_index` (`data` or `web`) at launch. Markers map to citations, not one-to-one — a citation may back the answer without a surviving inline marker. `result.metadata`, when present, carries the answer's `definitions`, `assumptions`, and `methodology` (with citations lifted out into the registry). > [!NOTE] > > **Frozen contract.** The Answer Agent never returns structured output or inline data — the result is `answer` + `cards` + `citations` + `metadata`, and nothing else. If you need machine-usable structured data, use the [Retrieval Agent](/documentation/integrating-tako/agent/retrieval)'s `output_schema`. To download the data behind a card, pass its URL to [Contents](/documentation/integrating-tako/contents/overview). ## Choosing sources `source_indexes` controls where the agent researches. It defaults to `["data", "web"]` — both — so pass it only to **restrict**: * `["data", "web"]` (default) — Tako's curated knowledge graph **and** the live web. * `["data"]` — the curated knowledge graph only. * `["web"]` — the live web only. The legacy value `"tako"` is accepted as a synonym for `"data"`. ## Continue a thread Each run belongs to a thread. Pass a prior run's `thread_id` to ask a follow-up in the same conversation: ```python theme={null} follow_up = client.agent.answer.run( AnswerAgentRunRequest(query="How does that compare to Delta over the same period?", thread_id=run.thread_id) ) ``` Omit `thread_id` to start a fresh thread. A thread is pinned to one agent product and one set of `source_indexes`. ## Stream live progress To show progress while the agent works, stream the run over Server-Sent Events instead of polling — send `Accept: text/event-stream` on dispatch. `client.agent.answer.stream()` yields `AnswerAgentStreamEnvelope` events; the terminal `agent_result` event carries the same `AnswerAgentResult` you'd get from polling. See the [coding-agent reference](/documentation/integrating-tako/agent/for-coding-agent#stream-live-progress-sse) for the full event taxonomy and resume semantics. > [!TIP] > > Stream for live progress in interactive UIs; poll for simple server-to-server flows. Both return the same `AnswerAgentResult` when the run completes. # Company Agent Source: https://docs.tako.com/documentation/integrating-tako/agent/company A list-building agent that finds companies and the people at them, and returns them as structured rows. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/agent/for-coding-agent) ## Capabilities The **Company Agent** builds lists of companies and the people at them. It starts from structured company and people data (company registries, licensed company and people profiles) and reads the web only to fill gaps. Use it for prospecting and research lists: "CISOs at American hospitals", "UK fintechs incorporated since 2020", "the executive officers at four airlines". > **Lists as structured rows** > > Each list comes back as a card whose rows you can export through [Contents](/api-reference/contents): names, roles, companies, locations and LinkedIn URLs. > **Contacts you pay for only when you want them** > > Contact details come back as withheld [premium columns](/documentation/integrating-tako/contents/for-coding-agent#premium-columns). Review the list first, then reveal the contacts you want with a premium export. > **Grounded and citable** > > The written `answer` carries `[n]` markers that join a top-level `citations` registry. > **Asynchronous** > > Dispatch a run, then poll or stream it to completion. Most runs finish in about a minute. For a written, synthesized answer, use the [Answer Agent](/documentation/integrating-tako/agent/answer). For schema-defined structured output over Tako's knowledge graph, use the [Retrieval Agent](/documentation/integrating-tako/agent/retrieval). ## Example [Try in Playground](https://tako.com/console/playground/agent/company) Dispatch a run, then poll `GET /v1/agent/company/runs/{run_id}` until `status` is `completed` or `failed`: ```bash theme={null} # 1. Dispatch. Returns 202 with a CompanyAgentRun: {"run_id": "...", "status": "queued"}. curl -X POST https://tako.com/api/v1/agent/company/runs \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "Show me the executive officers at Delta, United, American and Southwest." }' # 2. Poll with the run_id until status is "completed" or "failed". curl https://tako.com/api/v1/agent/company/runs/RUN_ID \ -H "X-API-Key: YOUR_API_KEY" ``` ## Effort `effort` picks the tier. Omit it on a new thread and Tako uses `medium`. * **`low`** answers from company registries and the web only. It screens companies by status, incorporation date, location and industry, and resolves named companies and people. It can't rank or filter by financial metrics such as revenue or headcount. Ask it for one and it answers the part it can and says which part it couldn't. * **`medium`** (default) and **`high`** can use every source. `high` uses a stronger model and a larger budget, and always takes longer. A thread keeps its effort. A follow-up that sends a different `effort` returns `409 effort_mismatch`, so start a new thread to change it. ## Get the rows and reveal contacts Each list in `result.cards` is a card. Export its rows through [Contents](/api-reference/contents) with the card's `webpage_url`: ```bash theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tako.com/card/CARD_ID/", "content_format": "csv" }' ``` Contact cells are withheld in that export. To reveal them, send the same request with `"premium": true`; [Premium columns](/documentation/integrating-tako/contents/for-coding-agent#premium-columns) covers which columns are premium, how they bill, and paging a long list with `offset` and `max_rows`. ## Structured output The Company Agent doesn't take an `output_schema` yet. A request that sends one returns `400`. Export a list's card through Contents to get its rows as data. ## Continue a thread Each run belongs to a thread. Pass a prior run's `thread_id` to ask a follow-up in the same conversation, for example "Now add their heads of IT". Omit `thread_id` to start a fresh thread. A thread is pinned to the Company Agent, one set of `source_indexes` and one `effort`. ## Stream live progress Send `Accept: text/event-stream` on dispatch to stream the run over Server-Sent Events instead of polling. The stream carries `RetrievalAgentStreamEnvelope` events, and the terminal `agent_result` event carries the same result you'd get from polling. See the [coding-agent reference](/documentation/integrating-tako/agent/for-coding-agent#stream-live-progress-sse) for the event taxonomy and resume semantics. > [!NOTE] > > The Python and TypeScript SDKs don't have a Company Agent method yet. Call the endpoint over HTTP until they do. # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/agent/for-coding-agent The build reference for coding agents integrating Tako's agents. Self-contained and copy-paste-ready — dispatch, poll, and stream a Retrieval 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 **Retrieval 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), [Retrieval Agent](/documentation/integrating-tako/agent/retrieval), 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 | | - | - | - | | **Retrieval Agent** | `/v1/agent/retrieval/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/{retrieval,answer}/runs` → **`202`** with a run object (`status: "queued"`). * **Poll:** `GET /v1/agent/{retrieval,answer}/runs/{run_id}` until `status` is `completed` or `failed`. * **List:** `GET /v1/agent/{retrieval,answer}/runs` returns the caller's runs, newest first. > [!WARNING] > > **Match the path to the agent.** A run dispatched at `/v1/agent/retrieval/runs` must be polled at `/v1/agent/retrieval/runs/{run_id}` — the retrieval and answer collections are separate. A `thread_id` is likewise pinned to one product; a follow-up must use the same agent. ## 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 **Retrieval Agent** — the **Answer Agent** is identical with `client.agent.answer.*` and the `/v1/agent/answer/runs` path. ```bash cURL theme={null} # 1. Dispatch — returns 202 with {"run_id": "...", "status": "queued"}. curl -X POST https://tako.com/api/v1/agent/retrieval/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/retrieval/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.retrieval_agent_run_request import RetrievalAgentRunRequest config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) run = client.agent.retrieval.run( RetrievalAgentRunRequest(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.retrieval.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.retrieval.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.retrieval.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); } } ``` ## 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.retrieval.stream()` / `client.agent.answer.stream()` yield per-product envelope events (`RetrievalAgentStreamEnvelope` / `AnswerAgentStreamEnvelope`); the terminal `agent_result` event carries the same result you'd get from polling. ```python Python theme={null} import os from tako import Configuration from tako.lib import Tako from tako.models.retrieval_agent_run_request import RetrievalAgentRunRequest config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) req = RetrievalAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue and YoY growth") with client.agent.retrieval.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.retrieval.get(stream.run_id) print(run.status) ``` ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/agent/retrieval/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"}' ``` 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 `RetrievalAgentResult` 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/{retrieval,answer}/runs/{run_id}` with `Accept: text/event-stream`. ## 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 **Retrieval 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-retrieval-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. > [!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. ```python theme={null} # Continue a thread, and restrict to Tako's curated data: follow_up = client.agent.retrieval.run( RetrievalAgentRunRequest( query="Now break the leader's revenue down by segment", thread_id=run.thread_id, source_indexes=["data"], ) ) ``` ## Structured output (Retrieval 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`. ```python Python theme={null} import jsonschema from tako.lib import Tako, TakoDatasetView, derive_response_schema from tako.models.retrieval_agent_run_request import RetrievalAgentRunRequest 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.retrieval.run( RetrievalAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue", output_schema=schema) ) run = client.agent.retrieval.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.retrieval.run({ query: "...", output_schema: schema }); const run = await tako.agent.retrieval.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); } ``` 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 (`RetrievalAgentRun` or `AnswerAgentRun`): ```json theme={null} { "run_id": "run_8sKd2m1f0aQ", "object": "agent.retrieval.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: * **`RetrievalAgentResult`** — `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 Retrieval 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. ## List past runs `GET /v1/agent/{retrieval,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/retrieval/runs?limit=20" \ -H "X-API-Key: $TAKO_API_KEY" ``` ## When to use which * **Retrieval 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. | 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 retrieval run at `/v1/agent/answer/runs/{id}` | Poll at the **same** collection you dispatched to (`/v1/agent/retrieval/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 **Retrieval 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`. | # Agents Source: https://docs.tako.com/documentation/integrating-tako/agent/overview Asynchronous deep-research agents that reason across many retrievals over Tako's data and the live web — the Retrieval Agent returns schema-defined structured data and cards, the Answer Agent returns a synthesized, cited answer. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/agent/for-coding-agent) ## Choosing a Tako surface Tako has four query surfaces along two axes — **how much work you want done** (a single call vs. a multi-step research run) and **what comes back** (structured data and cards vs. a written answer): | | **Instant** — one synchronous call | **Agent** — dispatch a run, poll or stream to completion | | - | - | - | | **Structured data + cards** | **[Search](/documentation/integrating-tako/search/overview)** — fast retrieval of knowledge cards and web results, no synthesis | **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** — multi-step retrieval returning schema-defined structured outputs and visualization cards | | **Written, cited answer** | **[Answer](/documentation/integrating-tako/answer/overview)** — one synthesized, source-attributed answer over the same retrieval | **[Answer Agent](/documentation/integrating-tako/agent/answer)** — deep research synthesizing many sources into an opinionated, cited answer backed by visualizations | Use **Search** or **Answer** when a single call suffices — a known value, a time series, a direct comparison of a few named entities, or a [list of companies or people](/documentation/integrating-tako/search/overview#people-and-company-lists) with a filter or two comes back in one fast, synchronous request. Reach for an **agent** when the question needs real multi-step work — resolving a cohort that takes several steps, such as a registry screen combined with a company's directors, ranking or filtering a set by financial metrics, or multi-hop aggregation across many entities — where a run taking tens of seconds to a few minutes is worth a far more thorough result. Between the two agents: * **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** — when you want **machine-usable structured data** out. Define an `output_schema` and the agent fills it, including exact retrieved rows in dataset slots, alongside the visualization cards. * **[Answer Agent](/documentation/integrating-tako/agent/answer)** — when you want a **written, citation-backed answer** synthesized from everything it found, with the cards that support it. ## What the agents do Both agents take a hard, natural-language question and **plan and research** it across Tako's curated knowledge graph **and** the live web — calling tools, fanning out subagents, and reasoning across many entities — rather than answering from a single retrieval. Because they draw on Tako's curated, structured data directly, they reach a grounded result fast and token-efficiently instead of only scraping and filtering unstructured web pages. > **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** > > Data-retrieval agent that returns schema-defined structured data outputs and visualization cards. Define an `output_schema` to get machine-usable JSON — synthesized fields plus dataset slots filled with exact retrieved rows. > **[Answer Agent](/documentation/integrating-tako/agent/answer)** > > Deep-research agent that synthesizes multiple sources into an opinionated, cited answer backed by insightful visualizations. Every `[n]` marker in the answer joins the top-level citations registry. > **[Company Agent](/documentation/integrating-tako/agent/company)** > > List-building agent that finds companies and the people at them, from company registries and licensed company and people data, and returns them as structured rows with contacts you can reveal. ## How a run works Both agents share the same lifecycle and request shape — only the result differs. * **Asynchronous and long-running.** You **dispatch** a run (`POST` returns `202` with a run whose `status` is `queued`), then **poll** `GET …/{run_id}` — or **stream** it over Server-Sent Events — until `status` is `completed` or `failed`. A run can take minutes, which is what lets it do real multi-step work in the background instead of racing a single call. * **Threaded.** Each run belongs to a thread. Pass a prior run's `thread_id` to ask a follow-up in the same conversation; omit it to start a new thread. A thread is pinned to one agent product and one set of sources. * **Grounded in Tako data and the live web.** `source_indexes` defaults to `["data", "web"]` — both. Pass it only to **restrict** to one source (`["data"]` or `["web"]`). The legacy value `"tako"` is a synonym for `"data"`. * **Single effort tier.** `effort` is fixed at `"medium"` for both agents today; it's optional and defaults to `medium`. > [!NOTE] > > The **Retrieval Agent** (`/v1/agent/retrieval/runs`) and **Answer Agent** (`/v1/agent/answer/runs`) are distinct from one-shot [Search](/documentation/integrating-tako/search/overview) (`/v1/search`) and [Answer](/documentation/integrating-tako/answer/overview) (`/v1/answer`). The agents dispatch a background research run; the one-shot endpoints return in a single synchronous call. ## Next steps [Try in Playground](https://tako.com/console/playground/agent/retrieval) * **[Retrieval Agent →](/documentation/integrating-tako/agent/retrieval)** — structured data outputs and cards. * **[Answer Agent →](/documentation/integrating-tako/agent/answer)** — a synthesized, cited answer. * **[Company Agent →](/documentation/integrating-tako/agent/company)** — lists of companies and the people at them. * **[For your coding agent →](/documentation/integrating-tako/agent/for-coding-agent)** — a self-contained build reference covering both agents. # Retrieval Agent Source: https://docs.tako.com/documentation/integrating-tako/agent/retrieval A data-retrieval agent that runs multi-step research and returns schema-defined structured data outputs and visualization cards. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/agent/for-coding-agent) ## Capabilities The **Retrieval Agent** is a data-retrieval agent that returns **schema-defined structured data outputs and visualization cards**. Hand it a question that needs real retrieval work — resolving a cohort, ranking or filtering a set, multi-hop aggregation — and it researches Tako's curated knowledge graph and the live web, then returns exactly the shape you asked for. > **Structured data out** > > Define an `output_schema` (JSON Schema) and the agent fills it: synthesized scalar fields plus **dataset slots** (`x-tako-dataset`) populated with the exact retrieved rows. Get machine-usable JSON, not prose you have to parse. > **Visualization cards** > > Every run may also build Tako `cards` — interactive knowledge cards with their sources and methodology — ready to embed alongside the data. On by default; suppress with `cards: false`. > **Grounded and citable** > > The agent draws on Tako's curated, structured data and the live web. The written `answer` carries `[n]` markers that join a top-level `citations` registry covering both, so every claim is attributable. > **Asynchronous and long-running** > > Dispatch a run, then poll or stream it to completion (a run can take minutes) — real multi-step work in the background instead of racing a single call. For a specific, known value, time series, or direct comparison, use one-shot [Search](/documentation/integrating-tako/search/overview) instead — it returns in one fast call. For a written, synthesized answer rather than structured data, use the [Answer Agent](/documentation/integrating-tako/agent/answer). ## Example [Try in Playground](https://tako.com/console/playground/agent/retrieval) Dispatch a run, then poll `GET /v1/agent/retrieval/runs/{run_id}` until `status` is `completed` or `failed`: **cURL** ```bash theme={null} # 1. Dispatch — returns 202 with a RetrievalAgentRun: {"run_id": "...", "status": "queued"}. curl -X POST https://tako.com/api/v1/agent/retrieval/runs \ -H "X-API-Key: YOUR_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/retrieval/runs/RUN_ID \ -H "X-API-Key: YOUR_API_KEY" ``` **Python** ```bash theme={null} pip install tako-sdk ``` ```python theme={null} import os import time from tako import Configuration from tako.lib import Tako from tako.models.retrieval_agent_run_request import RetrievalAgentRunRequest config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) run = client.agent.retrieval.run( RetrievalAgentRunRequest(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.retrieval.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) ``` > **Example API Response** > > A completed run. `result.answer` is markdown with `[n]` citation markers; `result.citations` is the registry those markers join; `result.cards[0]` is the lead card. > > ```json theme={null} > { > "run_id": "run_8sKd2m1f0aQ", > "object": "agent.retrieval.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 the largest US semiconductor companies with about $60.9B in FY2024 revenue (+126% YoY), ahead of Broadcom and AMD [1][2].", > "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 > } > ``` ## Structured outputs Pass an `output_schema` (a JSON Schema) to get machine-usable JSON back in `result.structured_output`. Two kinds of field: * **Synthesized fields** — ordinary schema properties the agent writes (a headline, a count, a boolean). * **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`. ```json theme={null} { "type": "object", "properties": { "summary": { "type": "string", "description": "One-sentence summary of the finding." }, "companies": { "x-tako-dataset": true, "description": "The companies with their revenue and growth, as a table.", "columns": ["company", "revenue_usd", "yoy_growth_pct"] } }, "required": ["summary"] } ``` The supported subset is `object` / `array` / `string` / `number` / `integer` / `boolean`, plus `enum`, `required`, and `description`. Caps: 16 KB, depth 5, 64 properties, 4 dataset slots. A schema outside these bounds returns `400 output_schema_invalid`. The SDK ships two helpers for consuming the result — `derive_response_schema()` (the schema `structured_output` actually validates against, with each slot as `TakoDataset | null`) and `TakoDatasetView` (a records / DataFrame view over a filled slot): ```python theme={null} import jsonschema from tako.lib import Tako, TakoDatasetView, derive_response_schema from tako.models.retrieval_agent_run_request import RetrievalAgentRunRequest schema = { "type": "object", "properties": { "summary": {"type": "string"}, "companies": {"x-tako-dataset": True, "columns": ["company", "revenue_usd", "yoy_growth_pct"]}, }, "required": ["summary"], } run = client.agent.retrieval.run( RetrievalAgentRunRequest(query="Largest US semiconductor companies by 2024 revenue", output_schema=schema) ) run = client.agent.retrieval.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]) ``` > [!NOTE] > > When the request carries an `output_schema`, the result adds `structured_output_status` (`complete` / `partial` / `failed`) and, for `partial` runs, `unfilled_fields` — the dot-separated paths of dataset slots the agent honestly couldn't fill. `structured_output_citations` maps field paths back to the same `[n]` citation index space as the answer. ## Choosing sources `source_indexes` controls where the agent researches. It defaults to `["data", "web"]` — both — so pass it only to **restrict**: * `["data", "web"]` (default) — Tako's curated knowledge graph **and** the live web. * `["data"]` — the curated knowledge graph only. * `["web"]` — the live web only. The legacy value `"tako"` is accepted as a synonym for `"data"`. ## Continue a thread Each run belongs to a thread. Pass a prior run's `thread_id` to ask a follow-up in the same conversation: ```python theme={null} follow_up = client.agent.retrieval.run( RetrievalAgentRunRequest(query="Now break the leader's revenue down by segment", thread_id=run.thread_id) ) ``` Omit `thread_id` to start a fresh thread. A thread is pinned to one agent product and one set of `source_indexes`. ## Stream live progress To show progress while the agent works, stream the run over Server-Sent Events instead of polling — send `Accept: text/event-stream` on dispatch. `client.agent.retrieval.stream()` yields `RetrievalAgentStreamEnvelope` events; the terminal `agent_result` event carries the same `RetrievalAgentResult` you'd get from polling. See the [coding-agent reference](/documentation/integrating-tako/agent/for-coding-agent#stream-live-progress-sse) for the full event taxonomy and resume semantics. > [!TIP] > > Stream for live progress in interactive UIs; poll for simple server-to-server flows. Both return the same `RetrievalAgentResult` when the run completes. # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/answer/for-coding-agent The build reference for coding agents integrating Tako Answer. Self-contained and copy-paste-ready — endpoint, X-API-Key auth, every request parameter, the grounded answer response shape, and runnable cURL, Python, and TypeScript examples. Use this page to generate a working Answer integration. This page is a complete, self-contained reference for building against **Tako Answer**. 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 the auto-generated schema, see the [Answer API reference](/api-reference/answer); for a narrative introduction, see the [Overview](/documentation/integrating-tako/answer/overview). ## What Answer does `POST https://tako.com/api/v1/answer` takes a natural-language `query` and returns a **synthesized written `answer`** in a single fast call, grounded in real-time, trusted data — alongside the **Tako knowledge cards** and **web results** that back it. Use it to ground your own model output and keep what you show consistent with Tako's data (for example, grounding a stock-price analysis with Tako's up-to-date pricing card). Answer takes the **same request body as [Search](/documentation/integrating-tako/search/for-coding-agent)**; the only response difference is the added `answer` string. Because each backing card ships with named sources and methodology, you get an answer you can **attribute and cite**, not just a paragraph of text. * **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). ## Install ```bash theme={null} pip install tako-sdk # Python npm install tako-sdk # TypeScript / JavaScript ``` ## Minimal working example Each snippet makes the same call and is complete after you set `TAKO_API_KEY`. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/answer \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What is the weather in San Francisco?" }' ``` ```python Python theme={null} import os from tako import Configuration, SearchRequest from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) response = client.answer(SearchRequest(query="What is the weather in San Francisco?")) print(response.answer) for card in response.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! }); const response = await tako.answer({ query: "What is the weather in San Francisco?" }); console.log(response.answer); for (const card of response.cards ?? []) { console.log(card.title, card.webpage_url); } ``` ## Request parameters The body is a `SearchRequest` — identical to Search. Only `query` is required; `effort` (`fast` default / `instant` / `deep`), `sources`, `country_code` (`US`), `locale` (`en-US`), `timezone`, and `output_settings` all behave exactly as documented in the [Search reference](/documentation/integrating-tako/search/for-coding-agent#request-parameters). ### Forcing entity boundaries in `query` Tako identifies the entities in the query before it retrieves the cards and web results that ground the answer. Double quotes force a phrase to be exactly one entity, and an optional `:LABEL` suffix sets its type. Use this when you already know the entity — an agent usually does. ```json theme={null} { "query": "\"tesla motors\" club revenue" } { "query": "\"Tesla\":PRODUCT price" } ``` Labels: `PERSON`, `ORG`, `GPE`, `LOC`, `PRODUCT`, `DATE`, `EVENT`, `MONEY`, `CARDINAL`, `METRIC`, `TIME_PERIOD`, `UNITS`. Quoting is all-or-nothing: every `"` in the query must belong to a non-empty pair of 80 characters or less. If one quote is unmatched, Tako ignores every quote in that query and searches as usual, so an ordinary web-style phrase query still works. An unrecognized label stays in the query text. The quotes never reach the search text. Search accepts the same syntax. ### Latency Target p50 latency (not a guarantee), by `effort`: | `effort` | Target p50 | | - | - | | `instant` | \~750 ms | | `fast` *(default)* | \~950 ms | | `deep` | \~6 s | ### Choosing sources > [!TIP] > > **Use both sources.** Omit `sources` and Answer grounds in **both** Tako's curated knowledge graph and the live web — the recommended default for the most complete, accurate answer. Narrow to one index only when you have a specific reason. By default Answer grounds in **both** Tako's curated knowledge graph (`data`) and the live web (`web`), 5 results each. `sources` is an **object** whose keys select the indexes — a source is used only if its key is present: | `sources` value | Grounds in | | - | - | | *(omitted)* | both — equivalent to `{ "data": {}, "web": {} }` | | `{ "data": {} }` | Tako curated knowledge graph only | | `{ "web": {} }` | the live web only | Per-source `count` (1–20, default 5) and `include_contents` work as in Search. The legacy key `tako` maps to `data`. ## What a response looks like A `200` returns an `AnswerResponse` — a `SearchResponse` plus a required `answer` string. **`cards[0]` is the lead card**: the best one to show alongside the answer. ```json theme={null} { "answer": "It's currently 52°F in San Francisco and feels like 52°F, with partly cloudy skies. Today's high is 54°F and the low is 50°F; the rest of the week is expected to be mostly clear with highs around 53–55°F.", "cards": [ { "card_id": "mA-C6b48zxyU3G33JJdO", "title": "San Francisco, CA Weather Metrics", "description": "This card displays the weather forecast for San Francisco, CA for today. Currently, it's 52°F and feels like 52°F. The high for today is 54°F and the low is 50°F.", "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/", "sources": [ { "source_name": "NOAA National Weather Service", "source_description": "The U.S. agency providing weather forecasts and warnings, part of NOAA.", "source_index": "tako" } ], "methodologies": [ { "methodology_name": "Data Collection", "methodology_description": "Raw meteorological data from NOAA's NWS, sourced from weather.gov, with no statistical adjustments applied." } ], "source_indexes": ["tako"] } ], "web_results": [], "request_id": "f20f965b-6bbd-40df-b50b-a8861f34df24" } ``` | Field | Type | Description | | - | - | - | | `answer` | string | The synthesized, grounded answer. Always present. | | `cards` | array | Backing Tako cards; `cards[0]` is the lead card to display. | | `web_results` | array | Backing web results (`title`, `url`, plus optional `snippet`, `source_name`, `publish_date`). | | `request_id` | string | Always present — log it for support/debugging. | Backing cards carry the same `content`/`cost` fields as [Search](/documentation/integrating-tako/search/for-coding-agent#what-a-response-looks-like); credits apply per call — see [Credits & billing](/documentation/additional-resources/credits-and-billing#the-cost-field). ### Grounding and attribution To keep your content accurate and citable, read these fields off each card: * **`description`** — a natural-language description of the data and the latest data point. * **`sources`** — named sources (`source_name`, `source_description`) backing the card. * **`methodologies`** — how the data was collected or derived. * **`image_url`** — a static image of the card to display next to the answer. ## When to use Answer Answer is for a **ready-to-show, source-attributed answer in one call** — synthesized prose that draws the **best answer from both Tako's curated data and the live web**, shipped with the cards and web results that back it. Reach for it when: * **You want prose you can drop straight in.** One synchronous call returns a written `answer`, not raw results to assemble yourself — ideal for chat assistants and any surface that needs text. * **The answer has to be citable.** Every answer comes back with its backing cards — named sources and methodology — so you can attribute each claim and keep content auditable. * **You want it to stay current.** Re-run the same query to refresh prose that stays grounded in live data, across Tako's curated knowledge graph **and** the live web by default. * **You're grounding your own model.** Inline the underlying data with `include_contents` so your model reasons over real numbers, not snippets. **Only need the raw cards and web results?** Use [Search](/documentation/integrating-tako/search/for-coding-agent) — the same request body, without the synthesized `answer`. **Need multi-step reasoning** — resolving a cohort, ranking a set, multi-hop aggregation? Use the [Agent](/documentation/integrating-tako/agent/for-coding-agent). ## Common mistakes > [!WARNING] > > Avoid these — they are the patterns coding agents most often get wrong. | Wrong | Correct | | - | - | | `Authorization: Bearer $KEY` | `X-API-Key: $KEY` | | Posting to `/v1/search` or `/v3/answer` | Answer is `POST /v1/answer`; Search is `POST /v3/search` | | `"sources": ["data"]` (array) | `"sources": { "data": {} }` (object — key presence selects the source) | | Treating any card as the lead | `cards[0]` is the lead card — display it alongside the `answer` | | Showing the `answer` without attribution | Read `sources` / `methodologies` off the backing cards to cite it | | Leaving one unmatched `"` in `query` | Pair every quote — a single stray quote turns off [entity forcing](#forcing-entity-boundaries-in-query) for the whole query | ## Errors Failures return a `BaseAPIError` body: `{ "error_message": "...", "error_type": "..." }`. | Status | Meaning | Fix | | - | - | - | | `400` | Malformed request (bad JSON, invalid parameter). | 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). | | `408` | The request timed out. | Retry the request. | # Answer Source: https://docs.tako.com/documentation/integrating-tako/answer/overview Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/answer/for-coding-agent) ## Capabilities Answer is the **fastest way to get a written, source-attributed answer** from Tako. It runs the same retrieval as [Search](/documentation/integrating-tako/search/overview) — Tako's structured knowledge cards **and** the live web — and **synthesizes the best answer from both** into one response, returned in a single synchronous call you can use inline. > **One fast, synchronous call** > > A single request returns a finished `answer` — no dispatching, polling, or retrieval to orchestrate. Fast enough to drop inline into a chat, an assistant, or an agentic loop. > **Built on Tako Search** > > Answer grounds on the same retrieval as [Search](/documentation/integrating-tako/search/overview) — structured knowledge cards plus the live web — then writes the answer over them, so you get prose *and* the results that produced it in one call. > **Ready to show, with receipts** > > Every answer ships with the Tako `cards` **and web results** that back it — named sources and methodology — so it's citable, auditable, and safe to drop straight into a user-facing surface. Choosing between Tako's surfaces (see the [full comparison](/documentation/integrating-tako/agent/overview#choosing-a-tako-surface)): | Surface | What it's for | | - | - | | **[Answer](/api-reference/answer)** | A written, source-attributed answer in one call. | | **[Search](/documentation/integrating-tako/search/overview)** | Fast, parallel retrieval of structured data and cards, no synthesized answer. | | **[Answer Agent](/documentation/integrating-tako/agent/answer)** | Deep research synthesizing a cited answer backed by visualizations. | | **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** | Multi-step research returning schema-defined structured data and cards. | ## Example [Try in Playground](https://tako.com/playground) **cURL** ```bash theme={null} curl -X POST https://tako.com/api/v1/answer \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "How have Nvidia and AMD headcount changed since 2013?" }' ``` **Python** ```bash theme={null} pip install tako-sdk ``` ```python theme={null} import os from tako import Configuration, SearchRequest from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) response = client.answer(SearchRequest(query="How have Nvidia and AMD headcount changed since 2013?")) print(response.answer) for card in response.cards or []: print(card.title, card.webpage_url) ``` > **Example Result** > > Answer: "Nvidia has grown to about 36,000 full-time employees as of the end of 2024 — up roughly 309% from about 8,800 in 2013. AMD reached about 28,000 over the same period, giving Nvidia both the larger workforce and the faster growth of the two." > > [https://tako.com/embed/geYufvSx04pyWX7GpHeR/?dark\_mode=false](https://tako.com/embed/geYufvSx04pyWX7GpHeR/?dark_mode=false) ## Use cases Each of these is the same call shown above with a different query. Only the last one takes a parameter you haven't seen yet. **Answer a question mid-conversation.** A support bot or assistant hits a data question it can't answer from its own context, and one call returns finished prose plus the cards that back it — no retrieval to orchestrate. `"How did the S&P 500 do this week?"` **Keep generated content current.** Re-run the same query on a schedule and the prose refreshes with the data, so a page's words and its numbers never drift apart. `"Latest US CPI, year over year"` **Attribute every claim.** Read `card.sources` and `card.methodologies` on each returned card to cite the answer, so a reader can check it rather than trust it. `"Nvidia revenue growth"` **Ground in curated data only.** Pass `sources` to skip the web and answer from Tako's licensed data alone — the right choice when a web result would weaken an answer rather than strengthen it. ```python theme={null} response = client.answer(SearchRequest(query="United States military expenditure", sources={"data": {}})) ``` > [!NOTE] > > **Want the data behind a card as CSV?** Pass a card's URL to [Contents](/documentation/integrating-tako/contents/overview) to download its underlying data — or inline it here with `include_contents`. > [!TIP] > > By consuming the answer and its grounding information, you can keep your content always up to date and accurate. # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/chart-creation/for-coding-agent The build reference for coding agents integrating Tako Thin-Viz. Self-contained, copy-paste-ready cheat-sheet — create an embeddable card from your own data, with the component-type list, data shapes, request fields, and runnable cURL, Python, and TypeScript examples. Use this page to generate a working Thin-Viz integration. This is the quick, copy-pasteable reference for building cards with **Tako Thin-Viz**. For the full example gallery (every component with a worked example), see the [Overview](/documentation/integrating-tako/chart-creation/overview); for the auto-generated schema, see the [API reference](/api-reference/thinviz-direct-create). ## What Thin-Viz does `POST https://tako.com/api/v1/thin_viz/create/` turns your own data into an interactive, embeddable Tako card. One call, one card: you post an array of `components`, and the response gives you `embed_url`, `image_url`, and `webpage_url`. * **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). ## Install ```bash theme={null} pip install tako-sdk # Python npm install tako-sdk # TypeScript / JavaScript ``` ## Minimal working example A single line chart. Set `TAKO_API_KEY` and run. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Monthly Revenue", "source": "Internal Analytics", "components": [ { "component_type": "generic_timeseries", "config": { "datasets": [ { "label": "Revenue", "data": [ {"x": "2024-01", "y": 50000}, {"x": "2024-02", "y": 62000}, {"x": "2024-03", "y": 71000} ], "units": "$" } ], "chart_type": "line", "title": "Monthly Revenue" } } ] }' ``` ```python Python theme={null} import os from tako import Configuration, CreateCardRequest, ComponentConfig from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) card = client.create_card(CreateCardRequest( title="Monthly Revenue", source="Internal Analytics", components=[ ComponentConfig( component_type="generic_timeseries", config={ "datasets": [ { "label": "Revenue", "data": [ {"x": "2024-01", "y": 50000}, {"x": "2024-02", "y": 62000}, {"x": "2024-03", "y": 71000} ], "units": "$" } ], "chart_type": "line", "title": "Monthly Revenue" }, ), ], )) print(card.embed_url) ``` ```typescript TypeScript theme={null} import { Tako } from "tako-sdk"; const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! }); const card = await tako.createCard({ title: "Monthly Revenue", source: "Internal Analytics", components: [ { component_type: "generic_timeseries", config: { datasets: [ { label: "Revenue", data: [ { x: "2024-01", y: 50000 }, { x: "2024-02", y: 62000 }, { x: "2024-03", y: 71000 }, ], units: "$", }, ], chart_type: "line", title: "Monthly Revenue", }, }, ], }); console.log(card.embed_url); ``` The response is a card with `card_id`, `title`, `description`, `embed_url`, `image_url`, `webpage_url`, and `card_type`. ## Request fields (`CreateCardRequest`) | Field | Type | Required | Description | | - | - | - | - | | `components` | array | Yes | Ordered list of components; they render top-to-bottom on the card. | | `title` | string | No | Card title (falls back to a `header` component's title, else "Card"). | | `description` | string | No | Card description. | | `source` | string | No | Source attribution shown in the footer. | | `height` | integer (100–2000) | No | Override the aspect-ratio height. | | `postmessage_embed` | boolean | No | Enable postMessage data injection mode (default `false`). | | `normalize_currencies` | string | No | Target ISO 4217 code to normalize currency values to. | | `image_ttl_minutes` | integer (1–1440) | No | **ZDR cards only** — keep a downloadable preview image for this many minutes, then `410 Gone`. | Each entry in `components` is a `ComponentConfig`: `component_type` (from the enum below) + a free-form `config` object (plus optional `component_variant`). ## Component types These are the valid `component_type` values (`ComponentTypeEnum`): `header` · `generic_timeseries` · `categorical_bar` · `data_table_chart` · `financial_boxes` · `table` · `timeline` · `pie` · `histogram` · `scatter` · `bubble` · `choropleth` · `heatmap` · `boxplot` · `treemap` · `waterfall` · `sankey` · `marimekko` · `person_card` · `top_level_metric` See the [Overview](/documentation/integrating-tako/chart-creation/overview) for a worked `config` example of each. > [!NOTE] > > For stock-style cards, use `generic_timeseries` with `show_kicker_boxes: true` (each dataset with a `kicker` renders a box above the chart). `stock_boxes` appears in older examples but is **not** a `ComponentTypeEnum` value — prefer `generic_timeseries` + `show_kicker_boxes`. ## Data shapes — know which one a component wants Most components take `datasets`, but a few take a different shape. Getting this wrong is the most common error: | Shape | Components | Example | | - | - | - | | `datasets: [{ label, data: [{x, y}], units }]` | `generic_timeseries`, `categorical_bar`, `data_table_chart`, `pie`, `histogram`, `treemap`, `choropleth` | `{"x": "2024-01", "y": 50000}` | | `datasets` with `{x, y, r}` | `scatter`, `bubble` (`r` = radius on bubble) | `{"x": 4.2, "y": 12.5, "r": 15}` | | Flat `data: [{x, y, v}]` | `heatmap`, `marimekko` | `{"x": "Mon", "y": "AM", "v": 12}` | | `categories: [{ category, values: [] }]` | `boxplot` | raw values per category | | `items: []` | `waterfall`, `financial_boxes` | `{"category": "Revenue", "value": 100, "is_anchor": true}` | | `nodes` + `links` | `sankey` | `links` reference node `id`s; must form a DAG | | `columns` + `rows` | `table` | typed columns (`string`/`number`/`currency`/…) | ## Embedding the card Drop the returned `embed_url` into an iframe (add `?dark_mode=true`/`false` to theme it). For full embedding guidance — dynamic resizing, theme matching, glanceable mode — see [Embedding Knowledge Cards](/documentation/integrating-tako/embedding-knowledge-cards). ```html theme={null} ``` ## Preview images (ZDR cards) ZDR cards don't persist visualization data, so they have no standing preview image. Set `image_ttl_minutes` (1–1440) at creation to get a temporary `image_url`. Customize it with query params: `dark_mode`, `width`, `height`, `hideFooter`. After the TTL expires the image endpoint returns `410 Gone`. (`image_ttl_minutes` is ignored for standard cards, which already have persistent images.) ## Tips & errors * **Gzip large payloads.** For request bodies over \~1 MB, gzip the body (`Content-Encoding: gzip`) — Thin-Viz accepts it and it compresses well (up to \~10×). * **Auth:** `X-API-Key` header, not a bearer token. Errors return a bare `{ "error": "..." }` body: | Status | Meaning | Fix | | - | - | - | | `400` | Invalid request (bad component config, unknown `component_type`, wrong data shape). | Check the `component_type` against the enum and the data shape table above. | | `404` | Referenced resource not found. | Verify any IDs in the request. | | `500` | Server error. | Retry; if it persists, contact support. | # Thin-Viz Source: https://docs.tako.com/documentation/integrating-tako/chart-creation/overview Create interactive, embeddable cards from your own data, with a full gallery of every component type. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/chart-creation/for-coding-agent) Create interactive, embeddable cards from your own data using the Tako Thin-Viz API. ## Why Thin-Viz * **Your data, Tako's rendering.** Post your own values and component configs; Tako returns a polished, interactive card — no charting library or frontend work on your side. * **A full component gallery.** Timeseries, bar, pie, table, treemap, sankey, choropleth, heatmap, waterfall, and more — composed top to bottom in a single card. * **Embed anywhere.** Every card comes back with an `embed_url` (interactive iframe) and an `image_url` (static image) for chat, dashboards, or docs. ## Creating a Card Create a card by posting your component configurations. One API call, one card. **cURL** ```bash theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Monthly Revenue", "source": "Internal Analytics", "components": [ { "component_type": "generic_timeseries", "config": { "datasets": [ { "label": "Revenue", "data": [ {"x": "2024-01", "y": 50000}, {"x": "2024-02", "y": 62000}, {"x": "2024-03", "y": 71000} ], "units": "$" } ], "chart_type": "line", "title": "Monthly Revenue" } } ] }' ``` **Python** ```bash theme={null} pip install tako-sdk ``` ```python theme={null} import os from tako import Configuration, CreateCardRequest, ComponentConfig from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) card = client.create_card(CreateCardRequest( title="Monthly Revenue", source="Internal Analytics", components=[ ComponentConfig( component_type="generic_timeseries", config={ "datasets": [ { "label": "Revenue", "data": [ {"x": "2024-01", "y": 50000}, {"x": "2024-02", "y": 62000}, {"x": "2024-03", "y": 71000} ], "units": "$" } ], "chart_type": "line", "title": "Monthly Revenue" }, ), ], )) print(card.embed_url) ``` Response: ```json theme={null} { "card_id": "abc123xyz", "title": "Monthly Revenue", "description": "", "embed_url": "https://tako.com/embed/abc123xyz", "image_url": "https://tako.com/api/v1/image/abc123xyz/", "webpage_url": "https://tako.com/card/abc123xyz", "card_type": "chart", "visualization_data": { ... } } ``` ### Request Fields | Field | Type | Required | Description | | - | - | - | - | | `components` | array | Yes | List of component configurations (see [Available Components](#available-components)) | | `title` | string | No | Card title (falls back to header component title if not provided) | | `description` | string | No | Card description | | `source` | string | No | Data source attribution displayed in footer | | `image_ttl_minutes` | integer | No | Minutes to keep a preview image available for download (1–1440). ZDR cards only. See [Preview Images for ZDR Cards](#preview-images-for-zdr-cards) | ## Embedding Your Card Use the `embed_url` from the response in an iframe: ```html theme={null} ``` Add `?dark_mode=true` or `?dark_mode=false` to control the theme. ## Preview Images for ZDR Cards Cards created with zero data retention (ZDR) don't normally support preview images because the visualization data isn't persisted. To get a downloadable image for a ZDR card, set `image_ttl_minutes` when creating the card: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Monthly Revenue", "image_ttl_minutes": 60, "components": [ ... ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Monthly Revenue", image_ttl_minutes=60, components=[...], # see the examples below for full component configs )) ``` The response includes an `image_url` that renders a screenshot on demand. You can customize the image with query parameters: | Parameter | Type | Description | | - | - | - | | `dark_mode` | boolean | `true` for dark theme, `false` for light | | `width` | integer | Image width in pixels | | `height` | integer | Image height in pixels | | `hideFooter` | boolean | Hide the card footer | ``` https://tako.com/api/v1/image/abc123xyz/?dark_mode=true&width=800&height=600 ``` After the TTL expires, the image endpoint returns `410 Gone`. The TTL must be between 1 and 1440 minutes (24 hours). > [!NOTE] > > `image_ttl_minutes` is only supported for ZDR cards and is ignored for standard cards, which already have persistent preview images. ## Shared Config Options Every component takes a `config` object whose shape depends on its `component_type`. Each example below lists its component's fields. Tako ignores unknown `config` fields without an error, so if an option has no effect, check its spelling. Many components accept these optional fields: | Field | Type | Description | | - | - | - | | `title` | string | Chart title. Most charts show it in the tooltip, not above the chart. For a visible title, add a `header` component. | | `series_color_theme` | string | Color scheme: `"default"`, `"red_bad_green_good"` (green for positive values, red for negative), `"gray"`, `"divergent"`, `"sequential"`, `"categorical"`, or `"uniform"` (one color for every point). | | `legend` | object | Legend settings: `show` (boolean, on by default for multi-series charts), `position` (`"top"` or `"bottom"`, default `"bottom"`), and `items`, a custom list of `label`, optional `value`, and optional `color` entries. | ### Color Overrides Set `color` to a hex string (`#RGB` or `#RRGGBB`) to override a series color in both themes. Add `color_light_mode` or `color_dark_mode` to use a different color in one theme; they take precedence over `color`. * Dataset-level colors work on `generic_timeseries`, `categorical_bar`, `data_table_chart`, and `scatter`. * Per-point `color` works on `categorical_bar`, `data_table_chart`, and `choropleth` data points. * Per-event `color` works on `timeline` events, in `#RRGGBB` form. * Other components take their colors from `series_color_theme`. `bubble` datasets accept `color` fields but don't apply them yet. ## Example: Stock Card with Kicker Boxes For financial data, use the `generic_timeseries` component with `show_kicker_boxes` enabled: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "AAPL vs GOOGL", "source": "Market Data", "components": [ { "component_type": "generic_timeseries", "config": { "datasets": [ { "label": "AAPL", "kicker": "NASDAQ", "data": [ {"x": "2024-01-01", "y": 185.50}, {"x": "2024-01-02", "y": 195.20} ], "units": "$" }, { "label": "GOOGL", "kicker": "NASDAQ", "data": [ {"x": "2024-01-01", "y": 152.60}, {"x": "2024-01-02", "y": 150.10} ], "units": "$" } ], "chart_type": "line", "title": "Stock Price (USD)", "show_kicker_boxes": true } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="AAPL vs GOOGL", source="Market Data", components=[ ComponentConfig( component_type="generic_timeseries", config={ "datasets": [ { "label": "AAPL", "kicker": "NASDAQ", "data": [ {"x": "2024-01-01", "y": 185.50}, {"x": "2024-01-02", "y": 195.20} ], "units": "$" }, { "label": "GOOGL", "kicker": "NASDAQ", "data": [ {"x": "2024-01-01", "y": 152.60}, {"x": "2024-01-02", "y": 150.10} ], "units": "$" } ], "chart_type": "line", "title": "Stock Price (USD)", "show_kicker_boxes": True }, ), ], )) ``` > [!NOTE] > > When `show_kicker_boxes` is enabled, each dataset with a `kicker` field renders a stock box above the chart showing the exchange label, current value, and change. ### Timeseries Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | The series to plot. Provide this or `datasets_by_interval`, not both | | `datasets_by_interval` | object | — | Maps ISO 8601 durations (for example, `"PT5M"` or `"P1D"`) to arrays of datasets. Tako builds the date picker from the intervals | | `chart_type` | `"line"` \| `"bar"` \| `"scatter"` | `"line"` | Default chart type. A dataset's `type` overrides it | | `title` | string | — | Tooltip title | | `default_date_picker_option` | string | First option | Initial date range as an ISO period, such as `"P1Y"`, `"P3M"`, or `"YTD"`. Falls back to the first option when the value isn't available | | `exclude_date_picker_options` | array | — | ISO periods to hide from the date picker, such as `["P3M", "P6M"]`. Ignored when it would hide every option | | `enable_percent_change_toggle` | boolean | `false` | Adds a toggle between absolute values and percent change | | `absolute_toggle_label` | string | `"$"` | Label for the absolute-value toggle button, such as `"€"` or `"MW"` | | `percent_toggle_label` | string | `"%"` | Label for the percent-change toggle button, such as `"YoY %"` | | `end_of_line_labels` | boolean | `false` | Shows a label at the end of each line on multi-series charts | | `show_kicker_boxes` | boolean | `false` | Renders a stock box above the chart for each dataset that has a `kicker` | | `enable_client_side_filtering` | boolean | `true` | Filters date ranges in the browser without a server call | | `disable_view_toggle` | boolean | — | Hides the view selector | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | Each dataset has: | Field | Type | Required | Description | | - | - | - | - | | `label` | string | Yes | Series name in the legend | | `data` | array | Yes | Points with `x` (a date string, timestamp, or number) and `y` (a number or `null`) | | `type` | `"line"` \| `"bar"` \| `"scatter"` | No | Chart type for this series | | `units` | string | No | Value units, such as `"$"`, `"%"`, or `"USD"` | | `kicker` | string | No | Exchange label for the stock box, such as `"NASDAQ"` | | `title` | string | No | Company or series name shown above the ticker in the stock box | | `color`, `color_light_mode`, `color_dark_mode` | string | No | See [Color Overrides](#color-overrides) | ## Example: Bar Chart ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Revenue by Region", "source": "Internal Analytics", "components": [ { "component_type": "header", "config": { "title": "Revenue by Region" } }, { "component_type": "categorical_bar", "config": { "datasets": [ { "label": "Revenue", "data": [ {"x": "North", "y": 120000}, {"x": "South", "y": 98000}, {"x": "East", "y": 87000}, {"x": "West", "y": 145000} ], "units": "$" } ] } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Revenue by Region", source="Internal Analytics", components=[ ComponentConfig( component_type="header", config={"title": "Revenue by Region"}, ), ComponentConfig( component_type="categorical_bar", config={ "datasets": [ { "label": "Revenue", "data": [ {"x": "North", "y": 120000}, {"x": "South", "y": 98000}, {"x": "East", "y": 87000}, {"x": "West", "y": 145000} ], "units": "$" } ] }, ), ], )) ``` ### Header Fields | Field | Type | Required | Description | | - | - | - | - | | `title` | string | Yes | Header title. When the request has no `title`, the first header's title becomes the card title | | `subtitle` | string | No | Line below the title | ### Bar Chart Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | Categorical datasets (see below). One dataset draws a single series; several draw a grouped chart | | `title` | string | — | Tooltip title | | `default_view` | `"vertical"` \| `"horizontal"` \| `"grouped"` \| `"stacked"` | `"vertical"` for one dataset, `"grouped"` for several | Initial view. One dataset supports `vertical` and `horizontal`; several support `grouped` and `stacked` | | `sort_series_by` | `"value"` \| `"alphabetical"` | `"value"` | Series order in grouped charts. `value` sorts by each series' value at the first category | | `sort_series_direction` | `"asc"` \| `"desc"` | `"desc"` | Sort direction | | `disable_view_toggle` | boolean | — | Hides the view selector, so the chart stays in its default view | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | Categorical datasets are also used by `data_table_chart`, `pie`, `treemap`, `histogram`, and `choropleth`. Each one has: | Field | Type | Required | Description | | - | - | - | - | | `label` | string | Yes | Series name in the legend | | `data` | array | Yes | Points with `x` (a category label) and `y` (a number). Every point needs a numeric `y`, or the request fails with a 400 that names the points | | `data[].highlight` | boolean | No | Emphasizes this bar. With `series_color_theme: "uniform"`, the highlighted bar gets the accent color and the rest are muted | | `data[].color` | string | No | Hex color for this point. Honored by `categorical_bar`, `data_table_chart`, and `choropleth` | | `units` | string | No | Value units, such as `"$"` or `"%"` | | `color`, `color_light_mode`, `color_dark_mode` | string | No | See [Color Overrides](#color-overrides) | ## Example: Data Table Chart A bar chart with an auto-generated data table below showing the values: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Q4 Sales by Product", "source": "Sales Team", "components": [ { "component_type": "header", "config": { "title": "Q4 Sales by Product" } }, { "component_type": "data_table_chart", "config": { "datasets": [ { "label": "Units Sold", "data": [ {"x": "Widget A", "y": 1200}, {"x": "Widget B", "y": 850}, {"x": "Widget C", "y": 2100} ] } ] } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Q4 Sales by Product", source="Sales Team", components=[ ComponentConfig( component_type="header", config={"title": "Q4 Sales by Product"}, ), ComponentConfig( component_type="data_table_chart", config={ "datasets": [ { "label": "Units Sold", "data": [ {"x": "Widget A", "y": 1200}, {"x": "Widget B", "y": 850}, {"x": "Widget C", "y": 2100} ] } ] }, ), ], )) ``` ### Data Table Chart Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | [categorical datasets](#bar-chart-fields). One dataset draws a single series; several draw a grouped chart | | `title` | string | — | Tooltip title | | `default_view` | `"vertical"` \| `"horizontal"` \| `"grouped"` \| `"stacked"` | `"vertical"` for one dataset, `"grouped"` for several | Initial view, as in `categorical_bar` | | `series_color_theme` | string | `"uniform"` for one dataset | See [Shared Config Options](#shared-config-options). | | `legend` | object | — | See [Shared Config Options](#shared-config-options). | ## Example: Financial Boxes Display financial metrics with growth indicators: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "AAPL Financials", "source": "SEC Filings", "components": [ { "component_type": "financial_boxes", "config": { "items": [ { "header": "Revenue", "value": "$94.9B", "growth": {"formattedValue": "6.1% YoY", "value": 0.061} }, { "header": "Net Income", "value": "$23.6B", "growth": {"formattedValue": "-3.4% YoY", "value": -0.034} }, { "header": "EPS", "value": "$1.64" } ] } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="AAPL Financials", source="SEC Filings", components=[ ComponentConfig( component_type="financial_boxes", config={ "items": [ { "header": "Revenue", "value": "$94.9B", "growth": {"formattedValue": "6.1% YoY", "value": 0.061} }, { "header": "Net Income", "value": "$23.6B", "growth": {"formattedValue": "-3.4% YoY", "value": -0.034} }, {"header": "EPS", "value": "$1.64"} ] }, ), ], )) ``` > [!NOTE] > > Growth indicator colors are automatic: positive `value` appears green, negative appears red. ### Financial Boxes Fields `items` is required. Each item has: | Field | Type | Required | Description | | - | - | - | - | | `header` | string | Yes | Metric name, such as `"Revenue"` | | `value` | string | No | Preformatted value, such as `"$10.5B"` | | `growth` | object | No | Growth indicator with `formattedValue` (the displayed text, such as `"5.25% YoY"`) and `value` (a number whose sign sets the color, such as `0.0525` for 5.25%) | ## Example: Top-Level Metric Show one large headline number or phrase with a caption: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "US Unemployment Rate", "source": "Bureau of Labor Statistics", "components": [ { "component_type": "top_level_metric", "config": { "accent_text": "As of March 2024", "headline": "3.8%", "subheadline": "US unemployment rate", "description": "Seasonally adjusted share of the labor force that is unemployed." } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="US Unemployment Rate", source="Bureau of Labor Statistics", components=[ ComponentConfig( component_type="top_level_metric", config={ "accent_text": "As of March 2024", "headline": "3.8%", "subheadline": "US unemployment rate", "description": "Seasonally adjusted share of the labor force that is unemployed." }, ), ], )) ``` ### Top-Level Metric Fields | Field | Type | Required | Description | | - | - | - | - | | `headline` | string | Yes | Large headline text | | `subheadline` | string | Yes | Caption below the headline | | `accent_text` | string | No | Small line above the headline | | `description` | string | No | Longer text below the metric | ## Example: Data Table A configurable table with columns, sorting, and pagination: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Product Performance", "components": [ { "component_type": "table", "config": { "columns": [ {"field": "name", "label": "Product", "type": "string"}, {"field": "revenue", "label": "Revenue", "type": "currency", "units": "$"}, {"field": "growth", "label": "Growth", "type": "percent", "units": "%"}, {"field": "rating", "label": "Rating", "type": "rating"}, {"field": "in_stock", "label": "In Stock", "type": "boolean"} ], "rows": [ {"name": "Product A", "revenue": 1500000, "growth": 15.0, "rating": 4, "in_stock": true}, {"name": "Product B", "revenue": 2300000, "growth": -5.0, "rating": 3, "in_stock": true}, {"name": "Product C", "revenue": 980000, "growth": 22.3, "rating": 5, "in_stock": false} ], "searchable": true, "paginated": true, "rows_per_page": 25, "initial_sort_field": "revenue", "initial_sort_order": "desc", "highlight_best": true } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Product Performance", components=[ ComponentConfig( component_type="table", config={ "columns": [ {"field": "name", "label": "Product", "type": "string"}, { "field": "revenue", "label": "Revenue", "type": "currency", "units": "$" }, { "field": "growth", "label": "Growth", "type": "percent", "units": "%" }, {"field": "rating", "label": "Rating", "type": "rating"}, { "field": "in_stock", "label": "In Stock", "type": "boolean" } ], "rows": [ { "name": "Product A", "revenue": 1500000, "growth": 15.0, "rating": 4, "in_stock": True }, { "name": "Product B", "revenue": 2300000, "growth": -5.0, "rating": 3, "in_stock": True }, { "name": "Product C", "revenue": 980000, "growth": 22.3, "rating": 5, "in_stock": False } ], "searchable": True, "paginated": True, "rows_per_page": 25, "initial_sort_field": "revenue", "initial_sort_order": "desc", "highlight_best": True }, ), ], )) ``` ### Table Column Types | Type | Rendering | Default Alignment | | - | - | - | | `string` | Plain text | Left | | `number` | Formatted with units | Right | | `date` | Date values | Left | | `percent` | Percentage formatting | Right | | `currency` | Right-aligned with currency formatting (uses `units` field) | Right | | `boolean` | Checkmarks (✓/✗) with green/red colors | Center | | `rating` | Stars (★★★☆☆) clamped to 1-5 range | Center | ### Table Config Fields | Field | Type | Default | Description | | - | - | - | - | | `columns` | array | *required* | Column definitions (see column types above) | | `rows` | array | *required* | Row data as list of objects. Keys should match column `field` values | | `title` | string | — | Optional title displayed above the table | | `searchable` | boolean | `false` | Enable search/filter functionality | | `paginated` | boolean | `false` | Enable pagination | | `rows_per_page` | integer | `25` | Number of rows per page when paginated | | `initial_sort_field` | string | — | Field to sort by initially | | `initial_sort_order` | `"asc"` \| `"desc"` | `"desc"` | Initial sort direction | | `highlight_best` | boolean | `false` | Highlight the best value in numeric columns with a green background. For currency: lowest is best. For number/rating: highest is best. Only highlights when there is a unique winner | Each column also supports an optional `align` field (`"left"`, `"right"`, or `"center"`) to override the default alignment. > [!NOTE] > > When `highlight_best` is enabled, the best value in each currency, number, and rating column is highlighted with a green background. Currency columns treat the lowest value as best; number and rating columns treat the highest value as best. Boolean columns are excluded — they use checkmark colors instead. ## Example: Timeline A timeline showing events or periods along a time axis: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Product Launch Timeline", "source": "Product Team", "components": [ { "component_type": "timeline", "config": { "events": [ { "start_date": "2024-01-15", "end_date": "2024-03-30", "label": "Research & Design", "value": "2.5 months" }, { "start_date": "2024-04-01", "end_date": "2024-07-15", "label": "Development", "value": "3.5 months" }, { "start_date": "2024-07-16", "end_date": "2024-08-31", "label": "Beta Testing", "value": "1.5 months" }, { "start_date": "2024-09-01", "end_date": "2024-09-15", "label": "Launch", "value": "2 weeks", "color": "#22C55E" } ], "title": "Product Launch Timeline" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Product Launch Timeline", source="Product Team", components=[ ComponentConfig( component_type="timeline", config={ "events": [ { "start_date": "2024-01-15", "end_date": "2024-03-30", "label": "Research & Design", "value": "2.5 months" }, { "start_date": "2024-04-01", "end_date": "2024-07-15", "label": "Development", "value": "3.5 months" }, { "start_date": "2024-07-16", "end_date": "2024-08-31", "label": "Beta Testing", "value": "1.5 months" }, { "start_date": "2024-09-01", "end_date": "2024-09-15", "label": "Launch", "value": "2 weeks", "color": "#22C55E" } ], "title": "Product Launch Timeline" }, ), ], )) ``` ### Timeline Fields | Field | Type | Required | Description | | - | - | - | - | | `events` | array | Yes | List of timeline events (see below) | | `title` | string | No | Chart title | | `series_color_theme` | string | No | Color theme for events | | `timeline_mode` | `"absolute"` \| `"ordinal"` | No | X-axis spacing mode. `absolute` spaces events proportional to real dates. `ordinal` spaces events evenly. Auto-detected if not set | | `ordinal_label_prefix` | string | No | Prefix for ordinal x-axis labels (e.g., `"Week"` renders as "Week 1", "Week 2") | Each event in the `events` array has: | Field | Type | Required | Description | | - | - | - | - | | `start_date` | string | Yes | Start date in ISO format (e.g., `"2024-01-15"`) | | `end_date` | string | Yes | End date in ISO format (e.g., `"2024-06-30"`) | | `label` | string | Yes | Event label | | `value` | string | No | Value to display (e.g., duration, cost) | | `color` | string | No | Override color for this event (hex format, e.g., `"#FF5733"`) | > [!NOTE] > > When `timeline_mode` is not set, the mode is auto-detected based on the distribution of events. Use `"ordinal"` mode when events have very different time gaps and you want them spaced evenly, or `"absolute"` mode when the proportional time spacing is meaningful. ## Example: Pie Chart Show proportional data as slices of a circle: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Browser Market Share", "source": "Statcounter", "components": [ { "component_type": "pie", "config": { "datasets": [ { "label": "Share", "data": [ {"x": "Chrome", "y": 65.2}, {"x": "Safari", "y": 18.7}, {"x": "Edge", "y": 5.4}, {"x": "Firefox", "y": 3.0}, {"x": "Other", "y": 7.7} ], "units": "%" } ], "title": "Global Desktop Browser Share" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Browser Market Share", source="Statcounter", components=[ ComponentConfig( component_type="pie", config={ "datasets": [ { "label": "Share", "data": [ {"x": "Chrome", "y": 65.2}, {"x": "Safari", "y": 18.7}, {"x": "Edge", "y": 5.4}, {"x": "Firefox", "y": 3.0}, {"x": "Other", "y": 7.7} ], "units": "%" } ], "title": "Global Desktop Browser Share" }, ), ], )) ``` Each data point uses `x` for the slice label and `y` for the value. Slices are colored automatically from the theme palette. ### Pie Chart Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | [categorical datasets](#bar-chart-fields). Each point's `x` is a slice label and `y` its value. Only the first dataset is drawn | | `title` | string | — | Tooltip title | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Histogram Bin raw values into a frequency distribution: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Order Value Distribution", "source": "Internal Analytics", "components": [ { "component_type": "histogram", "config": { "datasets": [ { "label": "Order Values", "data": [ {"x": "1", "y": 12.50}, {"x": "2", "y": 18.30}, {"x": "3", "y": 22.10}, {"x": "4", "y": 25.00}, {"x": "5", "y": 28.40}, {"x": "6", "y": 31.20}, {"x": "7", "y": 33.50}, {"x": "8", "y": 35.00}, {"x": "9", "y": 38.20}, {"x": "10", "y": 41.00}, {"x": "11", "y": 42.50}, {"x": "12", "y": 45.00}, {"x": "13", "y": 47.30}, {"x": "14", "y": 50.00}, {"x": "15", "y": 52.10}, {"x": "16", "y": 55.00}, {"x": "17", "y": 58.40}, {"x": "18", "y": 62.00}, {"x": "19", "y": 68.50}, {"x": "20", "y": 75.20}, {"x": "21", "y": 82.00}, {"x": "22", "y": 90.10}, {"x": "23", "y": 95.00}, {"x": "24", "y": 105.00}, {"x": "25", "y": 120.00} ], "units": "$" } ], "title": "Order Values ($)", "num_bins": 8 } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Order Value Distribution", source="Internal Analytics", components=[ ComponentConfig( component_type="histogram", config={ "datasets": [ { "label": "Order Values", "data": [ {"x": "1", "y": 12.50}, {"x": "2", "y": 18.30}, {"x": "3", "y": 22.10}, {"x": "4", "y": 25.00}, {"x": "5", "y": 28.40}, {"x": "6", "y": 31.20}, {"x": "7", "y": 33.50}, {"x": "8", "y": 35.00}, {"x": "9", "y": 38.20}, {"x": "10", "y": 41.00}, {"x": "11", "y": 42.50}, {"x": "12", "y": 45.00}, {"x": "13", "y": 47.30}, {"x": "14", "y": 50.00}, {"x": "15", "y": 52.10}, {"x": "16", "y": 55.00}, {"x": "17", "y": 58.40}, {"x": "18", "y": 62.00}, {"x": "19", "y": 68.50}, {"x": "20", "y": 75.20}, {"x": "21", "y": 82.00}, {"x": "22", "y": 90.10}, {"x": "23", "y": 95.00}, {"x": "24", "y": 105.00}, {"x": "25", "y": 120.00} ], "units": "$" } ], "title": "Order Values ($)", "num_bins": 8 }, ), ], )) ``` The `y` values are the raw data — bins are computed automatically. Use `num_bins` to set the number of bins, or `bin_width` to set a fixed width. ### Histogram Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | [categorical datasets](#bar-chart-fields). Each point's `y` is one raw value to bin. Only the first dataset is drawn | | `num_bins` | integer | `10` | Number of bins | | `bin_width` | number | — | Width of each bin. Overrides `num_bins` | | `title` | string | — | Tooltip title | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Scatter Plot Show the relationship between two continuous variables: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Height vs Weight", "source": "Survey Data", "components": [ { "component_type": "scatter", "config": { "datasets": [ { "label": "Participants", "data": [ {"x": 165, "y": 60, "label": "P1"}, {"x": 170, "y": 68, "label": "P2"}, {"x": 175, "y": 72, "label": "P3"}, {"x": 180, "y": 78, "label": "P4"}, {"x": 185, "y": 85, "label": "P5"}, {"x": 190, "y": 92, "label": "P6"} ], "x_units": "cm", "y_units": "kg" } ], "title": "Height vs Weight", "x_axis_label": "Height (cm)", "y_axis_label": "Weight (kg)" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Height vs Weight", source="Survey Data", components=[ ComponentConfig( component_type="scatter", config={ "datasets": [ { "label": "Participants", "data": [ {"x": 165, "y": 60, "label": "P1"}, {"x": 170, "y": 68, "label": "P2"}, {"x": 175, "y": 72, "label": "P3"}, {"x": 180, "y": 78, "label": "P4"}, {"x": 185, "y": 85, "label": "P5"}, {"x": 190, "y": 92, "label": "P6"} ], "x_units": "cm", "y_units": "kg" } ], "title": "Height vs Weight", "x_axis_label": "Height (cm)", "y_axis_label": "Weight (kg)" }, ), ], )) ``` Each data point's optional `label` is shown in the tooltip on hover. ### Scatter Plot Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | Datasets (see below) | | `title` | string | — | Tooltip title | | `x_axis_label`, `y_axis_label` | string | — | Axis labels | | `show_point_labels` | boolean | `false` | Shows each point's `label` next to the point. When `false`, labels appear only in the tooltip | | `show_horizontal_grid` | boolean | `false` | Draws horizontal grid lines at the y-axis ticks | | `y_axis_position` | `"left"` \| `"right"` | `"right"` | Side of the y-axis. With `left`, point labels move to the right of each point | | `y_axis_min` | number | Automatic | Lower bound of the y-axis, with a tick at that value | | `y_axis_step` | number | Automatic | Spacing between y-axis ticks | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | Each dataset has: | Field | Type | Required | Description | | - | - | - | - | | `label` | string | Yes | Series name in the legend | | `data` | array | Yes | Points with numeric `x` and `y`, and an optional `label` | | `x_units`, `y_units` | string | No | Units for each axis | | `highlight` | boolean | No | Draws this dataset's point labels in the dataset color. It doesn't change the point color | | `color`, `color_light_mode`, `color_dark_mode` | string | No | See [Color Overrides](#color-overrides) | ## Example: Bubble Chart A scatter plot with a size dimension — useful for plotting three variables at once: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Product Performance", "source": "Internal Analytics", "components": [ { "component_type": "bubble", "config": { "datasets": [ { "label": "Products", "data": [ {"x": 4.2, "y": 12.5, "r": 15, "label": "Widget A"}, {"x": 3.8, "y": 18.2, "r": 22, "label": "Widget B"}, {"x": 4.6, "y": 8.7, "r": 30, "label": "Widget C"}, {"x": 3.5, "y": 22.0, "r": 12, "label": "Widget D"}, {"x": 4.8, "y": 15.3, "r": 25, "label": "Widget E"} ] } ], "title": "Rating vs Margin (bubble = revenue)", "x_axis_label": "Avg Rating (out of 5)", "y_axis_label": "Margin (%)" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Product Performance", source="Internal Analytics", components=[ ComponentConfig( component_type="bubble", config={ "datasets": [ { "label": "Products", "data": [ {"x": 4.2, "y": 12.5, "r": 15, "label": "Widget A"}, {"x": 3.8, "y": 18.2, "r": 22, "label": "Widget B"}, {"x": 4.6, "y": 8.7, "r": 30, "label": "Widget C"}, {"x": 3.5, "y": 22.0, "r": 12, "label": "Widget D"}, {"x": 4.8, "y": 15.3, "r": 25, "label": "Widget E"} ] } ], "title": "Rating vs Margin (bubble = revenue)", "x_axis_label": "Avg Rating (out of 5)", "y_axis_label": "Margin (%)" }, ), ], )) ``` The third dimension is encoded as bubble radius via `r` on each data point. ### Bubble Chart Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | Same shape as [scatter datasets](#scatter-plot-fields). Each point also needs `r`, the value that sets its size | | `title` | string | — | Chart title | | `x_axis_label`, `y_axis_label` | string | — | Axis labels | | `show_point_labels` | boolean | `false` | Shows each point's `label` next to the point | | `show_horizontal_grid` | boolean | `false` | Draws horizontal grid lines at the y-axis ticks | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Choropleth Map Color geographic regions by value. `map_type` is `"us_states"` or `"world"`: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Population by State", "source": "US Census", "components": [ { "component_type": "choropleth", "config": { "datasets": [ { "label": "Population (millions)", "data": [ {"x": "California", "y": 39.0}, {"x": "Texas", "y": 30.5}, {"x": "Florida", "y": 22.6}, {"x": "New York", "y": 19.5}, {"x": "Pennsylvania", "y": 12.9}, {"x": "Illinois", "y": 12.5}, {"x": "Ohio", "y": 11.8}, {"x": "Georgia", "y": 11.0}, {"x": "North Carolina", "y": 10.7}, {"x": "Michigan", "y": 10.0} ], "units": "M" } ], "title": "US State Population", "map_type": "us_states" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Population by State", source="US Census", components=[ ComponentConfig( component_type="choropleth", config={ "datasets": [ { "label": "Population (millions)", "data": [ {"x": "California", "y": 39.0}, {"x": "Texas", "y": 30.5}, {"x": "Florida", "y": 22.6}, {"x": "New York", "y": 19.5}, {"x": "Pennsylvania", "y": 12.9}, {"x": "Illinois", "y": 12.5}, {"x": "Ohio", "y": 11.8}, {"x": "Georgia", "y": 11.0}, {"x": "North Carolina", "y": 10.7}, {"x": "Michigan", "y": 10.0} ], "units": "M" } ], "title": "US State Population", "map_type": "us_states" }, ), ], )) ``` For US states, `x` is the full state name (e.g. `"California"`). For world maps, use `"map_type": "world"` and country names. ### Choropleth Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | [categorical datasets](#bar-chart-fields). Each point's `x` is a region name (a US state or a country) and `y` its value. Only the first dataset is drawn | | `map_type` | `"us_states"` \| `"world"` | `"us_states"` | Which map to draw | | `title` | string | — | Chart title | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Heatmap A 2D grid where color encodes value. Useful for things like correlation matrices or activity by day-and-hour: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Sales by Day & Shift", "source": "POS System", "components": [ { "component_type": "heatmap", "config": { "data": [ {"x": "Mon", "y": "Morning", "v": 12}, {"x": "Mon", "y": "Midday", "v": 38}, {"x": "Mon", "y": "Afternoon", "v": 22}, {"x": "Mon", "y": "Evening", "v": 45}, {"x": "Tue", "y": "Morning", "v": 15}, {"x": "Tue", "y": "Midday", "v": 42}, {"x": "Tue", "y": "Afternoon", "v": 28}, {"x": "Tue", "y": "Evening", "v": 50}, {"x": "Wed", "y": "Morning", "v": 18}, {"x": "Wed", "y": "Midday", "v": 45}, {"x": "Wed", "y": "Afternoon", "v": 30}, {"x": "Wed", "y": "Evening", "v": 55}, {"x": "Thu", "y": "Morning", "v": 20}, {"x": "Thu", "y": "Midday", "v": 48}, {"x": "Thu", "y": "Afternoon", "v": 35}, {"x": "Thu", "y": "Evening", "v": 62}, {"x": "Fri", "y": "Morning", "v": 25}, {"x": "Fri", "y": "Midday", "v": 55}, {"x": "Fri", "y": "Afternoon", "v": 40}, {"x": "Fri", "y": "Evening", "v": 78} ], "title": "Orders by Day & Shift", "x_axis_label": "Day", "y_axis_label": "Shift", "v_label": "Orders" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Sales by Day & Shift", source="POS System", components=[ ComponentConfig( component_type="heatmap", config={ "data": [ {"x": "Mon", "y": "Morning", "v": 12}, {"x": "Mon", "y": "Midday", "v": 38}, {"x": "Mon", "y": "Afternoon", "v": 22}, {"x": "Mon", "y": "Evening", "v": 45}, {"x": "Tue", "y": "Morning", "v": 15}, {"x": "Tue", "y": "Midday", "v": 42}, {"x": "Tue", "y": "Afternoon", "v": 28}, {"x": "Tue", "y": "Evening", "v": 50}, {"x": "Wed", "y": "Morning", "v": 18}, {"x": "Wed", "y": "Midday", "v": 45}, {"x": "Wed", "y": "Afternoon", "v": 30}, {"x": "Wed", "y": "Evening", "v": 55}, {"x": "Thu", "y": "Morning", "v": 20}, {"x": "Thu", "y": "Midday", "v": 48}, {"x": "Thu", "y": "Afternoon", "v": 35}, {"x": "Thu", "y": "Evening", "v": 62}, {"x": "Fri", "y": "Morning", "v": 25}, {"x": "Fri", "y": "Midday", "v": 55}, {"x": "Fri", "y": "Afternoon", "v": 40}, {"x": "Fri", "y": "Evening", "v": 78} ], "title": "Orders by Day & Shift", "x_axis_label": "Day", "y_axis_label": "Shift", "v_label": "Orders" }, ), ], )) ``` Heatmap takes a flat `data` array of `{x, y, v}` points (not a `datasets` list). `v` is the value used to determine color intensity. ### Heatmap Fields | Field | Type | Default | Description | | - | - | - | - | | `data` | array | *required* | Cells with `x` and `y` category labels and a numeric `v` (or `null`) that sets the color intensity | | `title` | string | — | Chart title | | `x_axis_label`, `y_axis_label` | string | — | Axis labels | | `v_label` | string | — | Name of the value, shown in tooltips | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Box Plot Show the statistical distribution (min, Q1, median, Q3, max) of values across categories. Provide raw values per category — quartiles are computed for you: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Response Time by Region", "source": "Telemetry", "components": [ { "component_type": "boxplot", "config": { "categories": [ { "category": "US-East", "values": [42, 48, 51, 55, 58, 60, 62, 65, 68, 72, 75, 80, 85, 90, 110] }, { "category": "US-West", "values": [38, 42, 45, 48, 50, 52, 55, 58, 62, 65, 70, 75, 82, 95, 120] }, { "category": "EU", "values": [55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 110, 120, 135, 150, 180] }, { "category": "APAC", "values": [80, 90, 100, 110, 120, 130, 140, 150, 160, 175, 190, 210, 240, 280, 350] } ], "title": "p50/p95 Response Time by Region", "y_axis_label": "Response time", "units": "ms" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Response Time by Region", source="Telemetry", components=[ ComponentConfig( component_type="boxplot", config={ "categories": [ { "category": "US-East", "values": [ 42, 48, 51, 55, 58, 60, 62, 65, 68, 72, 75, 80, 85, 90, 110 ] }, { "category": "US-West", "values": [ 38, 42, 45, 48, 50, 52, 55, 58, 62, 65, 70, 75, 82, 95, 120 ] }, { "category": "EU", "values": [ 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 110, 120, 135, 150, 180 ] }, { "category": "APAC", "values": [ 80, 90, 100, 110, 120, 130, 140, 150, 160, 175, 190, 210, 240, 280, 350 ] } ], "title": "p50/p95 Response Time by Region", "y_axis_label": "Response time", "units": "ms" }, ), ], )) ``` Box plot uses `categories` (not `datasets`) and each category has a `values` array of raw numbers. ### Box Plot Fields | Field | Type | Default | Description | | - | - | - | - | | `categories` | array | *required* | Entries with a `category` label and a `values` array of raw numbers. Tako computes the minimum, quartiles, median, and maximum | | `title` | string | — | Chart title | | `y_axis_label` | string | — | Y-axis label | | `units` | string | — | Value units | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Treemap Show hierarchical data as area-proportional rectangles — good for spend breakdowns or composition: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Cloud Spend by Service", "source": "AWS Cost Explorer", "components": [ { "component_type": "treemap", "config": { "datasets": [ { "label": "Monthly Spend", "data": [ {"x": "EC2", "y": 12500}, {"x": "RDS", "y": 6800}, {"x": "S3", "y": 3200}, {"x": "Lambda", "y": 1450}, {"x": "CloudFront", "y": 980}, {"x": "DynamoDB", "y": 720}, {"x": "ElastiCache", "y": 540}, {"x": "Other", "y": 410} ], "units": "$" } ], "title": "AWS Spend by Service ($)" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Cloud Spend by Service", source="AWS Cost Explorer", components=[ ComponentConfig( component_type="treemap", config={ "datasets": [ { "label": "Monthly Spend", "data": [ {"x": "EC2", "y": 12500}, {"x": "RDS", "y": 6800}, {"x": "S3", "y": 3200}, {"x": "Lambda", "y": 1450}, {"x": "CloudFront", "y": 980}, {"x": "DynamoDB", "y": 720}, {"x": "ElastiCache", "y": 540}, {"x": "Other", "y": 410} ], "units": "$" } ], "title": "AWS Spend by Service ($)" }, ), ], )) ``` Each rectangle's area is proportional to its `y` value. ### Treemap Fields | Field | Type | Default | Description | | - | - | - | - | | `datasets` | array | *required* | [categorical datasets](#bar-chart-fields). Each point's `x` is a rectangle label and `y` sets its area. Only the first dataset is drawn | | `title` | string | — | Tooltip title | | `legend`, `series_color_theme` | | | See [Shared Config Options](#shared-config-options). | ## Example: Waterfall Show how a starting value changes through a series of additions and subtractions to reach an ending value. Use `is_anchor: true` for totals/subtotals (bars that start at zero): ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Income Statement", "source": "Q3 Earnings", "components": [ { "component_type": "waterfall", "config": { "items": [ {"category": "Revenue", "value": 100.0, "is_anchor": true}, {"category": "COGS", "value": -38.0}, {"category": "Gross Profit", "value": 62.0, "is_anchor": true}, {"category": "OpEx", "value": -28.0}, {"category": "Operating Income", "value": 34.0, "is_anchor": true}, {"category": "Tax", "value": -7.0}, {"category": "Net Income", "value": 27.0, "is_anchor": true} ], "title": "Q3 Income Breakdown", "units": "$" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Income Statement", source="Q3 Earnings", components=[ ComponentConfig( component_type="waterfall", config={ "items": [ {"category": "Revenue", "value": 100.0, "is_anchor": True}, {"category": "COGS", "value": -38.0}, { "category": "Gross Profit", "value": 62.0, "is_anchor": True }, {"category": "OpEx", "value": -28.0}, { "category": "Operating Income", "value": 34.0, "is_anchor": True }, {"category": "Tax", "value": -7.0}, {"category": "Net Income", "value": 27.0, "is_anchor": True} ], "title": "Q3 Income Breakdown", "units": "$" }, ), ], )) ``` By default, positive deltas are green and negative deltas are red. Anchor bars (`is_anchor: true`) are drawn as solid bars rising from zero. ### Waterfall Fields | Field | Type | Default | Description | | - | - | - | - | | `items` | array | *required* | Entries with a `category` label, a `value` (the change, positive or negative), and an optional `is_anchor`. An anchor bar starts from zero, so use it for totals such as net income | | `title` | string | — | Chart title | | `units` | string | — | Value units | | `series_color_theme` | string | `"red_bad_green_good"` | See [Shared Config Options](#shared-config-options). | | `legend` | object | — | See [Shared Config Options](#shared-config-options). | ## Example: Sankey Show directed flows between nodes — revenue splitting into cost and profit branches, a user funnel, energy flow, or any weighted graph. Provide a list of `nodes` and a list of `links`; every link's `source` and `target` must reference a node `id`. Links must form a DAG (no cycles or self-loops); duplicate `(source, target)` pairs are summed. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Q1 Income Statement", "source": "Company Financials", "components": [ { "component_type": "sankey", "config": { "nodes": [ {"id": "data_center", "label": "Data Center"}, {"id": "gaming", "label": "Gaming"}, {"id": "revenue", "label": "Revenue"}, {"id": "cogs", "label": "Cost of Revenue", "polarity": "negative"}, {"id": "gross", "label": "Gross Profit", "polarity": "positive"}, {"id": "opex", "label": "Operating Expenses", "polarity": "negative"}, {"id": "op_profit", "label": "Operating Profit", "polarity": "positive"}, {"id": "tax", "label": "Tax", "polarity": "negative"}, {"id": "net_profit", "label": "Net Profit", "polarity": "positive"} ], "links": [ {"source": "data_center", "target": "revenue", "value": 22.6}, {"source": "gaming", "target": "revenue", "value": 2.6}, {"source": "revenue", "target": "cogs", "value": 5.6}, {"source": "revenue", "target": "gross", "value": 20.4}, {"source": "gross", "target": "opex", "value": 3.5}, {"source": "gross", "target": "op_profit", "value": 17.3}, {"source": "op_profit", "target": "tax", "value": 2.4}, {"source": "op_profit", "target": "net_profit","value": 14.9} ], "title": "Revenue to Net Profit", "units": "$B" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Q1 Income Statement", source="Company Financials", components=[ ComponentConfig( component_type="sankey", config={ "nodes": [ {"id": "data_center", "label": "Data Center"}, {"id": "gaming", "label": "Gaming"}, {"id": "revenue", "label": "Revenue"}, { "id": "cogs", "label": "Cost of Revenue", "polarity": "negative" }, { "id": "gross", "label": "Gross Profit", "polarity": "positive" }, { "id": "opex", "label": "Operating Expenses", "polarity": "negative" }, { "id": "op_profit", "label": "Operating Profit", "polarity": "positive" }, {"id": "tax", "label": "Tax", "polarity": "negative"}, { "id": "net_profit", "label": "Net Profit", "polarity": "positive" } ], "links": [ { "source": "data_center", "target": "revenue", "value": 22.6 }, {"source": "gaming", "target": "revenue", "value": 2.6}, {"source": "revenue", "target": "cogs", "value": 5.6}, {"source": "revenue", "target": "gross", "value": 20.4}, {"source": "gross", "target": "opex", "value": 3.5}, {"source": "gross", "target": "op_profit", "value": 17.3}, {"source": "op_profit", "target": "tax", "value": 2.4}, { "source": "op_profit", "target": "net_profit", "value": 14.9 } ], "title": "Revenue to Net Profit", "units": "$B" }, ), ], )) ``` A node's `polarity` sets its color: `positive` renders green, `negative` red, and `neutral` (the default) gray. Each flow shades from its source node's color to its target node's color. Use this to make a profit-vs-cost story read at a glance — leave upstream/source nodes neutral so only the meaningful splits carry color. ### Sankey fields | Field | Required | Description | | - | - | - | | `nodes` | yes | List of nodes (min 2). Each has `id` (canonical key) and `label` (display string), plus optional `polarity`. | | `nodes[].polarity` | no | `positive`, `negative`, or `neutral` (default). Sets the node's color, which the flows into and out of it shade toward. | | `links` | yes | List of directed flows (min 1). Each has `source` (node id), `target` (node id), and `value` (positive number). | | `title` | no | Chart title. | | `units` | no | Units for flow values (e.g. `$`, `$B`, `%`), used in tooltips. | ## Example: Marimekko Show a two-level part-to-whole breakdown as a mosaic of variable-width stacked columns — market share by region and brand, spend by department and category, or contract obligations by agency and vendor. Each column's width is proportional to its total, and each stacked segment's height is its share within that column. Marimekko takes the same flat `data` array of `{x, y, v}` points as the heatmap: `x` is the column (X dimension), `y` is the stacked segment (Y dimension), and `v` is the cell magnitude. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Smartphone Market Share", "source": "Internal Analytics", "components": [ { "component_type": "marimekko", "config": { "data": [ {"x": "North America", "y": "Apple", "v": 52}, {"x": "North America", "y": "Samsung", "v": 28}, {"x": "North America", "y": "Google", "v": 9}, {"x": "Europe", "y": "Samsung", "v": 34}, {"x": "Europe", "y": "Apple", "v": 31}, {"x": "Europe", "y": "Xiaomi", "v": 18}, {"x": "Asia Pacific", "y": "Xiaomi", "v": 25}, {"x": "Asia Pacific", "y": "Samsung", "v": 22}, {"x": "Asia Pacific", "y": "Oppo", "v": 19}, {"x": "Asia Pacific", "y": "Apple", "v": 15} ], "title": "Units Sold by Region & Brand", "x_axis_label": "Region", "y_axis_label": "Brand", "units": "M units", "top_n_columns": 8, "top_m_segments": 6 } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Smartphone Market Share", source="Internal Analytics", components=[ ComponentConfig( component_type="marimekko", config={ "data": [ {"x": "North America", "y": "Apple", "v": 52}, {"x": "North America", "y": "Samsung", "v": 28}, {"x": "North America", "y": "Google", "v": 9}, {"x": "Europe", "y": "Samsung", "v": 34}, {"x": "Europe", "y": "Apple", "v": 31}, {"x": "Europe", "y": "Xiaomi", "v": 18}, {"x": "Asia Pacific", "y": "Xiaomi", "v": 25}, {"x": "Asia Pacific", "y": "Samsung", "v": 22}, {"x": "Asia Pacific", "y": "Oppo", "v": 19}, {"x": "Asia Pacific", "y": "Apple", "v": 15} ], "title": "Units Sold by Region & Brand", "x_axis_label": "Region", "y_axis_label": "Brand", "units": "M units", "top_n_columns": 8, "top_m_segments": 6 }, ), ], )) ``` Cells that share an `(x, y)` pair are summed first; a pair is then dropped only if its net value is non-positive (so a downward adjustment nets against a positive value in the same cell rather than being dropped on its own). The largest `top_n_columns` columns and, within each, the largest `top_m_segments` segments render individually; everything else rolls into an "Other" column / "Other" segment so the mosaic always conserves the total. Segments are colored from the theme palette and labeled on the chart canvas (no separate legend). ### Marimekko fields | Field | Required | Description | | - | - | - | | `data` | yes | List of `{x, y, v}` cells. `x` is the column label, `y` is the stacked segment label, `v` is the cell magnitude. | | `title` | no | Chart title. | | `x_axis_label` | no | Label for the columns dimension (e.g. `Region`). | | `y_axis_label` | no | Label for the segments dimension (e.g. `Brand`). | | `units` | no | Units for values (e.g. `$`, `%`), used in tooltips and on-canvas labels. | | `top_n_columns` | no | Columns shown individually before the rest roll into an "Other" column (default `8`). | | `top_m_segments` | no | Segments shown individually per column before the rest roll into an "Other" segment (default `6`). | ## Example: Person Card Build a profile card with career, education, and about tabs from an [Exa](https://exa.ai) person search result. Pass the Exa result object as the `config`. A `person_card` must be the only component in the request. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "components": [ { "component_type": "person_card", "config": { "title": "Jane Doe | Engineer - Acme Corp", "url": "https://linkedin.com/in/janedoe", "image": "https://example.com/photo.jpg", "highlights": ["Technical Skills: Python, Go, TypeScript"], "entities": [ { "id": "https://exa.ai/library/person/jane-doe", "type": "person", "properties": { "name": "Jane Doe", "location": "San Francisco, CA", "workHistory": [ { "title": "Senior Engineer", "company": {"name": "Acme Corp"}, "dates": {"from": "2021-03", "to": null} } ], "educationHistory": [ { "degree": "BS Computer Science", "institution": {"name": "State University"}, "dates": {"from": "2013", "to": "2017"} } ] } } ] } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( components=[ ComponentConfig( component_type="person_card", config={ "title": "Jane Doe | Engineer - Acme Corp", "url": "https://linkedin.com/in/janedoe", "image": "https://example.com/photo.jpg", "highlights": ["Technical Skills: Python, Go, TypeScript"], "entities": [ { "id": "https://exa.ai/library/person/jane-doe", "type": "person", "properties": { "name": "Jane Doe", "location": "San Francisco, CA", "workHistory": [ { "title": "Senior Engineer", "company": {"name": "Acme Corp"}, "dates": {"from": "2021-03", "to": None} } ], "educationHistory": [ { "degree": "BS Computer Science", "institution": {"name": "State University"}, "dates": {"from": "2013", "to": "2017"} } ] } } ] }, ), ], )) ``` ### Person Card Fields | Field | Type | Required | Description | | - | - | - | - | | `entities` | array | Yes | Exa person entities, each with an `id`, `type: "person"`, and `properties`. Tako reads the first one | | `title` | string | No | Exa result title, such as `"Jane Doe \| Engineer - Acme Corp"` | | `url` | string | No | Profile URL | | `author` | string | No | Person's name | | `image` | string | No | Profile image URL | | `highlights` | array | No | Text highlights. Tako extracts skills from them | An entity's `properties` accept `name`, `firstName`, `lastName`, `location`, `workHistory` (entries with `title`, `location`, `company.name`, and `dates.from` and `dates.to`), and `educationHistory` (entries with `degree`, `institution.name`, and `dates`). The card title is the person's name, taken from `properties.name`, then `author`, then the part of `title` before the `|`. A request-level `title` overrides it. ## Combining Multiple Components Cards can contain multiple components stacked together. This lets you build richer visualizations by pairing related data. ### Financial Boxes + Timeseries Chart Pair summary metrics with a trend chart: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "AAPL Quarterly Overview", "source": "SEC Filings", "components": [ { "component_type": "financial_boxes", "config": { "items": [ { "header": "Revenue", "value": "$94.9B", "growth": {"formattedValue": "6.1% YoY", "value": 0.061} }, { "header": "Net Income", "value": "$23.6B", "growth": {"formattedValue": "-3.4% YoY", "value": -0.034} }, { "header": "EPS", "value": "$1.64" } ] } }, { "component_type": "generic_timeseries", "config": { "datasets": [ { "label": "Revenue", "data": [ {"x": "2023-Q1", "y": 94.8}, {"x": "2023-Q2", "y": 81.8}, {"x": "2023-Q3", "y": 89.5}, {"x": "2023-Q4", "y": 119.6}, {"x": "2024-Q1", "y": 90.8}, {"x": "2024-Q2", "y": 85.8}, {"x": "2024-Q3", "y": 94.9} ], "units": "$" } ], "chart_type": "bar", "title": "Quarterly Revenue ($B)" } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="AAPL Quarterly Overview", source="SEC Filings", components=[ ComponentConfig( component_type="financial_boxes", config={ "items": [ { "header": "Revenue", "value": "$94.9B", "growth": {"formattedValue": "6.1% YoY", "value": 0.061} }, { "header": "Net Income", "value": "$23.6B", "growth": {"formattedValue": "-3.4% YoY", "value": -0.034} }, {"header": "EPS", "value": "$1.64"} ] }, ), ComponentConfig( component_type="generic_timeseries", config={ "datasets": [ { "label": "Revenue", "data": [ {"x": "2023-Q1", "y": 94.8}, {"x": "2023-Q2", "y": 81.8}, {"x": "2023-Q3", "y": 89.5}, {"x": "2023-Q4", "y": 119.6}, {"x": "2024-Q1", "y": 90.8}, {"x": "2024-Q2", "y": 85.8}, {"x": "2024-Q3", "y": 94.9} ], "units": "$" } ], "chart_type": "bar", "title": "Quarterly Revenue ($B)" }, ), ], )) ``` ### Header + Bar Chart Add a styled header above any chart: ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/thin_viz/create/ \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Team Headcount", "components": [ { "component_type": "header", "config": { "title": "Team Headcount by Department", "subtitle": "As of January 2025" } }, { "component_type": "categorical_bar", "config": { "datasets": [ { "label": "Headcount", "data": [ {"x": "Engineering", "y": 142}, {"x": "Sales", "y": 89}, {"x": "Marketing", "y": 54}, {"x": "Operations", "y": 67} ] } ] } } ] }' ``` ```python Python theme={null} # Reuses the client from the first example above card = client.create_card(CreateCardRequest( title="Team Headcount", components=[ ComponentConfig( component_type="header", config={ "title": "Team Headcount by Department", "subtitle": "As of January 2025" }, ), ComponentConfig( component_type="categorical_bar", config={ "datasets": [ { "label": "Headcount", "data": [ {"x": "Engineering", "y": 142}, {"x": "Sales", "y": 89}, {"x": "Marketing", "y": 54}, {"x": "Operations", "y": 67} ] } ] }, ), ], )) ``` > [!NOTE] > > Components render in the order they appear in the `components` array - top to bottom on the card. ## Available Components | Component | Description | | - | - | | [`header`](#header-fields) | Card header with title and optional subtitle | | [`generic_timeseries`](#creating-a-card) | Line, bar, or scatter timeseries charts | | [`categorical_bar`](#example-bar-chart) | Bar chart with categorical x-axis (regions, products, etc.) | | [`data_table_chart`](#example-data-table-chart) | Bar chart with auto-generated data table below | | [`financial_boxes`](#example-financial-boxes) | Financial metric boxes with values and growth indicators | | [`table`](#example-data-table) | Data table with configurable columns (string, number, date, percent, currency, boolean, rating), sorting, formatting, and pagination | | [`timeline`](#example-timeline) | Timeline chart showing events or periods along a time axis | | [`pie`](#example-pie-chart) | Pie chart showing proportional data as slices of a circle | | [`histogram`](#example-histogram) | Histogram chart showing frequency distribution of values | | [`scatter`](#example-scatter-plot) | Scatter plot showing relationships between two continuous variables | | [`bubble`](#example-bubble-chart) | Bubble chart (scatter plot with size dimension) for 3-variable data | | [`choropleth`](#example-choropleth-map) | Choropleth map showing geographic data with color intensity by region | | [`heatmap`](#example-heatmap) | 2D heatmap with color intensity representing values | | [`boxplot`](#example-box-plot) | Box plot showing statistical distributions (min, Q1, median, Q3, max) | | [`treemap`](#example-treemap) | Treemap chart showing hierarchical data as proportional rectangles | | [`waterfall`](#example-waterfall) | Waterfall chart showing incremental positive/negative changes | | [`sankey`](#example-sankey) | Sankey diagram showing directed flows between nodes (revenue → cost/profit, funnels, energy flow) | | [`marimekko`](#example-marimekko) | Marimekko (mosaic) chart showing a two-level part-to-whole breakdown as variable-width stacked columns (region × brand, agency × vendor) | | [`top_level_metric`](#example-top-level-metric) | One large headline number or phrase with a caption | | [`person_card`](#example-person-card) | Person profile with career, education, and about tabs, built from an Exa person search result | # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/contents/for-coding-agent The build reference for coding agents integrating Tako Contents. Self-contained and copy-paste-ready — endpoint, X-API-Key auth, the url/mode request, both delivery modes, the CSV export behind a Tako card and the full extracted text behind a web page, and runnable cURL examples. Use this page to fetch the full content behind a Search or Answer result. This page is a complete, self-contained reference for building against **Tako Contents** — the endpoint that downloads the structured data behind a [Search](/documentation/integrating-tako/search/for-coding-agent) or [Answer](/documentation/integrating-tako/answer/for-coding-agent) result. 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 the auto-generated schema, see the [Contents API reference](/api-reference/contents); for a narrative introduction, see the [Overview](/documentation/integrating-tako/contents/overview). ## What Contents does `POST https://tako.com/api/v1/contents` takes a result `url` and returns the **full content behind** that result — one of two forms, depending on what the URL points to: a **structured-data CSV export** of a Tako card's underlying data, or the **full extracted text** of any other web page. You don't pass a `format` — Tako detects the right content from the URL and the response reports what it produced. Reach for it when you've already found the right result with Search or Answer and you want the **content itself**, fast, in a form you can parse and feed to a model. The CSV *is* the Tako card's data and the extracted text *is* the web page — there's no synthesis and nothing to re-derive — which makes Contents well suited to agentic loops that need precise, source-grounded inputs quickly. * **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). * **Prerequisite:** a result is downloadable only when it carries a `content` descriptor in the Search/Answer response. Use that result's URL here — a card's `webpage_url` or a web result's `url`. If a result has **no** `content` descriptor, its data isn't exportable — don't call Contents on it; the endpoint will refuse it (see [Errors](#errors)). ## Minimal working example This downloads the data behind a Tako card. The default `url` mode returns a short-lived presigned link; fetch that link to get the CSV. ```bash cURL theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/" }' ``` ```json Response theme={null} { "contents": [ { "format": "csv", "source_url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/", "cost": 0.021, "url": "https://tako.com/...signed-download...", "expires_at": "2026-06-29T18:30:00Z" } ], "request_id": "9c1b..." } ``` ## Request parameters The body is a `ContentsRequest`. Only `url` is required. | Parameter | Type | Default | Description | | - | - | - | - | | `url` | string | — *(required)* | The result URL to download. A Tako card's `webpage_url`, or a list row's `tako_url`, yields a CSV of the card's data; any other URL yields the page's extracted text. | | `mode` | string enum | `url` | Delivery mode. `url` returns a short-lived presigned download link; `inline` returns the content in the response body. | | `max_rows` | integer | the whole card in `url` mode; 2,000 rows in `inline` mode | **Card URLs only; web URLs ignore it.** Rows to return, clamped to the 100,000-row ceiling. Every card format honors it, `card_json` included. See [Which rows a capped export keeps](#which-rows-a-capped-export-keeps). This is the field that decides the bill: every row returned is billed. Omit it in `url` mode to get the whole card. See [Contents pricing](/documentation/integrating-tako/contents/pricing). | | `offset` | integer | `0` | Rows to skip before the export starts, in the card's own row order. Use it with `max_rows` to export one slice of a long list, such as rows 100 to 149 with `offset: 100` and `max_rows: 50`. A card with a date column, a sports card, or `card_json` answers `422` to an `offset` above 0. | | `quote_only` | boolean | `false` | Return the `cost` for this download without fetching it or being charged. | | `premium` | boolean | `false` | Reveal and bill a card's premium columns. See [Premium columns](#premium-columns). | ## Delivery modes `mode` controls how the content comes back: | `mode` | Returns | Use when | | - | - | - | | `url` *(default)* | A presigned, short-lived download `url` plus `expires_at`. | You want to stream or store the file, it may be large, or an agent will parse it. | | `inline` | The content directly in the response (`data`), with `total_rows` and `truncated`. | You want a small card's data in-process immediately. Returns 2,000 rows unless you set `max_rows`. Check `truncated` and `total_rows`. | > [!WARNING] > > **Don't read a large export into a model's context.** A card export can hold up to 100,000 rows, and at about 15 tokens per row, 10,000 rows cost about 150,000 tokens. Write the export to a file, on disk or in memory, and have your agent parse it with code. Give the model only the rows or results it needs. To cap the export instead, set `max_rows`. ```bash inline mode theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/", "mode": "inline" }' ``` ## Give a large export to an agent A model reads every row you pass it as tokens, so keep the export out of the model's context. Write the export to a file, on disk or in memory, and give your agent a code tool that parses it. The tool returns only what the model needs, such as the latest 12 rows or a computed growth rate. ```bash On disk theme={null} DOWNLOAD_URL=$(curl -s -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/"}' | jq -r '.contents[0].url') curl -s -o card.csv "$DOWNLOAD_URL" ``` ```python In memory theme={null} import os import pandas as pd import requests response = requests.post( "https://tako.com/api/v1/contents", headers={"X-API-Key": os.environ["TAKO_API_KEY"]}, json={"url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/"}, timeout=30, ) response.raise_for_status() df = pd.read_csv(response.json()["contents"][0]["url"]) ``` On disk, point the agent's code tool at `card.csv`. In memory, have your tool functions read `df`. Either way, the model sees the tool's result, not the rows. The download `url` is short-lived, so fetch it before `expires_at`. ## What a response looks like A `200` returns a `ContentsResponse`. `contents` is a list (today it always carries exactly one item, so the contract stays stable if a result ever yields multiple artifacts). | Field | Type | Description | | - | - | - | | `contents` | array | The downloadable artifacts (see `ContentItem` below). | | `request_id` | string | Always present — log it for support/debugging. | Each `ContentItem` carries: | Field | Type | Description | | - | - | - | | `format` | string | `csv` for a Tako card, `text` for a web page. | | `cost` | number | USD cost of this download. A card CSV bills a per-export baseline plus a per-row rate; web text extraction bills per page. See [Contents pricing](/documentation/integrating-tako/contents/pricing). | | `source_url` | string | The originating result URL that was resolved. | | `url` | string \| null | **url mode:** presigned, short-lived download link. Null in inline mode. | | `expires_at` | string \| null | **url mode:** ISO‑8601 UTC time when `url` expires. Null in inline mode. | | `data` | string \| null | **inline mode:** the content itself (CSV text or web text). Null in url mode. | | `total_rows` | integer \| null | **inline mode:** total rows in the source CSV (so you can tell whether the 100,000-row ceiling truncated it). | | `truncated` | boolean | **inline mode:** `true` when the export returned fewer rows than the card holds — because `max_rows` cut it, or because the card exceeds the 100,000-row ceiling. | | `offset` | integer \| null | The request's `offset`, echoed on a card export. Null for web text and quotes. | | `export_pricing` | object \| null | The card's rate card, including `baseline_usd`, `row_cpm_usd`, `premium_row_cpm_usd` (keyed by premium class), and `max_rows_ceiling`. See [Contents pricing](/documentation/integrating-tako/contents/pricing). Null for web text. | | `manifest` | array \| null | One entry per exported column, in column order: `name`, `dtype`, `unit`, `metric`, `entity`, and `premium_class` (`company_record`, `person_contact`, or null). | | `premium` | boolean | `true` when the item's premium cells are revealed, which happens only when the request sent `premium: true`. | | `premium_rows` | object \| null | Per premium class (`company_record`, `person_contact`), the number of distinct companies or people that the delivered rows hold a premium value for, revealed or not. Empty for a card with no premium columns. Null for web text. | | `redacted_cells` | object \| null | Column header to the 0-based positions, among the delivered rows, whose premium value was withheld. Null when nothing was withheld. | ## The CSV behind a Tako card For a Tako card, the CSV has a header row naming each series exactly as the card sources it, then one row per data point. A time-series card is keyed by an ISO‑8601 `Timestamp` (with timezone offset), one column per series: ```csv theme={null} Timestamp,usaspending_sum_obligation - Palantir Technologies Inc. Federal Contract Obligations 2007-10-01 00:00:00+00:00,135211.06 2008-10-01 00:00:00+00:00,4378216.96 2009-10-01 00:00:00+00:00,5801023.5 2010-10-01 00:00:00+00:00,20915978.15 2011-10-01 00:00:00+00:00,39889371.870000005 2012-10-01 00:00:00+00:00,33785816.41 2013-10-01 00:00:00+00:00,75264989.77 ``` …one row per fiscal year through the latest data point. The header is self-describing, so the values are ready to compute on and to attribute back to the card. ## People and company list cards A people or company list card from [Search](/documentation/integrating-tako/search/for-coding-agent#people-and-company-lists) shows the top 25 matches. Exporting it runs the card's search again and returns up to 1,000 rows. The search card's `content.total_rows` is the most rows that export returns, and `content.truncated` is `true` when that's more than the card shows. Pass `total_rows` as `max_rows` to get them all. * **Row links.** Each row carries a `tako_url` that opens a card for that one person (`https://tako.com/card/pe_…/`) or company (`https://tako.com/card/co_…/`). Pass that URL as `url` to export just that row. * **`card_json`.** A people list returns a `people` array of records in rank order, each with a `person_id`, and a company list a `companies` array of records, each with an `entity_id`. `card_json` never carries premium values: each record lists the premium fields that hold a value in `withheld_fields`. * **Paging.** List cards keep their first rows, so `offset` and `max_rows` export one slice, for example to reveal the contacts of 50 people at a time. The row order can change between calls, so check the rows you receive, for example by name. ## Premium columns Some columns cost more to reveal than the rest of the card. These are the premium columns today. On a real export, `manifest[].premium_class` is the authority: it marks each premium column with its class. | Class | Columns | Billed per | | - | - | - | | `person_contact` | Work email, personal email, and phone on a people list | Distinct person | | `company_record` | Estimated revenue, LinkedIn followers, parent, and subsidiaries on a company list | Distinct company | By default these cells are withheld. A withheld cell reads `[premium: withheld]` in CSV and is `null` in `json_records` and `json_compact`, and `redacted_cells` lists where they are. Search and Answer never reveal them. To reveal them, send the export again with `"premium": true`: ```bash theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tako.com/card/CARD_ID/", "premium": true, "max_rows": 50 }' ``` A premium export bills `export_pricing.premium_row_cpm_usd[class]` per 1,000 premium records, on top of the standard row rate, and `premium_rows` reports the count. [Contents pricing](/documentation/integrating-tako/contents/pricing#premium-columns) defines a premium record and explains how to estimate the charge before you export. * `premium` has no effect on a card without premium columns or on a web URL. * Contents answers `400` to `premium: true` with `content_format: "card_json"`. * Over MPP, `premium: true` returns `403` before any charge, unless `quote_only` is also `true`. ## Which rows a capped export keeps When a card holds more rows than `max_rows` or the 100,000-row ceiling, Contents returns part of it. Which part depends on the format. **`csv`, `json_records`, and `json_compact`:** * **A card with a date column** keeps the rows with the latest dates. Contents counts back from the latest date in the card's data, not from today. On a card with future rows, such as a season schedule or a weather forecast, a capped export keeps the far end of the future and drops the dates nearest today. * **A card without a date column**, such as a ranking or a comparison table, keeps its first rows. * Either way, the rows stay in the card's own order. A card that shows newest first exports newest first. **`card_json`** cuts each card type its own way. A price or indicator series keeps its newest readings. A forecast, a schedule, or a ranking keeps its first records. So a capped schedule card can return its latest games as `csv` and its earliest games as `card_json`. To get a specific stretch of a large card, don't rely on the cap. Check `truncated` and `total_rows`, and raise `max_rows` until `truncated` is `false`, or use a card that covers a narrower range. ## When to use Contents Contents fetches the **full content behind** a result you already have from [Search](/documentation/integrating-tako/search/for-coding-agent) or [Answer](/documentation/integrating-tako/answer/for-coding-agent) — a **structured-data CSV export** of a Tako card's underlying series, or the **full extracted text** of any other web page. Reach for it when: * **You've already found the result and want the content itself, not prose.** There's no synthesis and nothing to re-derive — the CSV *is* the card's data, and the extracted text *is* the page, ready to use directly. * **It's a second step, not a search.** Contents operates on a URL you already have: find the result with Search or Answer first, then fetch what's behind it. * **You're building agentic apps that need precise, source-grounded inputs fast.** Pull the exact numbers behind a card, or the full clean text behind a web result, and act on real content instead of scraping it yourself. Search and Answer *find* the result; Contents *fetches* the content behind it. To skip the second call, inline the content directly in a Search/Answer response with the per-source [`include_contents`](/documentation/integrating-tako/search/for-coding-agent#choosing-sources) setting. ## Common mistakes > [!WARNING] > > Avoid these — they are the patterns coding agents most often get wrong. | Wrong | Correct | | - | - | | `Authorization: Bearer $KEY` | `X-API-Key: $KEY` | | Passing a `format` field | You don't — Tako detects `csv` vs `text` from the URL and reports it back | | Passing a query string as `url` | `url` must be a **result URL** from Search/Answer (a card's `webpage_url` or a web result's `url`) | | Reading `data` after a `url`-mode call | `url` mode returns a download link in `url`; `data` is populated only in `inline` mode | | Leaving `max_rows` unset on a large card | An omitted `max_rows` returns the whole card in `url` mode, up to 100,000 rows, and 2,000 rows in `inline` mode. It bills every row returned. Set it when you want a slice — and use `quote_only` to price the call first | | Trusting an export is complete | An export returns at most 100,000 rows in either mode. Check `truncated` and `total_rows`, and narrow the card or its time range if rows remain | | Reading a large export into a model's context | Write the export to a file, on disk or in memory, and have the agent parse it with code. Give the model only the rows it needs. See [Give a large export to an agent](#give-a-large-export-to-an-agent) | | Reusing the download `url` later | It's short-lived — honor `expires_at` and re-request if it has expired | | Calling Contents on a result with no `content` descriptor | Its absence means the data isn't exportable — check for `content` first and skip those results | ## Errors Failures return a `BaseAPIError` body: `{ "error_message": "...", "error_type": "..." }`. | Status | Meaning | Fix | | - | - | - | | `400` | Malformed request — bad JSON, missing `url`, or a `url` that isn't a fetchable result URL. | Pass a result `url` from a Search/Answer response (a card's `webpage_url` or a web result's `url`). | | `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). | | `400` | `premium: true` with `content_format: "card_json"`. | Use `csv`, `json_records`, or `json_compact` for a premium export. | | `403` | The result's underlying data can't be exported. | Expected for results that came back **without** a `content` descriptor — don't call Contents on them. | | `403` | `premium: true` over MPP without `quote_only`. | Premium exports need an API key. Over MPP, send `premium` only with `quote_only: true`. | | `404` | No such card, or the card has no exportable data. | Verify the `url` came from a recent Search/Answer result. | | `408` | The request timed out. | Retry the request. | | `422` | `offset` above 0 on a card with a date column, a sports card, or `content_format: "card_json"`. | Drop `offset`, or use `max_rows` alone. Only cards whose export keeps their first rows, such as list cards, take an `offset`. | # Contents Source: https://docs.tako.com/documentation/integrating-tako/contents/overview Fetch agent-ready full content behind any Search or Answer result — a structured-data CSV export for a Tako card, or the full extracted contents of a web page. Precise, source-grounded inputs for agentic apps. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/contents/for-coding-agent) ## When to use Contents [Contents](/api-reference/contents) fetches the **full content behind** a result you already have from [Search](/documentation/integrating-tako/search/overview) or [Answer](/documentation/integrating-tako/answer/overview) — in agent-ready form. Pass the result's URL and Tako returns one of two things, depending on what the URL points to: * a **structured-data CSV export** — the exact series behind a Tako card, ready to parse, compute on, or load into a dataframe; or * the **full extracted contents of a web page** — clean, parseable text, not raw HTML. Reach for it when: > **You've already found the result and want the content itself, not prose** > > There's no synthesis and nothing to re-derive — the CSV *is* the card's data, and the extracted text *is* the page, ready to use directly. > **It's a second step, not a search** > > Contents operates on a URL you already have: find the result with Search or Answer first, then fetch what's behind it. > **You're building agentic apps that need precise, source-grounded inputs fast** > > Pull the exact numbers behind a card, or the full clean text behind a web result, and act on real content instead of scraping it yourself. ## How it fits Contents operates on a result you already have: **1. Search or Answer with a query** Call [Search](/documentation/integrating-tako/search/overview) or [Answer](/documentation/integrating-tako/answer/overview) with a query. **2. Find the downloadable result** A downloadable result carries a `content` descriptor in the response (`format` is `csv` for a Tako card, `text` for a web page). **3. Pass its URL to Contents** Pass that result's URL — a card's `webpage_url` or a web result's `url` — to **Contents** to download the data. > [!NOTE] > > A result is downloadable **only when** it carries a `content` descriptor in the Search or Answer response. Some results' underlying data can't be exported, so they come back **without** one — that's expected, not an error. Check for the descriptor and skip Contents for results that don't have it. Both shapes carry a cost, reported on the descriptor: a card CSV bills a per-export baseline plus a per-row rate, and web-page text extraction bills per page. See [Contents pricing](/documentation/integrating-tako/contents/pricing). ## What the content looks like **Tako card → CSV** **A Tako card → structured-data CSV.** Contents returns the card's data as a CSV with a header row and one row per data point — the same series the card visualizes. A time-series card comes back keyed by an ISO‑8601 `Timestamp` (with timezone offset), one column per series: ```csv theme={null} Timestamp,usaspending_sum_obligation - Palantir Technologies Inc. Federal Contract Obligations 2007-10-01 00:00:00+00:00,135211.06 2008-10-01 00:00:00+00:00,4378216.96 2009-10-01 00:00:00+00:00,5801023.5 2010-10-01 00:00:00+00:00,20915978.15 2011-10-01 00:00:00+00:00,39889371.870000005 2012-10-01 00:00:00+00:00,33785816.41 2013-10-01 00:00:00+00:00,75264989.77 2014-10-01 00:00:00+00:00,95333802.78999999 2015-10-01 00:00:00+00:00,131712687.67 2016-10-01 00:00:00+00:00,132155363.88 2017-10-01 00:00:00+00:00,116424925.9 2018-10-01 00:00:00+00:00,162675170.62 2019-10-01 00:00:00+00:00,268668118.8 2020-10-01 00:00:00+00:00,228912825.77 2021-10-01 00:00:00+00:00,376844743.53 2022-10-01 00:00:00+00:00,348059526.7 2023-10-01 00:00:00+00:00,541197885.14 2024-10-01 00:00:00+00:00,1020566208.97 2025-10-01 00:00:00+00:00,713329284.7 ``` The header names the series exactly as the card sources it, so the values are self-describing and ready to attribute. **Web page → text** **A web page → full extracted contents.** For any non-card URL, Contents returns the page's full contents as clean, extracted text — the readable body, not raw HTML or navigation chrome — ready to feed straight to a model: ```text theme={null} Palantir Technologies Inc. — Federal Contract Obligations Palantir's U.S. federal contract obligations have grown sharply over the past decade, from roughly $135K in FY2007 to more than $1B in FY2024. The largest single-year jumps came in FY2019 and FY2024, driven by expanded defense and intelligence engagements… (full page text continues) ``` No crawling, boilerplate stripping, or HTML parsing on your side — you get the page's content in a form your model can read directly. ## Example [Try in Playground](https://tako.com/playground) Pass a card's URL — Tako detects the right content from the URL and returns a short-lived download link: ```bash theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/" }' ``` ```json theme={null} { "contents": [ { "format": "csv", "source_url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/", "cost": 0.021, "url": "https://tako.com/...signed-download...", "expires_at": "2026-06-29T18:30:00Z" } ], "request_id": "9c1b..." } ``` Want the data straight back in the response instead of a download link? Set `"mode": "inline"`. See the [For Your Coding Agent](/documentation/integrating-tako/contents/for-coding-agent) page for the full request and response reference. ## Use cases **Feed a small card's numbers to an agent.** Ask for `inline` mode and the rows come back in the response body, so the agent computes on real values instead of inferring them from prose. `inline` returns 2,000 rows unless you set `max_rows`. ```bash theme={null} curl -X POST https://tako.com/api/v1/contents \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/", "mode": "inline"}' ``` > [!WARNING] > > **Don't read a large export into a model's context.** A card export can hold up to 100,000 rows, and at about 15 tokens per row, 10,000 rows cost about 150,000 tokens. Write the export to a file, on disk or in memory, and have your agent parse it with code. Give the model only the rows or results it needs. To cap the export instead, set `max_rows`. **Give a large series to an agent.** Ask for `url` mode instead and you get a short-lived signed link rather than the rows. Write the file to disk or load it into memory, and have the agent parse it with code. The model sees only what the code returns. ```python theme={null} import pandas as pd df = pd.read_csv(download_url) ``` Here `download_url` is the signed `url` from a `mode: "url"` Contents response. See [Give a large export to an agent](/documentation/integrating-tako/contents/for-coding-agent#give-a-large-export-to-an-agent). **Read the full page behind a web result.** Pass a web URL where the examples above pass a card URL — the call is otherwise identical. You get clean extracted text instead of raw HTML, in `contents[0].data`, ready to drop into a model's context with no crawler and no HTML parser. # Contents pricing Source: https://docs.tako.com/documentation/integrating-tako/contents/pricing What a Contents download costs — the per-export baseline, the per-row rate, how max_rows drives the bill, and where to read your exact rate before you fetch. A Contents download is priced by what it returns. A **card CSV export** carries a flat per-export baseline plus a per-row rate. A **web page extraction** is a flat per-page rate. The rates on this page are list prices. The API returns your account's exact rates before you fetch, in `export_pricing`, and those override the list prices here. ## What a card CSV export costs ``` cost = baseline_usd + row_cpm_usd × rows / 1000 ``` | Component | Rate | | - | - | | Per-export baseline | `$0.001`, charged once per export regardless of size | | Per-row rate | From `$0.01` per 1,000 rows, and higher for some licensed sources | | Row ceiling | 100,000 rows per export. A larger `max_rows` is clamped to it | `rows` is the number of rows the export actually returns, which is the smaller of `max_rows` and the rows the card holds. Every returned row is billed, from the first, and a card with fewer rows than your cap bills only the rows it returns. ### A worked example A 2,000-row card at the base rate, exported in full: ``` $0.001 + $0.01 × 2000 / 1000 = $0.021 ``` The same card from a licensed source that bills `$0.10` per 1,000 rows: ``` $0.001 + $0.10 × 2000 / 1000 = $0.201 ``` ## Read your exact rate before you fetch Do not assume the floor rate. Every downloadable card in a Search or Answer response carries an `export_pricing` object, and its `row_cpm_usd` is **that card's rate for your account** — already adjusted for the card's source and for any negotiated terms on your account: ```json theme={null} "export_pricing": { "baseline_usd": 0.001, "row_cpm_usd": 0.01, "max_rows_ceiling": 100000 } ``` Put those numbers into the formula above and you get the charge exactly. That is the reliable way to price an export. The list prices on this page don't account for negotiated terms on your account, and `export_pricing` does. > [!NOTE] > > `export_pricing` also carries a deprecated `free_rows` field, always `0`. Ignore it; it will be removed. ### Quote without fetching Send `quote_only: true` on a Contents request to price an export without retrieving it or being charged. You get the `cost` back with no content and no charge. Search and Answer have no equivalent — see [Search inline contents vs. the Contents API](#search-inline-contents-vs-the-contents-api). ## Premium columns Some cards carry premium columns, such as the contact columns on a people list. A default export withholds them and bills only the formula in [What a card CSV export costs](#what-a-card-csv-export-costs). An export that sends `premium: true` reveals them and adds a premium term: ``` cost = baseline_usd + row_cpm_usd × rows / 1000 + premium_row_cpm_usd[class] × premium_rows[class] / 1000 (for each class) ``` * `premium_row_cpm_usd` is in the card's `export_pricing`, keyed by class: `person_contact` for contact details, `company_record` for premium company fields. * `premium_rows[class]` is the item's count of premium records. A premium record is a distinct person or company that the delivered rows hold a value for in a column of that class. A person with a work email and a phone counts once. A row whose person or company can't be identified counts as its own record. * Rows with no premium value add nothing to the premium term. To estimate the premium term before you pay for it: * **Upper bound:** send `quote_only: true` with `premium: true`. The quote builds no rows, so it counts every requested row as a premium record. It never undercharges, but it can quote more than the export bills. * **Exact count:** run a default export of the same slice, without `premium`. It bills the standard row rate, and its `premium_rows` counts withheld values too, so it's the count a `premium: true` export of those rows bills. A list's row order can change between calls, so check that the premium export returns the same rows. * A search card's `content.premium_rows` counts only the rows the card shows. When its `truncated` is `true`, the export reads more rows than that. See [Premium columns](/documentation/integrating-tako/contents/for-coding-agent#premium-columns) for which columns are premium. The list prices for the company and people data behind people and company lists: | Rate | List price | | - | - | | Per-row rate (`row_cpm_usd`) | `$1.50` per 1,000 rows | | `person_contact` | `$45.00` per 1,000 people revealed | | `company_record` | `$22.50` per 1,000 companies revealed | Negotiated terms on your account can change these. `export_pricing` gives your exact rates. ### A premium export, worked through A premium export of 50 people from a people list, 40 of whom have a work email, a personal email, or a phone: ``` $0.001 + $1.50 × 50 / 1000 + $45.00 × 40 / 1000 = $1.876 ``` The same export without `premium` costs `$0.076`. A `quote_only` request for it quotes `$2.326`, because the quote counts all 50 rows as premium records. ## How `max_rows` drives the bill `max_rows` is the single field that decides the size of a card export, so it is the field that decides the bill. | `max_rows` | Rows returned | Billed | | - | - | - | | omitted | The whole card, up to 100,000, in `url` mode; 2,000 in `inline` mode (20 per card on Search and Answer) | Every row returned | | a number | That many, up to 100,000 | Every row returned | | above 100,000 | 100,000 | 100,000 | Set `max_rows` when you need a slice. A 50-row preview of a licensed-source card costs `$0.006` where the full 2,000 rows cost `$0.201`. > [!NOTE] > > On Search and Answer, `max_rows` is a **per-card** cap. A response holding five cards bills each one separately, each with its own baseline. ### What bounds a multi-card search response One search response cannot bill without limit, however many cards it holds. All cards share a budget of 20,000 inline rows, spent in card order, so a 20-card request at `max_rows: 100000` returns and bills at most 20,000 rows. A card that arrives after the budget runs out comes back without inline rows. It isn't dropped, and it still carries its own `export_pricing`, so you can fetch its rows through Contents. `truncated` and `total_rows` tell you when this happened: a card can come back with fewer rows than you asked for because the budget ran out, not because the card was small. ## Search inline contents vs. the Contents API Card data reaches you two ways, priced identically per row but shaped very differently: | | Search / Answer inline | Contents API | | - | - | - | | How you ask | `sources.data.include_contents: true` | `POST /api/v1/contents` with the card's `webpage_url` | | Rows when you omit `max_rows` | 20 per card | The whole card, up to 100,000, in `url` mode; 2,000 in `inline` mode | | Scope of `max_rows` | Per card — a five-card response bills five times | The one card in the request | | Delivery | Always inline in the response body | `mode: url` for a presigned link, or `mode: inline` | | Price a call first | Not available | `quote_only: true` | | Serialization | CSV | `csv`, `json_records`, `json_compact`, or `card_json` | Both charge the same per-export baseline and per-row rate, so the difference in cost is entirely the difference in rows. > [!NOTE] > > **`quote_only` is Contents-only.** There is no way to price a Search or Answer response before running it. If you need a quote, read `export_pricing` off the card and compute it, or call Contents with `quote_only: true`. The small inline default is deliberate: a search response can hold many cards, and each one bills separately, so a `/contents`-sized default would multiply the bill by the number of cards in the response. Reach for inline contents when you want a usable preview alongside the results, and for the Contents API when you want the card's full data. ## What a web page extraction costs Text extraction from a web page is a flat `$0.001` per page. `max_rows` does not apply — it has no rows. ## Where to find what you were charged * **Per item** — every `ContentItem` carries a `cost` in USD: what that one download cost. * **Per request** — when your account is billed pay-as-you-go, the response carries a `usage` object whose **`total_cost_usd`** is the request total. (`usage.data` is only the inline-data component, and is omitted when a request inlined none.) On accounts that are not metered per request the `usage` key is **absent**; that is expected, and the per-item `cost` fields are still populated. # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/data-graph/for-coding-agent The build reference for coding agents integrating Tako's Data Graph. Self-contained and copy-paste-ready — Graph Search, Graph Related, and Graph Node endpoints, every parameter, real response shapes, and the full agentic workflow for grounding /v3/search queries in resolved graph nodes. This page is a complete, self-contained reference for building against **Tako's Data Graph** — and for the agentic pattern it enables: discover what data Tako has, ground your searches on it, and report what's missing. It is written for coding agents (and developers in a hurry): 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 the auto-generated schemas, see the API reference ([Graph Search](/api-reference/graph-search), [Graph Related](/api-reference/graph-related), [Graph Node](/api-reference/graph-node)); for a narrative introduction, see the [Overview](/documentation/integrating-tako/data-graph/overview). > **[Skip the wiring — install the tako-graph-agent skill](/documentation/integrations/agent-skills/graph-agent)** > > One command teaches your coding agent everything on this page: resolve names to nodes, read what data exists, compose pinned searches, report gaps honestly. ## What the Data Graph does Tako serves live financial, macroeconomic, and company data as embeddable knowledge-card charts through [`POST /v3/search`](/documentation/integrating-tako/search/for-coding-agent). The graph endpoints tell you **what data Tako actually has before you search** — so you compose queries that hit, pin the exact nodes you resolved, and honestly report gaps. * **Base URL:** `https://tako.com/api/` * **Auth:** every endpoint on this page — including the graph endpoints — takes your API key in the **`X-API-Key`** request header. Create a key in the [Tako console](https://tako.com/console/api-keys). * **Cost:** graph calls consume **no credits**. They are bounded by rate limits only: **180/minute and 10,000/day** per account. Back off on `429` with jittered exponential retry. * **Endpoints:** `GET /v1/graph/search` (resolve a name to nodes), `GET /v1/graph/related` (explore what a node connects to), `GET /v1/graph/node/{id}` (confirm what an id refers to — occasional use). * **Parameter spelling:** the tables on this page give the wire spelling. The SDKs take `types` as a list (`["entity"]`), and the TypeScript SDK camelCases parameter names (`nodeId` for `node_id`). > [!NOTE] > > **A returned node is a guide, not a guarantee** — see [Knowing what you don't know](#knowing-what-you-dont-know) for how to report coverage honestly. ## Install ```bash theme={null} pip install tako-sdk # Python npm install tako-sdk # TypeScript / JavaScript ``` ## Minimal working example Resolve an entity, read its metrics, then run a search pinned to the resolved node. Each snippet makes the same three calls and is complete after you set `TAKO_API_KEY`. The cURL example shows the REST equivalent. ```python Python theme={null} import os from tako import Configuration, DataSourceSettings, GraphNodeType, SearchRequest, Sources from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) # 1. Resolve the entity. nodes = client.graph_search("nvidia", types=[GraphNodeType.ENTITY]).results if not nodes: print("Gap: Tako has no entity node for 'nvidia'") raise SystemExit(0) node = nodes[0] # 2. Read its revenue metrics. page = client.graph_related(node.id, relation="metrics", q="revenue") metrics = page.relation.items if page.relation else [] print([m.name for m in metrics]) # 3. Search, pinned to the resolved node. results = client.search(SearchRequest( query=f"{node.name} quarterly revenue", sources=Sources(data=DataSourceSettings(node_ids=[node.id])), )) for card in results.cards or []: print(card.title, card.embed_url) ``` ```typescript TypeScript theme={null} import { Tako, GraphNodeType } from "tako-sdk"; const tako = new Tako({ apiKey: process.env.TAKO_API_KEY! }); // 1. Resolve the entity. const { results: nodes } = await tako.graphSearch({ q: "nvidia", types: [GraphNodeType.Entity] }); const node = nodes[0]; if (!node) { console.log("Gap: Tako has no entity node for 'nvidia'"); process.exit(0); } // 2. Read its revenue metrics. const page = await tako.graphRelated({ nodeId: node.id, relation: "metrics", q: "revenue" }); const metrics = page.relation?.items ?? []; console.log(metrics.map((m) => m.name)); // 3. Search, pinned to the resolved node. const results = await tako.search({ query: `${node.name} quarterly revenue`, sources: { data: { node_ids: [node.id] } }, }); for (const card of results.cards ?? []) { console.log(card.title, card.embed_url); } ``` ```bash cURL theme={null} # 1. Resolve the entity curl "https://tako.com/api/v1/graph/search?q=nvidia&types=entity" \ -H "X-API-Key: $TAKO_API_KEY" # 2. What revenue-like metrics exist for it? (use the id from step 1) curl "https://tako.com/api/v1/graph/related?node_id=NODE_ID&relation=metrics&q=revenue" \ -H "X-API-Key: $TAKO_API_KEY" # 3. Search, pinned to the resolved node curl -X POST https://tako.com/api/v3/search \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "Nvidia quarterly revenue", "sources": {"data": {"node_ids": ["NODE_ID"]}}}' ``` ## Graph Search — resolve a name to nodes `GET /v1/graph/search` resolves free text to entity and metric nodes. Decide up front whether you're resolving **a thing** (entity) or **a measure** (metric) and pass the matching `types` — don't mix them in one lookup. | Parameter | Type | Default | Description | | - | - | - | - | | `q` | string | — *(required)* | Search text. Shorter than 2 characters returns an empty `results` list (200, not an error). | | `types` | string | both | Comma-separated facets: `entity`, `metric`. Unknown value → 400. | | `label` | string enum | none | Ranking **boost** toward one NER label (see below). Unknown value → 400. Supplying it disables inference. | | `infer_label` | boolean | `true` | Auto-detect the entities in `q` and apply the matching label boost. Disable with `false`/`0`/`no`/`off`. | | `limit` | integer | `20` | Max results; values above 50 are clamped to 50 (below 1 → 400). | ### Labels: disambiguating without filtering Ambiguous names ("air china", "apple") resolve to a pile of nodes across types — a company can share its name with a country or a fruit. A **`label`** biases the ranking toward one entity category (`label=ORG` floats Air China the airline above China the country). It is a **boost, not a filter** — matching nodes rank higher, but off-label nodes still return. Use it to steer the top result, not to guarantee exclusivity. * **Values:** `PERSON`, `ORG`, `GPE`, `LOC`, `PRODUCT`, `EVENT`, `LANGUAGE`, `MONEY`, `METRIC`, `STOCK_TICKER`, `WEBSITE`. Sports teams are `ORG`. * **Auto-detection:** with no explicit `label`, Tako infers the entities in `q` and applies the matching boost; the labels it found come back in `inferred_labels` (empty list = inference ran, found nothing; absent = didn't run). ### Response ```json theme={null} { "results": [ { "id": "nvidia-3f8b2c", "type": "entity", "name": "NVIDIA", "aliases": ["Nvidia Corporation", "NVDA"], "description": "American technology company", "subtype": "Companies", "label": "ORG" } ], "inferred_labels": ["ORG"] } ``` Results are ordered by a relevance + popularity blend. Fields that are null are omitted from the response, not sent as `null`. ## Graph Related — explore what a node connects to `GET /v1/graph/related` takes a `node_id` from Graph Search and returns everything it connects to, grouped by relation. | Parameter | Type | Default | Description | | - | - | - | - | | `node_id` | string | — *(required)* | The node's opaque public id. Unknown id → 404. | | `relation` | string | none | A relation **key** to paginate: `metrics`, `entities`, `siblings`, `part_of`, `members`, or a named edge like `rel:competes_with`. Omit for the overview. | | `q` | string | none | Case-insensitive substring filter on related nodes' names + aliases. Filters every group of the overview, or the one paginated group. | | `label` / `infer_label` | — | as above | Same boost semantics as Graph Search; inference runs only when `q` is set. | | `cursor` | string | none | Opaque pagination cursor from `next_cursor`. Malformed → 400. | | `limit` | integer | `50` | Page size; values above 100 are clamped. | > [!NOTE] > > An unknown `relation` key returns **200 with empty items**, not an error — so a typo'd `rel:*` key reads as "no relation." Relation keys are per-node: read the overview to discover them, then drill. ### Overview response (no `relation`) An ordered `relations[]` list — named semantic edges first, then membership, data co-reference, and siblings. Empty groups are dropped. ```json theme={null} { "node": { "id": "nvidia-3f8b2c", "type": "entity", "name": "NVIDIA", "subtype": "Companies" }, "relations": [ { "key": "rel:competes_with", "kind": "related", "label": "Competes With", "items": [{ "id": "amd-91c04d", "type": "entity", "name": "AMD", "subtype": "Companies" }], "total": 12, "total_capped": false }, { "key": "part_of", "kind": "membership", "label": "Part Of", "items": [{ "id": "magnificent-seven-55aa01", "type": "entity", "name": "Magnificent Seven" }], "total": 3, "total_capped": false }, { "key": "metrics", "kind": "data", "label": "Metrics", "items": [{ "id": "revenues-8d21aa", "type": "metric", "name": "Revenues" }], "total": 250, "total_capped": true }, { "key": "siblings", "kind": "sibling", "label": "Other Companies", "items": [{ "id": "microsoft-7be913", "type": "entity", "name": "Microsoft" }], "total": 250, "total_capped": true } ] } ``` Groups by `kind`: | `kind` | Keys | Meaning | | - | - | - | | `related` | `rel:` — `rel:competes_with`, `rel:subsidiaries_of`, `rel:in_industry`, `rel:headquartered_in`, … | **Named semantic edges.** The richest signal for entities. Keys are per-node — read the overview, then drill. | | `membership` | `part_of`, `members` | Cohort/geo membership (Apple `part_of` FAANG; a league's `members`). | | `data` | `metrics`, `entities` | Table co-reference — the metrics/entities that share datasets with this node. | | `sibling` | `siblings` | Same-type peers (the class namespace — "Other Companies" — not a curated cohort). | | `source` | `sources` | The data publishers whose datasets cover this node (for example S\&P Global, Visible Alpha). | ### Drill response (`relation=`) ```json theme={null} { "node": { "id": "nvidia-3f8b2c", "type": "entity", "name": "NVIDIA", "subtype": "Companies" }, "relation": { "key": "metrics", "kind": "data", "label": "Metrics", "items": [ { "id": "revenues-8d21aa", "type": "metric", "name": "Revenues", "aliases": ["Total Revenue"] }, { "id": "revenue-per-employee-1c9f70", "type": "metric", "name": "Revenue Per Employee" } ], "total": 14, "total_capped": false, "next_cursor": null } } ``` Items are in `relation.items` — **not** `results`. A node with no relations returns 200 with empty groups, not a 404. ### Reading related results well * **Always pass `q` on big entities.** Unfiltered, a big entity returns hundreds of items (`total` caps at **250** with `total_capped: true` — render as "250+"). `q=revenue` narrows and floats the right metrics ("Revenues", "Revenue Per Employee") to the top. Pagination ends at the cap — narrow with `q` to reach the tail. * **Items are ranked by popularity, blended with match strength when `q` is set** — read the top few, not the tail. * **Every item carries an `id`** — use it to hop (metric → its entities → their relations…) and to pin into search. * **Enumerating a cohort** ("Nvidia's competitors", "all NBA teams", "the Magnificent Seven"): resolve the anchor entity → read its overview → pick the group by its `key` (a `rel:*` edge or `members`) → drill with `limit=100` if `total` exceeds the preview. Members arrive as **full nodes** — feed them straight into per-member searches, no re-resolution. This replaces LLM-recalled member lists — the usual source of hallucinated comparisons — with a database read. Don't treat a capped `siblings` group as a cohort: it's the class namespace, not curated peers. ## Graph Node — confirm what an id refers to `GET /v1/graph/node/{id}` resolves a single id (from a search card, or from the other graph endpoints) to its full `name`, `aliases`, and `description`. 404 when the id doesn't resolve. You'll rarely need it — mostly to verify that a card was built from the entity you meant before you rely on it. ## Grounding `/v3/search` — two levers, use both 1. **Query text (wording drives retrieval).** Compose queries from the resolved node's **name, aliases, and description** — so a metric aliased "inflation" answers an inflation question even when the user said "CPI". Keep queries short and data-shaped (subject + measure + time). Analytical/causal phrasing ("how has X affected Y") retrieves nothing — collect the series, do the analysis in your synthesis step. 2. **Pinned node ids (deterministic candidacy).** Pass resolved ids in the request body: ```json theme={null} { "query": "Nvidia quarterly revenue", "sources": { "data": { "node_ids": ["nvidia-3f8b2c", "revenues-8d21aa"], "strict": false } } } ``` | Setting | Behavior | | - | - | | `node_ids` | Up to **20 distinct ids** (deduped before the cap is enforced). Pinned nodes get **guaranteed retrieval candidacy + a strong boost**; organic results are unaffected. Malformed id → 400; an id that no longer resolves is skipped. | | `strict` | When `true`, return **only** cards matching a pinned node — `node_ids` must then be non-empty (else 400). | Use text alone when you resolved intent but not a specific node; add pinned ids whenever you *did* resolve the exact metric/entity and want to force it in. **Enforce grounding in code, not just the prompt** — let your LLM write the wording, but guard what it emits: ```typescript theme={null} if (!citesListedMetric(q, resolvedMetrics)) drop(q); // must cite a resolved name/alias verbatim if (namesMultipleEntities(q, resolved)) drop(q); // one entity per query if (kept.length === 0) queries = fallbackQueries(lookup); // mechanical "{entity} {fragment}" pairs ``` Don't deterministically concatenate `"{node} {metric}"` as the *primary* composer — it misses alias-named metrics and reads templated. Let the model compose from names + aliases; keep the concat as the fallback. ## The agentic loop The full pattern for a data-question agent: 1. **Break** a multi-part question into a few entities and/or metrics. 2. **Resolve** each with Graph Search (`types=entity` OR `types=metric`; add `label` only to force disambiguation, else let inference run). 3. **Pick** the node(s) — read `subtype`, `label`, and `description`. 4. **Explore** their `q`-filtered relations, ranked by popularity and match strength; keep the top few. For cohorts, drill the named `rel:*` edge or `members`. 5. **Compose** grounded `/v3/search` queries from the resolved names and aliases, **and pin the resolved ids** in `sources.data.node_ids`. 6. **Fetch** concurrently; render each card's `embed_url` as an iframe (embeds post their height via a `tako::resize` message — see [Embedding Knowledge Cards](/documentation/integrating-tako/embedding-knowledge-cards)); **report gaps**. Any stage can come up empty — a valid, visible outcome, not a failure. Give your planner a **validated output contract** so the graph phase runs deterministically: ```typescript theme={null} const zLookup = z.object({ entities: z.array(z.string().min(1)).min(1).max(3), // 1-3 DIFFERENT names for ONE subject ("Google", "Alphabet") label: z.enum(NER_LABELS).nullable().optional(), // optional boost; omit to let inference run metricFilters: z.array(z.string().min(1)).min(1).max(5), // short metric-NAME fragments for related's substring q }); ``` * **Models:** question-breakdown and node-picking run fine on cheap/fast models; the **compose step is the one that matters** (wording drives hits) — give it your best model. * **Latency/cost:** several graph round-trips + a couple of LLM calls fire before the data search, so the loop is slower than a single `/v3/search` — but graph calls cost no credits and the LLM calls are small. The payoff is grounded searches that hit more often, plus an honest gaps report. * **Caps are yours:** a few entities, a few related metrics each, dedupe queries case-insensitively, bound total searches. Raise for research, lower for a chat sidebar. > [!TIP] > > **Skip the wiring.** If you're building an agentic application that integrates Tako Search, the **[tako-graph-agent skill](/documentation/integrations/agent-skills/graph-agent)** packages this whole loop — resolve, explore, compose, pin, report gaps — as one installable file, with a one-command install. ## Knowing what you don't know Report what the graph can't ground as explicit **gaps** — *"Tako has X and Y, but not Z."* An empty result is a valid, honest answer; never invent data to fill a gap. Saying what Tako knows **and doesn't** is the whole advantage over blind search. Two caveats keep the report honest: * **"Related" is table-level, not entity-level.** A node's related metrics are the metrics in the datasets that *cover* that entity — strong evidence, not proof of that exact combination. `/v3/search` is the final validator. * **The graph is not the whole index.** It indexes what's structured as metrics/entities — not everything search can return. **Stock/share-price and market-quote data especially** is usually not a graph metric, yet search almost always has it (same for rankings, screens, overviews). A thin graph result for an entity Tako obviously covers is a cue to run an entity-level `/v3/search`, **not** to declare a gap. ## When to use — and when to decline Use the graph-grounded loop for data/chart questions where Tako plausibly has coverage (public companies, macro indicators, indices, commodities, sports). **Skip discovery** when you already know the exact chart — a named chart or fully-qualified metric + entity goes straight to `POST /v3/search`. **Decline** advice/opinion/prediction asks ("should I buy X", "will Y happen") — the pipeline fetches data, it doesn't forecast; serve the factual sub-question and decline the rest. Poor fit: purely qualitative/sentiment asks, coding, trivia. ## Common mistakes > [!WARNING] > > Avoid these — they are the patterns coding agents most often get wrong. | Wrong | Correct | | - | - | | Calling the graph endpoints without a key | Graph endpoints require the same `X-API-Key` as `/v3/search` (401 without). They cost no credits, but they are authenticated. | | Skipping `q` on `graph/related` for a big entity | Unfiltered returns hundreds of items. Pass `q=` and read the top of the ranking. | | `types=entity,metric` in one lookup | Decide "thing vs. measure" up front and pass one. | | Treating `label` as a filter | It's a ranking **boost** — off-label nodes still return. Read `subtype`/`label` on each node to actually pick. | | Only drilling `metrics`/`entities` | The `rel:*` named relations and `part_of`/`members` are often the answer. Read the overview's keys first. | | Hardcoding a `rel:*` key from a past run | Keys are per-node — fetch the overview, then drill. An unknown key returns 200 with empty items, so a typo reads as "no relation." | | Treating a capped `siblings` group as a cohort | Siblings are the class namespace ("Other Companies"), not curated peers — use a named `rel:*` edge or `members` for a real comparison set. | | Parsing related items from `results[]` | Overview items are in `relations[].items`; drill items are in `relation.items`. The wrong key reads a 200 as zero results. | | Composing text queries but never pinning ids | You resolved the exact node — pin it via `sources.data.node_ids`. Text + ids beats text alone. | | Composing analytical/causal queries | "How has X affected Y" retrieves nothing — query the series, analyze in synthesis. | | Declaring a gap because the graph had no metric | Price/market data especially lives outside the graph — run an entity-level `/v3/search` before declaring a gap. | | Skipping the gaps output | Gaps are the point — otherwise you're back to blind search. | ## Errors Failures return `{ "error_message": "...", "error_type": "..." }`. | Status | Meaning | Fix | | - | - | - | | `400` | Missing `q`/`node_id`, an unknown `types` or `label` value, a malformed cursor, or `limit` below 1. | Validate against the parameter tables above. | | `401` | Missing or invalid API key. | Send a valid key in the `X-API-Key` header. | | `404` | `node_id` (or node path id) doesn't resolve. | Re-resolve the name with Graph Search — ids can go stale across graph rebuilds. | | `429` | Rate limit exceeded (180/min or 10,000/day). | Back off with jittered exponential retry. | | `503` | A backing data store is temporarily unavailable. | Retry with backoff. | # Data Graph Source: https://docs.tako.com/documentation/integrating-tako/data-graph/overview Discovery endpoints that tell your agent what data Tako has. Resolve names to graph nodes, explore their relationships, then pin them into Search for grounded results. No credits consumed. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/data-graph/for-coding-agent) > **[Skip the wiring — install the tako-graph-agent skill](/documentation/integrations/agent-skills/graph-agent)** > > One command teaches your coding agent this whole workflow: resolve names to nodes, read what data exists, compose pinned searches, and report gaps honestly. ## When to use the Data Graph The Data Graph is Tako's index of what its curated data covers. Every entity (companies, countries, teams, commodities) and every metric (revenue, CPI, win percentage) is a node, and real relationships connect them. Query the Data Graph before you call [Search](/documentation/integrating-tako/search/overview). Your agent then searches for data that exists, pins the nodes it resolved, and reports what's missing. Use it when: > **You want to know what Tako has before you search** > > Resolve a name, such as `Nvidia` or `inflation`, to real graph nodes, then read what data exists for them. You spend searches on confirmed coverage instead of on guesses. > **You want deterministic grounding** > > Pin resolved node ids into Search with `sources.data.node_ids`, and each pinned node becomes a retrieval candidate. To return only the cards that match your nodes, add `strict: true`. > **You want real relationships, not model memory** > > Read competitors, subsidiaries, industry peers, and cohort members off named graph edges such as `rel:competes_with` and `members`. A database read replaces the recalled list that produces a hallucinated comparison. > **You want honest gaps** > > If the Data Graph can't ground part of a question, your agent can say so: "Tako has X and Y, but not Z." An empty result is an honest answer, not a failure. ## Endpoints | Endpoint | What it does | | - | - | | **[Graph Search](/api-reference/graph-search)** `GET /v1/graph/search` | Resolve a name to nodes. `nvidia` returns the company node, with its id, aliases, and description. | | **[Graph Related](/api-reference/graph-related)** `GET /v1/graph/related` | List what a node connects to: its metrics, its entities, its named relations, and its cohorts. | | **[Graph Node](/api-reference/graph-node)** `GET /v1/graph/node/{id}` | Look up one node by id. Use it to verify an id you read off a card. | ## Free to call > **No credits consumed** > > Graph calls use the same `X-API-Key` header as every Tako endpoint, and they consume no credits. Rate limits are the only bound: 180 calls per minute, and 10,000 per day. > **[Explore the Data Graph at tako.com/data](https://tako.com/data)** > > Sign in to your Tako account and browse the Data Graph. Search any entity or metric, walk its relationships, and see what your agent sees before you write any code. > [!WARNING] > > **Warning:** A listed metric doesn't prove that Tako covers that exact entity-and-metric pair. A node's related metrics come from the datasets that cover the entity, so treat a listed metric as strong evidence and confirm it with a search. > > The Data Graph also isn't the whole index. Stock prices, market quotes, rankings, and overviews often sit outside it, and [Search](/documentation/integrating-tako/search/overview) still returns them. If the Data Graph returns a thin result for an entity you expect Tako to cover, search anyway before you report a gap. ## Example: discover, then search **1. Resolve the entity** ```bash theme={null} curl "https://tako.com/api/v1/graph/search?q=nvidia&types=entity" \ -H "X-API-Key: $TAKO_API_KEY" ``` The response holds nodes with `id`, `name`, `aliases`, and `subtype`. Pick the company node and keep its `id`. **2. See what data exists for it** ```bash theme={null} curl "https://tako.com/api/v1/graph/related?node_id=$NODE_ID&relation=metrics&q=revenue" \ -H "X-API-Key: $TAKO_API_KEY" ``` The response holds the metrics that match "revenue", ranked by popularity and match strength. These are the combinations Tako covers. **3. Search, pinned to what you resolved** ```bash theme={null} curl -X POST https://tako.com/api/v3/search \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "Nvidia quarterly revenue", "sources": { "data": { "node_ids": ["'$NODE_ID'"] } } }' ``` Search makes every pinned node a retrieval candidate and boosts it. Organic results don't change. ## Next steps * **[For your coding agent](/documentation/integrating-tako/data-graph/for-coding-agent)** — the complete build reference: every parameter, label boost, relation kind, response shape, and common mistake. * Building an agentic app? Install the **[tako-graph-agent skill](/documentation/integrations/agent-skills/graph-agent)**. It packages the discover, ground, and report-gaps loop for your coding agent. # Embedding Knowledge Cards Source: https://docs.tako.com/documentation/integrating-tako/embedding-knowledge-cards Embed Tako knowledge cards in your app — interactive iframes via embed_url, static images via image_url, dynamic resizing, dark/light theming, and glanceable mode. Tako [Search](/api-reference/search-v3) and [Answer](/api-reference/answer) responses contain one or more `cards` that can be embedded in your application. Each card includes: **embed\_url:** An iframe URL for embedding the interactive card**image\_url:** A link to a static image of the card ## Dynamic Sizing Tako Knowledge Cards vary in size. Some interactive elements will change the card's dimensions. The iframe emits a `resize` event when the card is resized, allowing you to adjust the size in your application. > **Example Dynamic Resizing Script** > > ```html theme={null} > > > > ``` **Static Images** For cases where you need a static representation, you can use the `image_url` to download and embed a static image of the card in your application. ## Switch between dark and light mode You can make your card dark themed by adding `?dark_mode=true` or light mode by explicitly adding `?dark_mode=false` to the embed url ### `dark_mode=true` ```html theme={null} ``` [https://tako.com/embed/9GCTI9YE-ebTYAn35bYT/?dark\_mode=true](https://tako.com/embed/9GCTI9YE-ebTYAn35bYT/?dark_mode=true) ### `dark_mode=false` ```html theme={null} ``` [https://tako.com/embed/9GCTI9YE-ebTYAn35bYT/?dark\_mode=false](https://tako.com/embed/9GCTI9YE-ebTYAn35bYT/?dark_mode=false) ### Automatically match the viewer's theme Rather than hardcoding the theme, you can detect the viewer's color mode at runtime and set `dark_mode` accordingly. The pattern is always the same: compute whether the viewer is in dark mode, append `?dark_mode=true` or `?dark_mode=false` to the embed URL, and re-apply when the preference changes. **Follow the viewer's OS / browser preference** Use the `prefers-color-scheme` media query. This works anywhere, with no knowledge of the host page, and updates live if the viewer switches their system theme. ```html theme={null} ``` **Follow your app's own theme** If your app manages its own theme (for example by toggling a `dark` class on ``), key off that instead and use a `MutationObserver` to react when it changes. ```html theme={null} ``` > [!NOTE] > > Setting an iframe's `src` reloads the card. If your page lets viewers toggle the theme frequently, debounce the handler so the card isn't reloaded on every rapid change. ## Glanceable mode Add `?glanceable=true` to an embed URL to render a simplified, glanceable version of the card. Glanceable mode is designed for contexts where the user has limited attention and needs to absorb the key information at a glance — for example, on a vehicle display, a smartwatch, or a TV dashboard. In glanceable mode the card: * **Enlarges typography** — headlines, metrics, axis labels, and table text are scaled up for readability from a distance. * **Hides interactive chrome** — search boxes, selectors, tabs, show/hide toggles, thumbs up/down, and the footer are removed, leaving only the content. * **Trims content to the essentials** — tables cap to their top rows, charts drop tooltips and animations and use thicker lines and fewer axis ticks, and supported cards (stocks, sports schedules, prediction markets, game scores) render a focused summary layout. Glanceable mode is supported on most card types. Cards that don't have a glanceable layout fall back to their standard rendering. ### `glanceable=true` Each card below renders in glanceable mode. Switch it to standard to see what the mode strips out. ```html theme={null} ``` Glanceable Standard [https://tako.com/embed/uoTFW3MnZ---CNI9tBnk/?glanceable=true](https://tako.com/embed/uoTFW3MnZ---CNI9tBnk/?glanceable=true) ```html theme={null} ``` Glanceable Standard [https://tako.com/embed/rDxhM21e0wG9YSgYRFDv/?glanceable=true](https://tako.com/embed/rDxhM21e0wG9YSgYRFDv/?glanceable=true) > [!NOTE] > > Glanceable mode can be combined with `?dark_mode=true` (or `false`) — for example `?glanceable=true&dark_mode=true`. ## Customizing Tako On an Enterprise plan, use the [theme builder](https://tako.com/user/theme/) to set the fonts, spacing, and colors of every card your account creates, so your embeds carry your brand rather than Tako's. A theme is stamped onto a card when the card is created, not when it's viewed. Every viewer of an embed therefore sees your theme — but a card you created before you saved the theme keeps the old one, so re-run the query to pick up a change. ### Custom fonts This card sets a title and body font in place of Tako's defaults. [https://tako.com/embed/87zRvmgSNHiOvo8DLOlH/](https://tako.com/embed/87zRvmgSNHiOvo8DLOlH/) # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/monitors/for-coding-agent The build reference for coding agents integrating Tako monitors. Create notification channels and monitors, name a subject, receive and verify webhook deliveries, and debug a monitor that doesn't fire, with copy-paste-ready requests. This page is a complete reference for building against **Tako monitors**. It's written for coding agents and for developers who want the whole contract on one page. Every request runs after you supply an API key. The parameters and response fields match the live API, and the common mistakes have their own table. For the generated schemas, see the API reference, starting at [Create a monitor](/api-reference/monitors-create). For the request Tako posts when a monitor fires, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). For an introduction, see the [Overview](/documentation/integrating-tako/monitors/overview). > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## What monitors do A monitor watches Tako data for a condition. When the condition occurs, Tako records a firing and posts a signed request to each notification channel on the monitor. * **Base URL:** `https://tako.com/api/` * **Auth:** every endpoint on this page takes your API key in the `X-API-Key` request header. Create a key in the [Tako console](https://tako.com/console/api-keys). * **Rate limits:** the monitors and notification channels endpoints allow 120 calls per minute, and 20,000 per day, per user. `GET /v1/sports/games` allows 60 calls per minute, and 5,000 per day. If you get a `429`, wait for the time in its `Retry-After` header when it has one. Otherwise, back off with jittered exponential retry. * **Limits:** by default, you can hold 1 active monitor and 2 notification channels. Tako counts these limits per user, not across an account. A `LIMIT_REACHED` response's `limit` gives your cap. * **Endpoints:** | Endpoint | What it does | | - | - | | `GET /v1/monitor_types` | List every monitor type, with the JSON Schema for its parameters. | | `GET`, `POST /v1/notification_channels` | List your channels, or register an `https` endpoint and get its signing secret. | | `GET`, `PATCH`, `DELETE /v1/notification_channels/{id}` | Read, pause or resume, or delete a channel. | | `GET`, `POST /v1/monitors` | List your monitors, or create one. | | `GET`, `PATCH`, `DELETE /v1/monitors/{id}` | Read a monitor, change its status or channels, or delete it. | | `GET /v1/monitors/{id}/firings` | List a monitor's firings, with each delivery's status. | | `GET /v1/sports/games` | List games, to find the ids that sports monitors take. | ## Minimal working example This script monitors AAPL for a 3% daily rise, as in the [Overview](/documentation/integrating-tako/monitors/overview#examples). It creates a channel and a monitor, then reads the monitor back. It needs `curl` and `jq`, and a `TAKO_API_KEY` environment variable. Replace the URL with an `https` endpoint that you control. ```bash theme={null} API=https://tako.com/api/v1 AUTH="X-API-Key: $TAKO_API_KEY" # 1. Create a channel. Store the secret: no later request returns it. CHANNEL=$(curl -s -X POST "$API/notification_channels" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{"name": "My receiver", "kind": "webhook", "url": "https://api.example.com/tako/webhooks"}') CHANNEL_ID=$(echo "$CHANNEL" | jq -r .id) echo "$CHANNEL" | jq -r .secret > tako-webhook-secret.txt # 2. Read the parameters that stocks.pct_change takes. curl -s "$API/monitor_types" -H "$AUTH" \ | jq '.items[] | select(.name == "stocks.pct_change") | .parameters_schema' # 3. Create the monitor. fire_once: false keeps it firing on every qualifying day. MONITOR_ID=$(curl -s -X POST "$API/monitors" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{ "name": "AAPL up 3% in a day", "type": "stocks.pct_change", "parameters": {"ticker": "AAPL", "window": "1d", "threshold": 3, "direction": "up"}, "fire_once": false, "channel_ids": ["'"$CHANNEL_ID"'"] }' | jq -r .id) # 4. Confirm what it watches, and whether Tako has evaluated it. curl -s "$API/monitors/$MONITOR_ID" -H "$AUTH" \ | jq '{subject, status, last_evaluated_at, last_evaluation_outcome}' ``` ## Notification channels A notification channel is an `https` endpoint that Tako posts firings to. Create the channel before the monitor that uses it. | Field | Description | | - | - | | `name` | *(Required)* Your label for the channel. | | `url` | *(Required)* The `https` endpoint. Tako rejects `http`, a port other than 443 or 8443, a URL with embedded credentials, and a host that resolves to a private or loopback address. | | `kind` | `webhook` (the default) sends nested JSON with the monitor, the firing, and the payload. `slack_workflow` sends a flat body of strings, for a Slack Workflow Builder trigger. See [Send monitor firings to Slack](/documentation/integrating-tako/monitors/slack). | The `201` response is the only response that holds `secret`. Tako signs every delivery to the channel with it. See [Verify the signature](/documentation/integrating-tako/monitors/webhook-deliveries#verify-the-signature). If you lose the secret, delete the channel and create a new one. Tako pauses a channel after 2 consecutive failed deliveries, and sets its `status_reason` to `delivery_failures`. Monitors that post to a paused channel keep recording firings, and no one receives them. To resume the channel, fix the endpoint, then send `PATCH /v1/notification_channels/{id}` with `{"status": "active"}`. ## Monitor types Call `GET /v1/monitor_types` before you create a monitor. The response is the authoritative list, and each item holds these fields: | Field | Description | | - | - | | `name` | The type name, namespaced as `domain.subtype`. | | `description` | What the type watches, when it fires, and how far its source runs behind. | | `parameters_schema` | The JSON Schema that your `parameters` must satisfy. | | `subject_schema` | The shape of the subject that the type resolves from your parameters. | | `lookback_seconds` | How far back an occurrence can be and still fire. | | `fire_once_only` | If `true`, the type accepts only `fire_once: true`. | The response also holds `firing_enabled`. While it's `false`, the environment accepts monitors and evaluates none of them. | Type | Fires when | Subject | | - | - | - | | `stocks.pct_change` | A stock's move over a `window` of `1d`, `1w`, or `1m` reaches `threshold` percent, in `direction`. | `ticker`, plus `exchange` | | `stocks.crosses` | A stock's price closes on the other side of your `level` from the prior close. | `ticker`, plus `exchange` | | `stocks.new_52w_extreme` | A stock passes its highest or lowest daily close of the preceding 365 days. | `ticker`, plus `exchange` | | `crypto.crosses` | A crypto asset's price in US dollars reaches your `level`. Fires once. | `ticker` or `coinmarketcap_id` | | `forex.crosses` | A currency pair's rate reaches your `level`, in the quote currency per one unit of the base. Fires once. | `base` and `quote`, as ISO 4217 codes | | `sports.game_start` | A game starts. | `game_id`, `team_id`, or both | | `sports.game_result` | A game ends. On a team, it fires only for the `outcome` you set. | `game_id`, `team_id`, or both | | `sports.score_event` | A score rises while a game is live or final. NBA isn't supported. | `game_id`, `team_id`, or both | | `sports.odds_crosses` | A game's betting line at the sportsbook you choose reaches your `level`. | `game_id` and `team_id` | > [!NOTE] > > `stocks.pct_change` and `stocks.new_52w_extreme` are **state alerts**. If you create one while its condition already holds, the next price update fires it. `stocks.crosses` does the same when the price has already crossed your level. On a delayed feed, that update arrives as late as the feed's delay. `crypto.crosses`, `forex.crosses`, and `sports.odds_crosses` work the other way: if the price or the line has already reached your level, the create fails. ## Create a monitor `POST /v1/monitors` takes these fields: | Field | Type | Default | Description | | - | - | - | - | | `type` | string | *(required)* | A type name from `GET /v1/monitor_types`. | | `parameters` | object | *(required)* | The subject and the condition. Tako validates it against the type's `parameters_schema`. | | `name` | string | `""` | Your label. Tako never reads it. | | `channel_ids` | string array | `[]` | Up to 20 channels to post each firing to. Every id must be a channel you own. An empty list records firings and delivers them nowhere. | | `fire_once` | boolean | `true` | If `true`, Tako pauses the monitor after its first firing. To fire on every new occurrence, set it to `false`. | | `active_from` | ISO 8601 string | now | Ignore occurrences before this time. | | `active_until` | ISO 8601 string | none | Pause the monitor as `expired` after this time. It must be in the future, and after `active_from`. | ### Check the resolved subject Tako resolves your parameters to one subject when you create the monitor, and never resolves them again. Every monitor response holds that subject in `subject`. Check it before you rely on the monitor. ```json theme={null} { "id": "0d6f1e77-4b9a-4f0e-8a4d-2c9e7b1f3a55", "name": "AAPL up 3% in a day", "type": "stocks.pct_change", "parameters": { "ticker": "AAPL", "exchange": "XNAS", "window": "1d", "threshold": "3", "direction": "up" }, "subject": { "ticker": "AAPL", "exchange": "XNAS", "company": "Apple Inc." }, "fire_once": false, "status": "active", "status_reason": null, "active_from": "2026-09-22T17:00:00+00:00", "active_until": null, "last_evaluated_at": null, "last_evaluation_outcome": null, "channel_ids": ["7c1e0b4a-5d2f-4e8b-9a61-3f0c2d7e8b14"], "created_at": "2026-09-22T17:00:00+00:00", "updated_at": "2026-09-22T17:00:00+00:00" } ``` `parameters` comes back in the form that you send, with names as Tako resolved them. A ticker that you sent as `aapl` reads back as `AAPL`, with the exchange that Tako picked. A crypto monitor reads back its asset as a `coinmarketcap_id`, whichever name you created it with. ### Handle a rejected create A `400` response holds an `error_type`: | `error_type` | What happened | What to do | | - | - | - | | `AMBIGUOUS_SUBJECT` | Your parameters match more than one subject. `candidates` lists each one, best first. | Copy the fields of the candidate you want into `parameters`, such as its `exchange`, and retry. | | `LIMIT_REACHED` | You're at your limit of active monitors or channels. `limit` is your cap. | Pause or delete a monitor, or delete a channel, that you don't need. For higher limits, contact [support@tako.com](mailto:support@tako.com). | | `BAD_REQUEST` | Anything else, such as parameters that fail the schema, or a `crypto.crosses`, `forex.crosses`, or `sports.odds_crosses` level that the price or the line has already reached. | Read `error_message`, and fix the request. | ### Name a sports subject Sports monitors take ids from `GET /v1/sports/games`. Each row's `id` is a `game_id`, and its `home.id` and `away.id` are team ids. This script monitors every 49ers win, as in the [Overview](/documentation/integrating-tako/monitors/overview#monitor-every-49ers-win). It finds the 49ers' team id, pauses the AAPL monitor to free the active monitor slot, then creates a `sports.game_result` monitor that follows the team. `CHANNEL_ID` is a channel that you created, and `MONITOR_ID` is the AAPL monitor from the [Minimal working example](#minimal-working-example). ```bash theme={null} API=https://tako.com/api/v1 AUTH="X-API-Key: $TAKO_API_KEY" # 1. Find the 49ers' team id in the NFL games for the next 7 days. TEAM_ID=$(curl -s "$API/sports/games?league=NFL" -H "$AUTH" \ | jq -r '.items[] | .home, .away | select(.name != null and (.name | test("49ers"))) | .id' \ | head -1) [ -n "$TEAM_ID" ] || { echo "No 49ers game in the next 7 days"; exit 1; } # 2. Free the active monitor slot. A paused monitor doesn't count toward the limit. curl -s -X PATCH "$API/monitors/$MONITOR_ID" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{"status": "paused"}' | jq '{id, status}' # 3. Fire on every 49ers win, for as long as the monitor is active. curl -s -X POST "$API/monitors" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{ "name": "49ers wins", "type": "sports.game_result", "parameters": {"team_id": "'"$TEAM_ID"'", "outcome": "wins"}, "fire_once": false, "channel_ids": ["'"$CHANNEL_ID"'"] }' | jq '{subject, status}' ``` The endpoint covers one day back to seven days ahead. A team's id doesn't change, so you can store it and reuse it when the team has no game in the window. Filter with `league`, `team`, `status`, `from`, and `to`. A row whose `source` is `schedule_table` comes from a schedule without a live feed, and a monitor rejects its ids. For `sports.game_start`, `sports.game_result`, and `sports.score_event`, the ids combine this way: * `game_id` alone watches the whole game, and each firing reports both sides. * `team_id` alone follows that team's games as they come. * `game_id` and `team_id` together watch one team's side of one game. ## Receive a delivery Each delivery is a `POST` with a JSON body and these headers: `webhook-id`, `webhook-timestamp`, and `webhook-signature`. Tako follows the [Standard Webhooks](https://www.standardwebhooks.com/) specification. For the full body, the verification code, and the payload fields for each type, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). A receiver needs to do these things, in order: 1. Verify `webhook-signature` against the raw request bytes, with the channel's secret. Reject a `webhook-timestamp` more than 5 minutes from the current time. 2. Skip a `webhook-id` that you've already stored. Every retry of one delivery sends the same id. 3. Store the body, and respond with a `2xx` within 10 seconds. 4. Do the slow work after you respond. If a delivery gets a `408`, a `429`, a `5xx`, or no response, Tako retries it 5 times over about 4 hours. Tako doesn't retry any other status, and doesn't follow redirects. For the retry schedule, see [How Tako retries](/documentation/integrating-tako/monitors/webhook-deliveries#how-tako-retries). ## Check a monitor that hasn't fired Read the monitor with `GET /v1/monitors/{id}`. `last_evaluated_at` says when Tako last evaluated it, and `last_evaluation_outcome` says what that evaluation found: | Outcome | What it means | | - | - | | `null` | Tako hasn't evaluated the monitor since you created it or resumed it. The type's source hasn't sent new data since then. | | `no_occurrence` | Tako read the data, and the condition wasn't met. | | `predates_monitor`, `before_window` | Tako found an occurrence from before you created the monitor, or before `active_from`. `before_window` also means that `active_from` hasn't arrived yet. The monitor is running, and it's waiting for a new occurrence. | | `outside_lookback` | Tako found an occurrence older than the type's `lookback_seconds`. | | `fired` | Tako recorded a firing. | | `already_fired` | Tako found an occurrence it had already recorded. | | `completed` | The subject reached an end state, such as a finished game, so the condition can't occur again. | | `expired`, `unloadable`, `unknown_type`, `crashed` | Tako stopped before it read the data, or the type failed while it read the data (`crashed`). | For every outcome, see the `last_evaluation_outcome` schema in [Get a monitor](/api-reference/monitors-get). If the monitor is `paused`, `status_reason` says why: `fired_once`, `expired`, `completed`, or `unloadable`. It's `null` when you paused the monitor yourself. ### Check deliveries `GET /v1/monitors/{id}/firings` lists the monitor's firings, newest first. Each firing holds `deliveries`, with one entry for each channel: | `state` | What it means | | - | - | | `pending`, `retrying` | Tako is still trying to deliver. | | `delivered` | The channel returned a `2xx`. | | `failed` | The delivery ended without a `2xx`. Read `attempts` and `last_status_code` to see why. | | `channel_paused` | The channel was paused, so Tako didn't send a request. Resume the channel to receive later firings. | The `deliveries` list covers the last 30 days. Tako removes a firing that's older than its type's lookback window. ## Change or remove a monitor `PATCH /v1/monitors/{id}` changes two fields, and nothing else: * `status`: set `paused` to stop evaluating the monitor, or `active` to resume it. If `active_until` has passed, resuming returns `400`. * `channel_ids`: replace the monitor's channels with this list. To change a monitor's type, parameters, or active window, delete the monitor and create a new one. Deleting a monitor also deletes its firings. To keep the history, pause the monitor instead. ## Common mistakes > [!WARNING] > > Avoid these. They're the mistakes that coding agents make most often with monitors. | Wrong | Correct | | - | - | | Creating a monitor to find out whether a condition already happened | A monitor fires only for occurrences after you create it. To read history, use [Search](/documentation/integrating-tako/search/overview). | | Losing the channel `secret` | Only the `201` create response returns it. Store it at create time. | | Verifying the signature against re-serialized JSON | Sign the raw request bytes. Any reformatting breaks the signature. See [Verify the signature](/documentation/integrating-tako/monitors/webhook-deliveries#verify-the-signature). | | Doing slow work before responding | Respond `2xx` within 10 seconds, then do the work. A slow receiver gets the same delivery again. | | Treating each request as a new firing | Deduplicate on `webhook-id`. | | Reading `payload` from a `slack_workflow` delivery | A `slack_workflow` body holds 7 strings, and `summary` carries the values. To read the values as fields, use a `webhook` channel. | | Guessing a `game_id` or `team_id` | Read ids from `GET /v1/sports/games`. | | Setting `fire_once: false` on `crypto.crosses` or `forex.crosses` | These types fire once. Read `fire_once_only` on the type. | | Setting a level that the price has already reached | `crypto.crosses`, `forex.crosses`, and `sports.odds_crosses` reject it, and `stocks.crosses` fires on the next price update. Set a level on the far side of the current value. | | Expecting a paused channel to resume itself | Fix the endpoint, then resume the channel with `PATCH`. | | Sending `PATCH` to change a threshold | Only `status` and `channel_ids` change. Delete the monitor and create a new one. | | Reading an empty firings list as a broken monitor | Read `last_evaluated_at` and `last_evaluation_outcome` first. | ## Errors A `400`, `401`, or `404` returns `{ "error_message": "...", "error_type": "..." }`. A `429` returns `{ "detail": "..." }`. | Status | Meaning | Fix | | - | - | - | | `400` | The parameters fail the type's schema, match more than one subject, or exceed your limit. | Read `error_type`. See [Handle a rejected create](#handle-a-rejected-create). | | `401` | The API key is missing or invalid. | Send a valid key in the `X-API-Key` header. | | `404` | The monitor or channel doesn't exist, or you don't own it. | Check the id. Every `channel_ids` entry must be a channel you own. | | `429` | You've exceeded a rate limit. | Wait for the time in the `Retry-After` header when the response has one. Otherwise, back off with jittered exponential retry. | # Monitors Source: https://docs.tako.com/documentation/integrating-tako/monitors/overview Watch Tako data for a condition, and get a signed webhook or a Slack message when it happens. Monitor stock moves, crypto and currency levels, and live sports, without polling. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/monitors/for-coding-agent) > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. ## How monitors work A monitor is a condition on Tako data, such as "AAPL moves up 3% in a day" or "the 49ers win." Tako evaluates the condition when new data arrives. When the condition occurs, Tako posts a signed request to your endpoint. You don't poll, and you don't schedule anything. You work with four objects: | Object | What it is | | - | - | | **Monitor type** | A kind of condition, such as `stocks.pct_change`. Each type publishes the parameters it takes and says when it fires. | | **Monitor** | One condition that you create from a type, with your parameters: a subject, such as a ticker or a game, and a threshold. | | **Notification channel** | Where Tako posts firings: an `https` endpoint that you own, or a Slack workflow that posts to a Slack channel. Tako signs every request it sends to a channel with the channel's secret. See [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). | | **Firing** | One occurrence of a monitor's condition. Tako records the firing and sends one delivery to each channel on the monitor. | ## Supported monitor types Tako supports four domains of monitor types. For the current list and each type's parameters, call [List monitor types](/api-reference/monitor-types). > **Stocks** > > A stock moves by a percentage over a day, a week, or a month. A stock's price crosses a level you set. A stock sets a new 52-week high or low. > **Crypto** > > A crypto asset's price in US dollars reaches a level you set. You name the asset by its ticker or its CoinMarketCap id. > **Forex** > > A currency pair's rate reaches a level you set, such as EUR/USD above 1.15. > **Sports** > > A game starts, a game ends, a team scores, or a betting line reaches a level you set. You can watch one game, or follow one team across its games. ## What to know before you start * **A monitor fires only for what happens after you create it.** You can't use a monitor to search history. * **Most monitors fire once.** By default, Tako pauses a monitor after its first firing. To fire on every new occurrence, set `fire_once` to `false`. The crypto and forex level types always fire once. * **A firing reports when the condition occurred, not the current value.** Each source reports new data on its own schedule. Each type's description in [List monitor types](/api-reference/monitor-types) says how far its source runs behind. * **By default, you can hold 1 active monitor and 2 notification channels.** Tako counts these limits per user. A `LIMIT_REACHED` response's `limit` gives your cap. To free a monitor slot, pause or delete a monitor. For higher limits, contact [support@tako.com](mailto:support@tako.com). * **The monitors endpoints allow 120 calls per minute, and 20,000 per day.** Each call takes the `X-API-Key` header that every Tako endpoint uses. ## Examples To build `parameters` for another type, read its `parameters_schema` in [List monitor types](/api-reference/monitor-types). ### Monitor AAPL for a 3% daily rise **1. Create a notification channel** ```bash theme={null} CHANNEL=$(curl -s -X POST https://tako.com/api/v1/notification_channels \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "My receiver", "kind": "webhook", "url": "https://api.example.com/tako/webhooks"}') CHANNEL_ID=$(echo "$CHANNEL" | jq -r .id) echo "$CHANNEL" | jq -r .secret > tako-webhook-secret.txt ``` `CHANNEL_ID` holds the channel's `id`, and `tako-webhook-secret.txt` holds its signing `secret`. Store the secret now, because no later request returns it. **2. Create the monitor** ```bash theme={null} AAPL_MONITOR=$(curl -s -X POST https://tako.com/api/v1/monitors \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "AAPL up 3% in a day", "type": "stocks.pct_change", "parameters": {"ticker": "AAPL", "window": "1d", "threshold": 3, "direction": "up"}, "fire_once": false, "channel_ids": ["'"$CHANNEL_ID"'"] }') AAPL_MONITOR_ID=$(echo "$AAPL_MONITOR" | jq -r .id) echo "$AAPL_MONITOR" | jq .subject ``` `fire_once: false` keeps the monitor active after it fires, so it fires again on the next day that AAPL rises 3%. `subject` in the response names the listing that Tako matched: `AAPL` on `XNAS`, Apple Inc. **3. Receive the firing** On a day that AAPL rises 3% or more from the prior session's close, Tako posts a `monitor.fired` event to your channel. This excerpt shows the body's `summary` and `payload`: ```json theme={null} { "type": "monitor.fired", "summary": "AAPL is at 233.70, up 3.2% from its 226.41 close on 2026-09-11, past your 3% threshold.", "payload": { "ticker": "AAPL", "exchange": "XNAS", "window": "1d", "baseline_date": "2026-09-11", "baseline_close": 226.41, "price": 233.7, "percent_change": 3.2188 } } ``` Verify the signature before you trust the body. For the full body and the verification code, see [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). ### Monitor every 49ers win This example posts to the channel you created in the preceding example. By default, you can hold 1 active monitor, so this example pauses the AAPL monitor before it creates a new one. **1. Find the 49ers' team id** ```bash theme={null} TEAM_ID=$(curl -s "https://tako.com/api/v1/sports/games?league=NFL" \ -H "X-API-Key: $TAKO_API_KEY" \ | jq -r '.items[] | .home, .away | select(.name != null and (.name | test("49ers"))) | .id' | head -1) echo "$TEAM_ID" ``` Each game in the response names its `home` and `away` teams, with an `id` for each. The list covers one day back to seven days ahead. The NFL plays every week from September to February, so the 49ers appear in most weeks of the season. If `TEAM_ID` is empty, the 49ers have no game in that window. A team's id doesn't change, so you can store it and reuse it when the team has no game in the window. **2. Pause the AAPL monitor** ```bash theme={null} curl -X PATCH https://tako.com/api/v1/monitors/$AAPL_MONITOR_ID \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"status": "paused"}' ``` A paused monitor doesn't count toward your limit. **3. Create the monitor** ```bash theme={null} curl -X POST https://tako.com/api/v1/monitors \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "49ers wins", "type": "sports.game_result", "parameters": {"team_id": "'"$TEAM_ID"'", "outcome": "wins"}, "fire_once": false, "channel_ids": ["'"$CHANNEL_ID"'"] }' ``` A `team_id` without a `game_id` follows the team from game to game. `fire_once: false` keeps the monitor active for the whole season. **4. Receive the firing** When a 49ers game ends in a win, Tako posts a `monitor.fired` event to your channel. This excerpt shows the body's `summary` and part of its `payload`: ```json theme={null} { "type": "monitor.fired", "summary": "San Francisco 49ers beat Los Angeles Rams 27 to 20.", "payload": { "team": "San Francisco 49ers", "opponent": "Los Angeles Rams", "home_team": "San Francisco 49ers", "away_team": "Los Angeles Rams", "team_score": 27, "opponent_score": 20, "outcome": "wins", "margin": 7, "winner": "San Francisco 49ers" } } ``` To fire only on a win by 10 points or more, add `"margin": 10` to `parameters`. To post each 49ers win to a Slack channel, register a `slack_workflow` notification channel, and attach it to the monitor. See [Send monitor firings to Slack](/documentation/integrating-tako/monitors/slack). ## Next steps * **[For your coding agent](/documentation/integrating-tako/monitors/for-coding-agent)**: the complete build reference, covering subjects, deliveries, debugging, and common mistakes. * **[Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries)**: the request body, signature verification, and how Tako retries a delivery. * **[Send monitor firings to Slack](/documentation/integrating-tako/monitors/slack)**: a Slack workflow that posts each firing to a Slack channel. * **[List monitor types](/api-reference/monitor-types)**: every type and the parameters it takes. # Send monitor firings to Slack Source: https://docs.tako.com/documentation/integrating-tako/monitors/slack Post each monitor firing to a Slack channel. Build a Slack workflow that starts from a webhook, register its trigger URL as a slack_workflow notification channel, and attach the channel to your monitors. > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. A notification channel whose `kind` is `slack_workflow` posts each firing to a Slack workflow. The workflow receives the request, and posts the firing's `summary` to a Slack channel, such as "San Francisco 49ers beat Los Angeles Rams 27 to 20." ## Before you start You need these things: * A paid Slack plan. A workflow that starts from a webhook requires one. * Permission to create workflows that start from a webhook. Slack workspace owners and admins can restrict who creates them. * A [Tako API key](https://tako.com/console/api-keys). ## Set up the workflow and the channel **1. Create a workflow that starts from a webhook** In Slack, open Workflow Builder, and create a workflow. For how the workflow starts, choose **From a webhook**. **2. Add a variable for each key that Tako sends** Add a **text** variable for each of these 7 keys: `summary`, `monitor_name`, `monitor_type`, `monitor_id`, `occurred_at`, `event_id`, and `event_type`. Tako sends every value as a string, and it sends all 7 keys for every monitor type. **3. Add a step that posts the message** Add a **Send a message to a channel** step, and choose the Slack channel. In the message, click **Insert a variable**, and choose `summary`. To name the monitor in the message too, insert `monitor_name`. **4. Publish the workflow, and copy its URL** Publish the workflow. Slack gives it a web request URL that starts with `https://hooks.slack.com/triggers/`. Keep the URL secret, because anyone who has it can start the workflow. **5. Register the URL as a notification channel** Replace the URL with the one that Slack gave your workflow. ```bash theme={null} SLACK_TRIGGER_URL="https://hooks.slack.com/triggers/..." SLACK_WORKFLOW_CHANNEL_ID=$(curl -s -X POST https://tako.com/api/v1/notification_channels \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Game night workflow", "kind": "slack_workflow", "url": "'"$SLACK_TRIGGER_URL"'"}' \ | jq -r .id) ``` `SLACK_WORKFLOW_CHANNEL_ID` holds the notification channel's `id`. **6. Attach the notification channel to a monitor** To post a new monitor's firings to Slack, pass the notification channel's `id` in `channel_ids` when you [create the monitor](/api-reference/monitors-create). To add Slack to a monitor that exists, send the monitor's full list of notification channels. Set `MONITOR_ID` to the monitor's `id`, and `WEBHOOK_CHANNEL_ID` to the `id` of the webhook channel that you want to keep: ```bash theme={null} curl -X PATCH https://tako.com/api/v1/monitors/$MONITOR_ID \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel_ids": ["'"$WEBHOOK_CHANNEL_ID"'", "'"$SLACK_WORKFLOW_CHANNEL_ID"'"]}' ``` `channel_ids` replaces the monitor's notification channels, so include every one that you want to keep. ## What the workflow receives Each firing sends one flat JSON object of strings: ```json theme={null} { "summary": "San Francisco 49ers beat Los Angeles Rams 27 to 20.", "monitor_name": "49ers wins", "monitor_type": "sports.game_result", "monitor_id": "5e2a9c14-7b3d-4f81-a0c6-2d9e8b7f1a43", "occurred_at": "2026-10-24T02:41:00+00:00", "event_id": "c8f1d2e7-3a4b-4c5d-9e6f-7a8b9c0d1e2f", "event_type": "monitor.fired" } ``` | Key | Value | | - | - | | `summary` | One sentence that describes the firing, with its values. Use it as the message. | | `monitor_name` | The `name` you gave the monitor. | | `monitor_type` | The monitor's type, such as `sports.game_result`. | | `monitor_id` | The monitor's id. | | `occurred_at` | When the occurrence happened, in ISO 8601. | | `event_id` | The firing's id, as [List a monitor's firings](/api-reference/monitors-firings) returns it. | | `event_type` | `monitor.fired`. | Every monitor type sends the same 7 keys, so one workflow can post the firings of all your monitors. ## Good to know * **`summary` carries the values.** The body holds no `payload`. To read a price or a score as its own field, create a `webhook` channel instead. See [Webhook deliveries](/documentation/integrating-tako/monitors/webhook-deliveries). * **Slack accepts 1 request per second for each workflow.** If Slack responds with a `429`, Tako retries the delivery. For the retry schedule, see [How Tako retries](/documentation/integrating-tako/monitors/webhook-deliveries#how-tako-retries). * **Tako pauses a notification channel after 2 consecutive failed deliveries.** If you unpublish the workflow, its deliveries fail, and Tako pauses the notification channel. Publish the workflow again, then [resume the notification channel](/api-reference/notification-channels-update). * **A notification channel's URL is fixed.** If you replace the workflow, it gets a new trigger URL. Every notification channel counts toward your limit, paused or not, so delete the old notification channel first. Deleting it removes it from every monitor. Then register the new URL as a new notification channel, and attach it to your monitors with `PATCH`. * **A `slack_workflow` notification channel counts toward your limit.** By default, you can hold 2 notification channels, of either kind. # Webhook deliveries Source: https://docs.tako.com/documentation/integrating-tako/monitors/webhook-deliveries The request Tako posts when a monitor fires, and how to verify it > [!WARNING] > > **Monitors is experimental.** Request and payload shapes can change without a deprecation window. Monitors isn't recommended for production workflows. When a monitor fires, Tako posts one request to every [notification channel](/api-reference/notification-channels-create) listed on that monitor. This page describes that request: its headers, its body, how to verify the signature, and how Tako retries a delivery that fails. To post firings to a Slack channel instead, see [Send monitor firings to Slack](/documentation/integrating-tako/monitors/slack). Tako follows the [Standard Webhooks](https://www.standardwebhooks.com/) specification, so a library that verifies Standard Webhooks signatures verifies these. ## Headers | Header | Value | | - | - | | `webhook-id` | The delivery id. Every retry of one delivery sends the same id, so use it to deduplicate. | | `webhook-timestamp` | When Tako sent this attempt, in Unix seconds. | | `webhook-signature` | `v1,` followed by the base64 HMAC-SHA256 signature. | | `content-type` | `application/json` | | `user-agent` | `Tako-Webhooks/1` | ## The request body A channel whose `kind` is `webhook` receives JSON in this shape: ```json theme={null} { "id": "4b1c2f2e-2a1b-4c7e-9f0a-6d3c1e5b8a90", "type": "monitor.fired", "created_at": "2026-09-12T20:05:00+00:00", "summary": "AAPL is at 233.70, up 3.2% from its 226.41 close on 2026-09-11, past your 3% threshold.", "monitor": { "id": "0d6f1e77-4b9a-4f0e-8a4d-2c9e7b1f3a55", "name": "AAPL up 3% in a day", "type": "stocks.pct_change", "parameters": { "ticker": "AAPL", "exchange": "XNAS", "window": "1d", "threshold": "3", "direction": "up" }, "subject": { "ticker": "AAPL", "exchange": "XNAS", "company": "Apple Inc." } }, "firing": { "id": "9a7c5d31-8e2b-4a66-b0d7-5f4e3c2a1b09", "occurrence_key": "2026-09-12", "occurred_at": "2026-09-12T20:00:00+00:00", "trigger": "push" }, "payload": { "ticker": "AAPL", "exchange": "XNAS", "window": "1d", "baseline_date": "2026-09-11", "baseline_close": 226.41, "price": 233.7, "percent_change": 3.2188 } } ``` | Field | Type | Description | | - | - | - | | `id` | string | The delivery id, matching the `webhook-id` header. | | `type` | string | The event type. Today the only value is `monitor.fired`. | | `created_at` | string | When Tako recorded the firing, in ISO 8601. | | `summary` | string | One sentence describing the firing, built from the monitor's parameters and the payload. Tako builds it at delivery time, so the wording can change without the payload changing. | | `monitor` | object | The monitor that fired. | | `firing` | object | The firing itself. | | `payload` | object | The values that caused the firing. Its fields depend on the monitor type. | `monitor` carries `id`, `name`, `type`, `parameters`, and `subject`. They match what [Get a monitor](/api-reference/monitors-get) returns: `parameters` is in the form [Create a monitor](/api-reference/monitors-create) accepts, and `subject` names what the monitor watches. `firing` carries `id`, `occurrence_key`, `occurred_at`, and `trigger`: * `occurrence_key` identifies the occurrence. Tako records one firing per key per monitor, so a source that reposts the same occurrence doesn't fire twice. * `occurred_at` is when the occurrence happened in the data. `created_at` is when Tako recorded it. The gap between them is however long the source took to report. * `trigger` says what woke the monitor. `push`, meaning new data arrived, is the only value Tako sends today. > [!WARNING] > > **Verify the signature before you trust the body.** Anyone who learns your channel's URL can post to it. The signature is the only thing that proves a request came from Tako. ## Verify the signature Tako signs the concatenation of the delivery id, the timestamp, and the raw request body, joined with periods: ``` {webhook-id}.{webhook-timestamp}.{raw body} ``` The key is the base64-decoded part of your channel's `secret` after the `whsec_` prefix. The signature is `v1,` followed by the base64 HMAC-SHA256 digest. Sign the bytes you received, not a re-serialized object. Tako sends compact JSON with no spaces after separators, and any reformatting breaks the signature. ```python Python theme={null} import base64 import hashlib import hmac import time TOLERANCE_SECONDS = 5 * 60 def verify(secret: str, headers: dict[str, str], raw_body: bytes) -> bool: timestamp = int(headers["webhook-timestamp"]) if abs(time.time() - timestamp) > TOLERANCE_SECONDS: return False key = base64.b64decode(secret.removeprefix("whsec_")) signed = f"{headers['webhook-id']}.{timestamp}.".encode() + raw_body expected = "v1," + base64.b64encode( hmac.new(key, signed, hashlib.sha256).digest() ).decode() return hmac.compare_digest(expected, headers["webhook-signature"]) ``` ```javascript Node.js theme={null} import crypto from "node:crypto"; const TOLERANCE_SECONDS = 5 * 60; export function verify(secret, headers, rawBody) { const timestamp = Number(headers["webhook-timestamp"]); if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false; const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const signed = Buffer.concat([ Buffer.from(`${headers["webhook-id"]}.${timestamp}.`), rawBody, ]); const expected = "v1," + crypto.createHmac("sha256", key).update(signed).digest("base64"); const received = Buffer.from(headers["webhook-signature"]); return ( received.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), received) ); } ``` Compare the signatures in constant time, and reject a `webhook-timestamp` far from the current time so an attacker can't replay an old delivery. ## How Tako retries Tako allows 10 seconds for a response, and doesn't follow redirects. | Response | What Tako does | | - | - | | `2xx` | Marks the delivery delivered. | | `408`, `429`, or any `5xx` | Retries. | | No response, such as a timeout or a connection error | Retries. | | Any other status, including a `3xx` redirect | Fails the delivery without retrying. | A delivery gets 6 attempts: the first one, then retries after 1 minute, 5 minutes, 15 minutes, 1 hour, and 2 hours 45 minutes. Tako stops retrying a delivery more than 4 hours 30 minutes old, even when attempts remain. Respond `2xx` as soon as you've stored the delivery, and do the slow work afterwards. A receiver that works for longer than 10 seconds gets retried, and receives the firing again. After 2 consecutive failed deliveries, Tako pauses the channel and sets its `status_reason` to `delivery_failures`. Monitors that post to a paused channel keep evaluating and recording firings, and those firings reach no one. Fix the destination, then [resume the channel](/api-reference/notification-channels-update), which clears the consecutive-failure count. To see how each delivery went, call [List a monitor's firings](/api-reference/monitors-firings) and read `deliveries`. ## Payload fields by monitor type `payload` is a flat object of strings, numbers, booleans, and nulls. The fields depend on the monitor's `type`. > **stocks.pct\_change** > > | Field | Type | Description | > | - | - | - | > | `ticker` | string | The listing's ticker. | > | `exchange` | string | The exchange's ISO 10383 MIC. | > | `window` | string | The window you set: `1d`, `1w`, or `1m`. | > | `baseline_date` | string | The session date of the reference close. | > | `baseline_close` | number | The reference close the move is measured from. | > | `price` | number | The price that met the threshold. | > | `percent_change` | number | The move from `baseline_close` to `price`, in percent. Negative for a down move. | > **stocks.crosses** > > | Field | Type | Description | > | - | - | - | > | `ticker` | string | The listing's ticker. | > | `exchange` | string | The exchange's ISO 10383 MIC. | > | `currency` | string, null | The exchange's currency. Null when the exchange has none recorded. | > | `level` | number | The level you set. | > | `crossed` | string | `above` or `below`, the direction of the crossing. | > | `previous_date` | string | The session date of the prior close. | > | `previous_close` | number | The prior close, on the other side of the level. | > | `price` | number | The price that crossed. | > **stocks.new\_52w\_extreme** > > | Field | Type | Description | > | - | - | - | > | `ticker` | string | The listing's ticker. | > | `exchange` | string | The exchange's ISO 10383 MIC. | > | `currency` | string, null | The exchange's currency. Null when the exchange has none recorded. | > | `broke` | string | `high` or `low`, the end of the range the price passed. | > | `extreme_close` | number | The 52-week extreme the price passed. | > | `extreme_date` | string | The session date of that extreme. | > | `price` | number | The price that passed it. | > **crypto.crosses** > > | Field | Type | Description | > | - | - | - | > | `ticker` | string, null | The asset's ticker, such as `BTC`. Null when the asset carries none. | > | `name` | string | The asset's name. | > | `coinmarketcap_id` | string | The CoinMarketCap id, which you can pass back as a `coinmarketcap_id`. | > | `level` | number | The level in USD you set. | > | `direction` | string | `above` or `below`, the direction you set. | > | `price` | number | The price in USD that reached the level. | > | `observed_at` | string | When the snapshot was taken, in ISO 8601. | > | `grain` | string | The sampling cadence the snapshot came from, as an ISO 8601 duration: `PT5M`, `PT30M`, or `P1D`. | > **forex.crosses** > > | Field | Type | Description | > | - | - | - | > | `base` | string | The ISO 4217 code of the base currency, such as `EUR`. | > | `quote` | string | The ISO 4217 code of the quote currency, such as `USD`. | > | `ticker` | string | The pair's ticker, the base code followed by the quote code, such as `EURUSD`. | > | `level` | number | The level you set, in the quote currency per one unit of the base. | > | `direction` | string | `above` or `below`, the direction you set. | > | `rate` | number | The closing mid-rate that reached the level. | > | `observed_at` | string | The end of the bar that carried that rate, in ISO 8601. | > **sports.game\_start** > > | Field | Type | Description | > | - | - | - | > | `game_id` | string | The game's id, as [List sports games](/api-reference/sports-games) returns it. | > | `league` | string, null | The league's name. | > | `team` | string, null | The team you watch. | > | `opponent` | string, null | The other team. | > | `home_team` | string, null | The home team. | > | `away_team` | string, null | The away team. | > | `start_time` | string | The scheduled start, in ISO 8601. | > **sports.game\_result** > > | Field | Type | Description | > | - | - | - | > | `game_id` | string | The game's id, as [List sports games](/api-reference/sports-games) returns it. | > | `league` | string, null | The league's name. | > | `team` | string, null | The team you watch. | > | `opponent` | string, null | The other team. | > | `home_team` | string, null | The home team. | > | `away_team` | string, null | The away team. | > | `team_score` | integer | The final score for the team you watch. | > | `opponent_score` | integer | The final score for the other team. | > | `outcome` | string | `wins` or `loses`, from the watched team's side. On a game-scoped monitor, it reads from the winner. | > | `margin` | integer | The margin of the result, always positive. | > | `winner` | string, null | The winning team's name. Null when the feed carries no name. | > | `winner_id` | string, null | The winning team's id, which you can pass back as a `team_id`. | > **sports.score\_event** > > | Field | Type | Description | > | - | - | - | > | `game_id` | string | The game's id, as [List sports games](/api-reference/sports-games) returns it. | > | `league` | string, null | The league's name. | > | `team` | string, null | The team that scored. On a team-scoped monitor, that's the team you watch. | > | `opponent` | string, null | The other team. | > | `side` | string, null | `home` or `away`, the side that scored. | > | `team_score` | integer | The score for `team`, after this score. | > | `opponent_score` | integer | The score for the other team. | > | `phase` | string | `live` or `final`, the state of the game when the score landed. | > **sports.odds\_crosses** > > | Field | Type | Description | > | - | - | - | > | `game_id` | string | The game's id, as [List sports games](/api-reference/sports-games) returns it. | > | `league` | string, null | The league's name. | > | `home_team` | string, null | The home team. | > | `away_team` | string, null | The away team. | > | `team` | string, null | The team you named. | > | `opponent` | string, null | The other team. | > | `market` | string | The market you set: `moneyline`, `spread`, or `total`. | > | `market_type` | string | The feed's market, which can be more specific than `market`. A `moneyline` reads `moneyline` or `3way`. | > | `side` | string, null | `over` or `under` for a total. Null for a moneyline or a spread. | > | `book` | string | The sportsbook's display name. | > | `book_slug` | string, null | The sportsbook's id, such as `draftkings`. Null when the feed carries no id. | > | `level` | number | The level you set. | > | `line` | number, null | The handicap or total that was quoted. Null for a moneyline, which has no line. | > | `price_american` | number, null | The quoted price in American odds. | > | `price_decimal` | number, null | The same price in decimal odds. | > | `open_line` | number, null | The opening handicap or total. Null for a moneyline. | > | `open_price_american` | number, null | The opening price in American odds. | > | `is_live` | boolean | True when the quote came from the live feed rather than the prematch one. | > [!NOTE] > > The fields a type sends are its contract. Tako can add a field, which your receiver should ignore, and removing one is a breaking change. While monitors is experimental, these shapes can change without a deprecation window. ## Slack workflow bodies A channel whose `kind` is `slack_workflow` receives a flat body of strings instead, because a Slack Workflow Builder trigger rejects nested values: ```json theme={null} { "summary": "AAPL is at 233.70, up 3.2% from its 226.41 close on 2026-09-11, past your 3% threshold.", "monitor_name": "AAPL up 3% in a day", "monitor_type": "stocks.pct_change", "monitor_id": "0d6f1e77-4b9a-4f0e-8a4d-2c9e7b1f3a55", "occurred_at": "2026-09-12T20:00:00+00:00", "event_id": "9a7c5d31-8e2b-4a66-b0d7-5f4e3c2a1b09", "event_type": "monitor.fired" } ``` This shape carries no `payload`, so a receiver that needs the values that caused the firing needs a `webhook` channel. Tako signs and retries both kinds the same way. To set up a Slack workflow that receives this body and posts `summary` to a channel, see [Send monitor firings to Slack](/documentation/integrating-tako/monitors/slack). ## What Tako accepts as a URL [Create a notification channel](/api-reference/notification-channels-create) rejects a `url` that: * Doesn't use `https`. * Uses a port other than 443 or 8443. * Embeds credentials, as in `https://user:pass@example.com/hook`. * Has a host that doesn't resolve. * Has a host that resolves to a loopback, link-local, multicast, private, reserved, or shared address. Tako normalizes the URL it stores: it lowercases the host, drops the fragment, and gives a bare host a trailing slash. Read `url` in the response to see what Tako posts to. # For Your Coding Agent Source: https://docs.tako.com/documentation/integrating-tako/search/for-coding-agent The build reference for coding agents integrating Tako Search. Self-contained and copy-paste-ready — endpoint, X-API-Key auth, every request parameter, the response shape, and runnable cURL, Python, and TypeScript examples. Use this page to generate a working Search integration. This page is a complete, self-contained reference for building against **Tako Search**. It is written for coding agents (and developers in a hurry): 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 the auto-generated schema, see the [Search API reference](/api-reference/search-v3); for a narrative introduction, see the [Overview](/documentation/integrating-tako/search/overview). ## What Search does `POST https://tako.com/api/v3/search` takes a natural-language `query` and returns **both data results — Tako knowledge cards** (interactive, embeddable data visualizations with structured fields, named sources, and methodology) — **and web search results**, with no synthesized answer on top. Reach for it when you want to render or post-process the results yourself. Responses are compact and fast, so you can call it frequently and in parallel inside agentic loops. Unlike a web-search API that returns text snippets, each Tako card is a live visualization backed by structured data and attribution — so you get renderable, citable results, not just links. * **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). * **Request body** is shared with [Answer](/documentation/integrating-tako/answer/overview) — the only difference is Answer adds a synthesized `answer` string. ## Install ```bash theme={null} pip install tako-sdk # Python npm install tako-sdk # TypeScript / JavaScript ``` ## Minimal working example Each snippet makes the same call and is complete after you set `TAKO_API_KEY`. ```bash cURL theme={null} curl -X POST https://tako.com/api/v3/search \ -H "X-API-Key: $TAKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What is the price of Silver?" }' ``` ```python Python theme={null} import os from tako import Configuration, SearchRequest from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) results = client.search(SearchRequest(query="What is the price of Silver?")) print(results.request_id) for card in results.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! }); const results = await tako.search({ query: "What is the price of Silver?" }); console.log(results.request_id); for (const card of results.cards ?? []) { console.log(card.title, card.webpage_url); } ``` ## Request parameters The body is a `SearchRequest`. Only `query` is required. | Parameter | Type | Default | Description | | - | - | - | - | | `query` | string | — *(required)* | The natural-language query. Inject session context for relevance (e.g. `"MSFT stock price last 6 months"` rather than `"MSFT"`). | | `effort` | string enum | `fast` | `fast` (balanced default), `instant` (serves cached embeds without re-running data retrieval — fastest, available on all tiers), or `deep` (widens retrieval and adds an LLM rerank — premium tier). | | `sources` | object | both sources, 5 results each | Which indexes to search. See [Choosing sources](#choosing-sources). | | `country_code` | string | `US` | ISO 3166-1 alpha-2 country code. | | `locale` | string | `en-US` | BCP-47 locale. | | `timezone` | string \| null | none | IANA timezone (e.g. `America/New_York`). | | `output_settings` | object \| null | none | Response-shape control: `image_dark_mode` (bool) and `force_refresh` (bool, default `false`, **`instant` mode only**). | ### Forcing entity boundaries in `query` Tako identifies the entities in the query before it searches. Double quotes force a phrase to be exactly one entity, and an optional `:LABEL` suffix sets its type. Use this when you already know the entity — an agent usually does. ```json theme={null} { "query": "\"tesla motors\" club revenue" } { "query": "\"Tesla\":PRODUCT price" } ``` Labels: `PERSON`, `ORG`, `GPE`, `LOC`, `PRODUCT`, `DATE`, `EVENT`, `MONEY`, `CARDINAL`, `METRIC`, `TIME_PERIOD`, `UNITS`. Quoting is all-or-nothing: every `"` in the query must belong to a non-empty pair of 80 characters or less. If one quote is unmatched, Tako ignores every quote in that query and searches as usual, so an ordinary web-style phrase query still works. An unrecognized label stays in the query text. The quotes never reach the search text. Answer accepts the same syntax. ### Latency Target p50 latency (not a guarantee), by `effort`: | `effort` | Target p50 | | - | - | | `instant` | \~300 ms | | `fast` *(default)* | \~500 ms | | `deep` | \~5 s | ## Choosing sources By default Search queries **both** Tako's curated knowledge graph (`data`) and the live web (`web`), 5 results each. `sources` is an **object** whose keys select the indexes — **a source is searched only if its key is present.** | `sources` value | Searches | | - | - | | *(omitted)* | both — equivalent to `{ "data": {}, "web": {} }` | | `{ "data": {} }` | Tako curated knowledge cards only | | `{ "web": {} }` | web results only | | `{ "data": {}, "web": {} }` | both, explicitly | Each source takes optional per-source settings: | Setting | Applies to | Type | Default | Description | | - | - | - | - | - | | `count` | `data`, `web` | integer (1–20) | `5` | Max results from this source. | | `include_contents` | `data`, `web` | boolean | `false` | Inline the underlying data in the response. | | `node_ids` | `data` only | array of strings (max 20 distinct) | none | Pin resolved [Data Graph](/documentation/integrating-tako/data-graph/for-coding-agent) node ids: pinned nodes get **guaranteed retrieval candidacy + a strong boost**; organic results are unaffected. Malformed id → 400; an id that no longer resolves is skipped. | | `strict` | `data` only | boolean | `false` | Return **only** cards matching a pinned node. Requires non-empty `node_ids` (else 400). | ```json theme={null} { "query": "Nvidia quarterly revenue", "sources": { "data": { "count": 10 } } } ``` > [!NOTE] > > The legacy key `tako` is accepted as a synonym for `data` and is mapped to it. Prefer `data` in new code. ## What a response looks like A `200` returns a `SearchResponse`: ```json theme={null} { "cards": [ { "card_id": "KfYeym50vtsF93LMsIFW", "title": "Silver Spot Price (USD/oz)", "description": "Spot price of silver in US dollars per troy ounce, updated intraday.", "webpage_url": "https://tako.com/card/KfYeym50vtsF93LMsIFW/", "image_url": "https://tako.com/api/v1/image/KfYeym50vtsF93LMsIFW/", "embed_url": "https://tako.com/embed/KfYeym50vtsF93LMsIFW/", "sources": [ { "source_name": "LBMA", "source_description": "London Bullion Market Association precious-metals benchmark prices.", "source_index": "tako" } ], "source_indexes": ["tako"], "content": { "format": "csv", "cost": 0.001 } } ], "web_results": [], "request_id": "f20f965b-6bbd-40df-b50b-a8861f34df24" } ``` | Field | Type | Description | | - | - | - | | `cards` | array | Tako knowledge cards (see below). | | `web_results` | array | Web results: `title`, `url` (both always present), plus optional `snippet`, `source_name`, `publish_date`, `content`. | | `usage` | object | Present when your account is billed pay-as-you-go; `usage.total_cost_usd` is the request total. The key is **absent** otherwise — sum the per-card `content.cost` values. See [Credits & billing](/documentation/additional-resources/credits-and-billing#the-cost-field). | | `request_id` | string | Always present — log it for support/debugging. | Each card (`TakoCard`) carries: | Field | Description | | - | - | | `card_id` | Stable card identifier. | | `title`, `description` | Human-readable label and summary. | | `embed_url` | iframe URL for the interactive card — see [Embedding Knowledge Cards](/documentation/integrating-tako/embedding-knowledge-cards). | | `image_url` | Static image of the card. | | `webpage_url` | Canonical card page; also the `url` you pass to [Contents](#downloading-the-underlying-data). | | `sources` | Named sources backing the card (`source_name`, `source_description`, `source_index`) — use for attribution. | | `methodologies` | How the data was collected/derived. | | `content` | A download descriptor (`format`: `csv` or `text`, `cost`, and `export_pricing`, plus `total_rows`, `truncated`, and `premium_rows` when the card inlines rows or can hold premium values), present **only when the result's data can be downloaded**. If it's **absent**, that result's data isn't exportable — skip it rather than calling Contents, which will refuse it. | ## People and company lists A query that asks for people or companies with at least one filter returns a list card, built from company registries and licensed company and people data: * **People:** a role plus a company or a place, or a people word such as "executives" plus a company, such as `"Heads of product at Figma"`, `"VPs of sales at Salesforce"`, `"executives at Databricks"`, or `"CFOs in Austin"`. Name several companies and Search returns a separate list card for each, such as `"engineers at Stripe or Plaid"`. * **Companies:** an industry or a company word such as "companies" or "startups", plus a place, an employee count, a funding stage, public or private, or a founding year, such as `"Cybersecurity startups in Tel Aviv"`, `"Series B fintechs"`, or `"AI startups founded since 2020"`. An industry plus a company word also works: `"fintech companies"`. A list card works like any other card, with these differences: * **It shows the top 25 matches.** A [Contents](/documentation/integrating-tako/contents/for-coding-agent#people-and-company-list-cards) export runs the search again and returns up to 1,000 rows. The card's `content.total_rows` is the most rows that export returns, and `content.truncated` is `true` when that's more than the card shows. Pass `total_rows` as `max_rows` to export the whole list. * **Each row links to its own card.** The row's `tako_url` column holds a link to a one-row card for that person (`/card/pe_…`) or company (`/card/co_…`). Pass it to Contents to export just that row. * **Some columns are premium.** A people list's contact columns (`person_contact`) and some company-list fields (`company_record`) are withheld in Search and in a default Contents export; [Premium columns](/documentation/integrating-tako/contents/for-coding-agent#premium-columns) names them. The card's `content.premium_rows` counts the records that hold them. Reveal them with a Contents export that sets `premium: true`. For a list that takes several steps, such as a registry screen combined with a company's directors, use the [Company Agent](/documentation/integrating-tako/agent/company). ## When to use Search Search is for **fast, structured data retrieval** — cards and web results you render or post-process yourself, with no synthesized answer. Reach for it when: * **You want structured results, not prose.** No answer-synthesis LLM call in the path, so responses come back fast — about **500 ms** on the default `fast` effort (see [Latency](#latency)) — call it frequently and fan out across many entities in parallel. * **You're feeding an agentic loop that needs a lot of accurate data, fast.** One query per entity; act on the structured cards instead of waiting on generated text. * **You want more than a single value.** Each result is a knowledge card — a full series with named sources and methodology — so one call returns the surrounding context too, ready to render or compute on. * **You want broad coverage.** Search queries Tako's curated knowledge graph, which includes companies and the people at them, **and** the live web by default, so you get a meaningful result across far more queries than either source alone. **Want prose instead?** Use [Answer](/documentation/integrating-tako/answer/for-coding-agent) for a synthesized, source-attributed answer over the same data in one call. **Need multi-step reasoning** — resolving a cohort, ranking a set, multi-hop aggregation? Use the [Agent](/documentation/integrating-tako/agent/for-coding-agent). ## Downloading the underlying data A result whose data is downloadable includes a `content` descriptor. Pass that result's `webpage_url` (for a card) or `url` (for a web result) to **Contents** (`POST /v1/contents`) to download it — a CSV of a Tako card's data, or extracted text for a web page. Not every result carries one: when a result's underlying data can't be exported, `content` is omitted. Treat its absence as "no download available" and skip the Contents call for that result. See the [Contents For Your Coding Agent](/documentation/integrating-tako/contents/for-coding-agent) page for the full request and response reference, including `url` vs `inline` delivery modes. To skip the second call, inline the data in this response with the per-source [`include_contents`](#choosing-sources) setting. ## Common mistakes > [!WARNING] > > Avoid these — they are the patterns coding agents most often get wrong. | Wrong | Correct | | - | - | | `Authorization: Bearer $KEY` | `X-API-Key: $KEY` | | `"sources": ["data"]` (array) | `"sources": { "data": {} }` (object — key presence selects the source) | | Omitting `sources` to get only Tako data | Omitting `sources` searches **both**; pass `{ "data": {} }` for data-only | | Confusing this with the Agent's `source_indexes` | Search/Answer use the `sources` **object**; the Agent uses a `source_indexes` **array** | | Leaving one unmatched `"` in `query` | Pair every quote — a single stray quote turns off [entity forcing](#forcing-entity-boundaries-in-query) for the whole query | ## Errors Failures return a `BaseAPIError` body: `{ "error_message": "...", "error_type": "..." }`. | Status | Meaning | Fix | | - | - | - | | `400` | Malformed request (bad JSON, invalid parameter). | Validate the body against the parameter table 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). | | `408` | The request timed out. | Retry; consider `effort: "instant"` for the fastest path. | # Search Source: https://docs.tako.com/documentation/integrating-tako/search/overview Fast data retrieval from proprietary licensed partners and web search results for any written query. Building with a coding agent? [Give your agent the full reference →](/documentation/integrating-tako/search/for-coding-agent) ## When to use Search [Search](/api-reference/search-v3) is for **fast retrieval you render or post-process yourself**, with no written answer on top. One call returns **both** kinds of result — **data results** (interactive Tako knowledge cards backed by structured data) **and web search results**. Reach for it when: > **You want raw, structured results, not prose** > > There's no answer-synthesis LLM call in the loop, so results come back fast — quick enough to fan out across many entities in parallel. > **You're building an agentic system that needs a lot of accurate data, fast** > > Call Search repeatedly — one query per entity — and act on structured results instead of waiting on generated text. > **You want more than a single number** > > Each result is a knowledge card — a full data series with its sources and methodology, not a lone value — so one query returns the surrounding context too, ready to render or compute on. > **You want broad coverage you can trust** > > No single source has everything, so Search queries Tako's curated index (finance, macro, geopolitics, sports, weather, companies and the people at them) **and** the live web **by default** — broad coverage you can trust. Choosing between Tako's surfaces (see the [full comparison](/documentation/integrating-tako/agent/overview#choosing-a-tako-surface)): | Surface | What it's for | | - | - | | **[Search](/api-reference/search-v3)** | Fast, parallel retrieval of structured data and cards, in one call, no synthesized answer. | | **[Answer](/documentation/integrating-tako/answer/overview)** | A written, source-attributed answer over the same retrieval, in one call. | | **[Retrieval Agent](/documentation/integrating-tako/agent/retrieval)** | Multi-step research returning schema-defined structured data and cards. | | **[Answer Agent](/documentation/integrating-tako/agent/answer)** | Deep research synthesizing a cited answer backed by visualizations. | ## Example [Try in Playground](https://tako.com/playground) Inject context from the user's session into the query to sharpen relevance — send `"MSFT stock price last 6 months"` rather than just `"MSFT"`. **cURL** ```bash theme={null} curl -X POST https://tako.com/api/v3/search \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What is the price of Silver?" }' ``` **Python** ```bash theme={null} pip install tako-sdk ``` ```python theme={null} import os from tako import Configuration, SearchRequest from tako.lib import Tako config = Configuration() config.api_key["apiKey"] = os.environ["TAKO_API_KEY"] client = Tako(config) results = client.search(SearchRequest(query="What is the price of Silver?")) print(results.request_id) for card in results.cards or []: print(card.title, card.webpage_url) ``` > **Example Result** > > Query: "What is the price of Silver?" > > [https://tako.com/embed/KfYeym50vtsF93LMsIFW/?dark\_mode=false](https://tako.com/embed/KfYeym50vtsF93LMsIFW/?dark_mode=false) ## Use cases > **Pulling real-time data across many entities** > > Fan out Search calls in an agentic loop to gather fresh, structured data fast. > > ```python theme={null} > for ticker in ["NVDA", "AMD", "INTC"]: > results = client.search(SearchRequest(query=f"{ticker} quarterly revenue")) > ``` > **Curated data only — skip the web** > > Restrict to Tako's authoritative knowledge graph when you only want vetted sources. > > ```python theme={null} > results = client.search(SearchRequest(query="US inflation rate", sources={"data": {}})) > ``` > **Rendering interactive cards in your app** > > Embed the live visualization from each result straight into your UI. Each card also carries `title`, `description`, `image_url`, `webpage_url`, and `sources` — use them for labels, a static image, or data provenance alongside the interactive `embed_url`. > > ```python theme={null} > results = client.search(SearchRequest(query="S&P 500 over the last 5 years")) > for card in results.cards or []: > print(card.embed_url) # drop into an