Charts

Ask in plain words and get a finished chart card, with the right chart, key figures, a title that states the finding and the source of every value.

POST /v1/charts turns a question in plain words into a chart card that already made the data-visualization decisions: which chart, what to measure, how to aggregate it (totals, shares, weekly grouping, a moving average, a base-100 index), the key figures on top, a title that states the finding ("The blue dollar rose 4.1% this week"), annotations, and the source of every value. You can also send your own data and get the same card.

  • The chart that tells it. One of 12 types, picked for the data. Force another and it's respected while the data allows it.
  • Numbers you can check. A figure (a price, a rate, a poll) is found verbatim in what was published and comes with the articles it came from. It is never invented.
  • Ready to publish. An iframe that adapts to its width, a PNG at 2× and an SVG, in four themes, public by id. From the API, the MCP server or the dashboard's playground.
  • Fast. A card is drawn in under a millisecond on our side. Coverage charts come back in about a second; charts with figures, in 2 to 5 seconds.

From a query

curl https://api.typesearch.ai/v1/charts \
  -H "Authorization: Bearer $TYPESEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "blue dollar this week" }'

It answers 201 with the card:

201 Created (abridged)
{
  "id": "chart_8k2m9q4x7w1p3n5z",
  "object": "chart",
  "type": "line",
  "theme": "light",
  "locale": "en",
  "chart": {
    "type": "line",
    "title": "The blue dollar rose 4.1% this week",
    "subtitle": "Selling rate, in pesos · Sep 19–25",
    "x": { "kind": "time", "granularity": "day" },
    "y": { "format": "currency", "currency": "ARS", "label": "Selling" },
    "series": [
      {
        "name": "Selling",
        "points": [
          { "x": "2026-09-19", "y": 1215, "sources": [2] },
          { "x": "2026-09-24", "y": 1250, "sources": [1] },
          { "x": "2026-09-25", "y": 1265, "sources": [0, 1] }
        ]
      }
    ],
    "kpis": [
      { "label": "Latest", "value": 1265, "change": { "value": 4.1, "percent": true } },
      { "label": "High", "value": 1265 },
      { "label": "Low", "value": 1215 }
    ]
  },
  "compatible_types": ["line", "area", "bar", "kpi", "table"],
  "plan": {
    "recipe": "figure_trend",
    "type": "line",
    "confidence": 0.92,
    "alternatives": [
      { "type": "area", "probability": 0.3 },
      { "type": "bar", "probability": 0.22 },
      { "type": "kpi", "probability": 0.14 }
    ],
    "topic": "the blue dollar",
    "needs": { "queries": ["blue dollar this week"], "mode": "fast", "days": 7, "min_points": 3, "max_results": 50 }
  },
  "sources": [
    {
      "url": "https://diario.example/economia/dolar-blue-hoy",
      "title": "El dólar blue cerró a 1.265 pesos",
      "source": "Diario Ejemplo",
      "published_at": "2026-09-25T18:10:00Z"
    },
    {
      "url": "https://noticias.example/mercados/cotizacion",
      "title": "Cotización del dólar: el blue tocó su máximo del mes",
      "source": "Noticias Ejemplo",
      "published_at": "2026-09-24T17:02:00Z"
    },
    {
      "url": "https://cronica.example/economia/blue-arranca-la-semana",
      "title": "El blue arranca la semana en 1.215 pesos",
      "source": "La Crónica Demo",
      "published_at": "2026-09-19T15:40:00Z"
    }
  ],
  "embed_url": "https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z",
  "image_url": "https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z.png",
  "svg_url": "https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z.svg",
  "svg": null,
  "created_at": "2026-09-26T14:02:11.482Z",
  "expires_at": "2026-12-25T14:02:11.482Z",
  "cached": false,
  "usage": { "cost_usd": 0.0019, "mode": "fast", "queries": 1, "duration_ms": 2840 },
  "warnings": []
}
  • chart: what is drawn. The series, the key figures on top (kpis), the annotations, how each axis is formatted, and a title that states the finding (unless you send your own title).
  • plan: what was measured (recipe), the chart it chose, and alternatives: other types that fit, which you can switch to for free with PATCH.
  • compatible_types: every type this data supports.
  • sources and each point's sources: the articles behind the chart, and the indexes in sources where each value was published.
  • embed_url, image_url and svg_url: public by id, no key needed. See Embed it and PNG and SVG.
  • usage.cost_usd: what the request was billed. See Pricing.

Everything you know from search narrows a chart too: days, published_after, published_before, countries, languages, include_domains, exclude_domains and timezone. When the query doesn't name the things to compare with "vs", pass them in compare (2 to 5).

What you can ask

The plan reads your query and picks a recipe: what to measure, the chart that tells it, the days to look at and the cheapest mode that gets the data (mode: "auto", the default).

