# 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 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. **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. 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`. # 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. **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" } ``` 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. | 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. 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. **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. 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 ## 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`. | `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 ## 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). **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 ## 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 ## 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 ## 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 ## 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 ## 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 ## 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). **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 ## 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 ## 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 ## 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 ## 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). 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 ## 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). **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 premium sources; web-page text extraction bills `$0.001` per page. 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. 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 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.
Follow the agent quickstart in the console. It generates a prompt for your coding agent in seconds to build with the Tako APIs. ```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 New to Tako? [Sign up](https://tako.com) and get free credits to start building. Create an API key from your [Tako console](https://tako.com/console/api-keys). 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. 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. 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. 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. 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. **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. ```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); } ```

A knowledge card — a full data series with its sources, ready to render or compute on.

``` ## 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 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. ```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" } } ] }' ``` ```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). `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 }, ), ], )) ``` 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"} ] }, ), ], )) ``` 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. 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"`) | 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} ] } ] }, ), ], )) ``` 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` 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). | | `quote_only` | boolean | `false` | Return the `cost` for this download without fetching it or being charged. | ## 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`. | ```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. | ## 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. ## 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 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). | | `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. | | `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. | # Contents Source: https://docs.tako.com/documentation/integrating-tako/contents/overview ## 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: 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. Contents operates on a URL you already have: find the result with Search or Answer first, then fetch what's behind it. 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: Call [Search](/documentation/integrating-tako/search/overview) or [Answer](/documentation/integrating-tako/answer/overview) with a query. A downloadable result carries a `content` descriptor in the response (`format` is `csv` for a Tako card, `text` for a web page). Pass that result's URL — a card's `webpage_url` or a web result's `url` — to **Contents** to download the data. 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 **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. **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 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"}' ``` **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. Every price below is also readable from the API before you fetch, so you never have to infer a bill from this page. ## 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 premium 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 premium 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 — a rate table on a docs page cannot account for per-source rates or per-account terms, and `export_pricing` can. `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). ## 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 premium card costs `$0.006` where the full 2,000 rows cost `$0.201`. 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. **`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). 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`). **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. | 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. **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 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 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: 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. 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`. 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. 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 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. 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:** 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 ```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`. ```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. ```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. ```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} ``` ``` ``` **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} ``` 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} ```
```