recipeAsk forWhat it measuresChartDaysMode
coverage"coverage of the Budget"Articles that mention the topic, per day, over the whole period. A clear peak is marked with that day's headline.area30ultra
coverage_compare"Milei vs Bullrich coverage over time"The same count for 2 to 5 names, one line each.line21ultra
share_of_voice"Milei vs Bullrich vs Kicillof coverage"Each name's share of the articles.donut30ultra
tone"tone of the coverage of the IMF"Positive, neutral and negative articles, relative to your query.donut14fast
tone_compare"tone of Milei vs Bullrich coverage"The same split, for each name.stacked_bar30fast
outlets"which outlets cover lithium"Articles per outlet.bar_horizontal30ultra
timeline"timeline of the dam case"The milestones of a story: the busiest day of each week, and the story most outlets told that day.timeline60fast
figure_trend"blue dollar this week"A figure the articles publish (a price, a rate, a poll), day by day.line, or kpi for "today"7fast
figure_compare"inflation" with compare: ["Chile", "Peru"]The latest published value of a figure, for each name.bar14fast
answersAny query, plus one typed question in questionsHow the answers split across the articles.donut7fast
index_prices"prices" with indexPrice per product, cheapest first (up to 12).bar_horizontalwhole indexfast
index_scatter"price and rating" with indexPrice against rating. Each dot's size is its number of reviews.scatterwhole indexfast
index_availability"availability" with indexProducts in stock, out of stock and on pre-order.donutwhole indexfast
index_brands"brands" with indexProducts per brand.donutwhole indexfast
  • Days. The defaults above apply unless a date in the query ("this week", "in August"), days, published_after or published_before sets the period.
  • Mode. Set mode to fix it. In auto, a figure chart that doesn't find enough figures in headlines and standfirsts reads the best articles (normal), and is billed in that mode.
  • Words first. When the query says it ("coverage", "tone", "which outlets", "timeline", "price", "vs"), the plan follows the words (plan.confidence: 1). Otherwise it is decided once and remembered for 7 days.
  • Custom index. With index (idx_…), the chart is built from the structured data each page of your custom index declares: price, rating, reviews, availability, brand.

A typed question makes an answers chart: how the articles answer it. One question per chart, with the same format as in structured output:

Request body
{
  "query": "the 2027 Budget",
  "questions": {
    "stance": {
      "type": "choice",
      "instructions": "What is the article’s stance on the budget?",
      "criteria": { "supportive": null, "critical": null, "neutral": "Reports without taking a side." }
    }
  }
}

How figures are found

A chart with figures (figure_trend, figure_compare) never makes a number up:

  • Verbatim. Numbers are found as written in the headlines, standfirsts and excerpts of the articles, with their currency or unit. Each one is then only confirmed, or discarded, as the value you asked for.
  • One value per day. Each point is the median of what was published that day, so two outlets with slightly different figures don't draw a zigzag. In a comparison, each name gets its latest published value.
  • A source for every point. Each point's sources lists indexes in the response's sources: the articles where that value was published. Show them in a tooltip or a footnote.
A point, and where it was published
{ "x": "2026-09-25", "y": 1265, "sources": [0, 1] }

If a publisher later asks not to be shown, its sources leave the card when it is served, and a point left without a source is hidden.

From your own data

Send data instead of query and you get the same card: we pick the chart (with type: "auto"), sort, summarize, add the key figures and write a title (unless you send title). It costs $0.30 per 1,000 charts. source sets the line in the footer.

Series

curl https://api.typesearch.ai/v1/charts \
  -H "Authorization: Bearer $TYPESEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "series": [
        {
          "name": "Subscribers",
          "points": [
            { "x": "2026-06-01", "y": 18200 },
            { "x": "2026-07-01", "y": 19650 },
            { "x": "2026-08-01", "y": 21400 },
            { "x": "2026-09-01", "y": 23900 }
          ]
        }
      ],
      "y": { "label": "Subscribers", "format": "number" }
    },
    "source": "Diario Ejemplo, internal data"
  }'
  • x is a date (2026-09-01) for a time axis, a number for a numeric one (a scatter), or text for categories. To say it yourself, set data.x.kind to time, category or number.
  • data.y formats the values: format (number, percent or currency, with currency: "USD"), unit, decimals, label, and higher_is_better: false when lower is better (a price): rankings then put the lowest first.
  • For a scatter, data.x_measure formats the x axis, each point's r sizes its dot and label names it.
  • role on a series (positive, neutral, negative, other) colors it as tone. highlight names the category to stand out.

Events

A list of dated events makes a timeline:

Request body
{
  "data": {
    "events": [
      { "date": "2026-07-30", "label": "Technical report on the dam leaks", "detail": "Diario Ejemplo" },
      { "date": "2026-08-06", "label": "The province halts construction" },
      { "date": "2026-08-19", "label": "The builder files an injunction", "end": "2026-08-22" }
    ]
  },
  "title": "The dam case, in three milestones"
}

Columns and rows

Request body
{
  "data": {
    "columns": [
      { "key": "model", "label": "Model" },
      { "key": "price", "label": "Price", "format": { "format": "currency", "currency": "USD" } },
      { "key": "rating", "label": "Rating", "format": { "decimals": 1 } }
    ],
    "rows": [
      { "model": "Ridgeline Trail 4", "price": 109, "rating": 4.6 },
      { "model": "Canyon Grip GTX", "price": 119, "rating": 4.4 }
    ]
  }
}
Limit
Series12, with up to 1,000 points each
Events50
Columns8
Rows100

With type: "auto", events make a timeline; columns and rows, a table; a numeric x, a scatter; dates, a line (a kpi for one or two points); several series of categories, a stacked_bar; percentages that add up to 100 in up to 6 parts, a donut; more than 7 categories, a bar_horizontal; fewer, a bar.

Chart types

type: "auto" (the default) picks the one that tells the data best. The other 12:

typeWhen auto picks itWhat the data needs
lineA figure over time, or several names over time.3 points in time or more.
areaCoverage over time: a volume.3 points in time or more.
barA few categories, or a figure compared across names.Up to 12 categories.
bar_horizontalRankings: outlets, prices, long names or more than 12 categories.One series of categories.
stacked_barEach category split into parts: tone by name.2 series or more, positive values, up to 12 categories.
pieParts of a whole.Positive values, 2 to 30 parts.
donutParts of a whole, with the total in the center: share of voice, tone, brands, availability.Positive values, 2 to 30 parts.
scatterTwo figures against each other: price and rating.A numeric x, 5 points or more.
funnelStages that shrink.One series of 3 to 7 categories that never grows.
timelineThe milestones of a story.Dated events.
kpiOne figure with its change: "today", "now".One point or more.
tableRows and columns.Always possible.

Forcing a type ("type": "donut") works while the data allows it. When it doesn't, the card is drawn with the closest type that fits and says so in warnings with chart_type_adjusted. compatible_types lists the types this data supports, best first.

The rules we apply

What someone who makes charts for a living would do, applied to every card. The same data always gives the same card.

  • Long daily series become weeks (past 45 days) or months (past 8 months), adding up counts and keeping the last value of a figure (grouped).
  • No dual axes. Lines on very different scales (more than 25 times apart) are compared as an index, base 100 (indexed).
  • Noisy daily series of three weeks or more get a 7-point moving average, with the raw series faint underneath (chart.smoothing).
  • Pies and donuts show at most 6 slices, with the rest in "Others", always last. Rankings show at most 12 bars; lines, areas and stacked bars, up to 6 series.
  • Rankings sort biggest first, or lowest first when lower is better: a price, inflation, country risk.
  • Long category names turn vertical bars into a horizontal ranking, so every label fits.
  • A log scale only on lines, and only when the values span more than 200 times. Never on bars.
  • Key figures on top: total, peak and daily average for coverage; latest (with its change), high and low for a figure.
  • The title states the finding: "Budget coverage peaked on Sep 10", "Milei gets 42% of the coverage", "The priciest models are not the best rated".

Themes

themeLooksFor
lightWhite and warm grays. The default.Apps, dashboards, docs.
darkNear black.Dark interfaces.
editorialWarm paper, print ink, serif figures.Media and blogs.
electricDeep blue gradient.Slides and landing pages.

The first series is always the typesearch electric blue, and the rest are told apart with color blindness too. Set theme when you create the chart, switch it for free with PATCH, or show one view in another theme with ?theme= on the embed or the image.

Embed it

embed_url is a page made to go in an iframe. It adapts to its width (under 480 px it switches to a compact layout), shows tooltips on hover and touch, weighs a few KB and loads nothing from third parties: the fonts are ours.

HTML
<iframe
  src="https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z"
  title="The blue dollar rose 4.1% this week"
  style="width: 100%; border: 0"
  height="440"
  loading="lazy"
></iframe>

The page tells its parent the height it needs, on load and on every resize, with { type: "typesearch:chart:resize", id, height }. Listen for it once and every chart on the page fits without scrollbars:

HTML
<script>
  window.addEventListener('message', (event) => {
    if (event.origin !== 'https://api.typesearch.ai') return;
    const data = event.data;
    if (!data || data.type !== 'typesearch:chart:resize') return;
    for (const frame of document.querySelectorAll('iframe')) {
      if (frame.contentWindow === event.source) frame.style.height = `${data.height}px`;
    }
  });
</script>

data.id is the chart id, if you'd rather match by it. Add ?theme=dark (or editorial, electric) to show it in another theme without making a new chart.

Embeds, images and SVGs are cached for 5 minutes, so a change made with PATCH shows up within that time. Each IP can load up to 600 per minute.

PNG and SVG

  • image_url (….png): the card at twice the resolution, for chat, email or slides. In Markdown: ![The blue dollar rose 4.1% this week](https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z.png).
  • svg_url (….svg): the vector card.
  • ?size=compact gives either one in the narrow layout, for phones and stories, and ?theme= in another theme: https://api.typesearch.ai/embed/chart_8k2m9q4x7w1p3n5z.png?size=compact&theme=electric.

Change a chart

PATCH /v1/charts/{id} switches the type, the theme, the title, the subtitle or the locale (en or es). It redraws from the same data, without searching again, and it's free. Pick the type from compatible_types. Without a title of your own, the title is rewritten for the new type and language.

curl -X PATCH https://api.typesearch.ai/v1/charts/chart_8k2m9q4x7w1p3n5z \
  -H "Authorization: Bearer $TYPESEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "area", "theme": "editorial" }'

It answers with the updated chart. The embed_url, image_url and svg_url stay the same.

List, get and delete

  • GET /v1/charts: your charts, newest first, limit at a time (up to 100). When has_more is true, pass next_before as before for the next page.
  • GET /v1/charts/{id}: one chart, with its data, plan and sources.
  • DELETE /v1/charts/{id}: deletes it ({ "id": "chart_…", "object": "chart", "deleted": true }). Its embed, image and SVG stop working.

Every key of your organization sees the same charts. Stored charts expire after 90 days on pay as you go and never on the monthly credit plan: expires_at says when, or null. See the reference.

See the plan first

plan_only: true returns only the plan: the recipe, the chart type, its alternatives and what it would search (plan.needs: queries, mode, days, minimum points). It doesn't search or draw, and it's free. chart and id come back null.

Without storing it

store: false stores nothing: there is no id and no URLs, and the SVG comes in the response, in svg. It costs the same. To get the SVG with a stored chart too, add include_svg: true.

Errors and warnings

StatuscodeWhen
400invalid_requestNeither query nor data, or both; a field out of range; a PATCH with nothing to change. errors lists each field.
402insufficient_credits, spend_limit_reachedNo credit left, or the key reached its monthly limit.
404chart_not_foundNo chart with that id for your organization: it never existed, it was deleted or it expired.
422insufficient_dataThe search didn't find enough data for this chart. Not billed.

insufficient_data says how many points it needed and how many it found. Try more days, another mode or a broader query:

422
{
  "type": "urn:typesearch:error:insufficient_data",
  "status": 422,
  "code": "insufficient_data",
  "detail": "Not enough data for this chart: it needs 3 and found 1. Try more days, another mode or a broader query. Not billed.",
  "request_id": "req_Vt4mQ8zK1pXa"
}

Warnings don't stop the chart. They come in warnings, each with a code and a message:

codeWhat happened
chart_type_adjustedThe data doesn't fit the type you asked for: it was drawn with the closest one that does.
groupedA long daily series was grouped by week, month or year.
indexedSeries on very different scales were compared as an index, base 100.

Pricing

A chart from a query costs its search, in the mode it needed and per query, plus $0.50 per 1,000 charts. With one query:

ModePer 1,000 chartsUsed by default for
ultra$1.50Coverage, share of voice, outlets.
fast$1.90Tone, timelines, figures, answers, custom index.
normal$2.70Figures, when headlines and standfirsts aren't enough.
deep$6.10Only when you set it.

A chart that compares names searches one query per name: share of voice between three names in ultra costs three searches at $1.00 per 1,000, plus one add-on. From your own data, a chart costs $0.30 per 1,000. usage.cost_usd says what each one cost.

Free:

  • Every view of an embed, image or SVG.
  • GET, list, PATCH and DELETE.
  • The same request within 10 minutes: the same chart comes back with cached: true. If only the search came from the cache, you pay just the add-on.
  • Failures, including 422 insufficient_data.
  • plan_only.

The list is in the OpenAPI document, in x-pricing.per_1000_charts: { "query_addon": 0.5, "from_data": 0.3 }. For reference, Tako's Search, which returns chart cards, lists $7 per 1,000. See Costs and caching.

From the MCP server

The MCP server's create_chart tool makes the same card from a query, so an agent can answer with a chart:

create_chart
{ "query": "Milei vs Bullrich vs Kicillof coverage", "theme": "dark" }

It takes query, type, theme, days, compare, countries, languages and index, and returns the title, subtitle, image_url (to show), embed_url, key_figures, the values drawn in data, events, sources and cost_usd. Same key, same prices.

On this page