How SaysData works

What SaysData does at each step between your AI’s question and the number — and the lists of what its checks refuse and warn about.

What SaysData does

The groundwork, not the answer.

Claude writes the answer, and whether it gets the numbers right is up to Claude. SaysData makes the official numbers easier to get, harder to get wrong and easier to interpret correctly.

Dataset discovery

Search thousands of official tables in plain language. Each result says what the table measures, where, over what period and how it’s broken down — so Claude can pick the right one, not just the first one.

Dataset profiling

Before a dataset can be queried, SaysData measures its file (units and scale, time format, missing and suppressed cells) and checks it against the agency’s published structure, its breakdowns and their members. Totals come from that structure where it marks them, and are otherwise recognised by their labels. A dataset whose file holds a breakdown or member that structure doesn’t declare, or that couldn’t be measured, is refused with a reason. Where a check has nothing to measure, the result says so.

Query guardrails

Queries with a defect from the Refused list below are refused before they run, with what to fix. Those with a risk from the Warned list run, and come back with a warning and the measured numbers.

Data from the primary source

Tables come straight from the publishing agency. The numbers, codes and flags stay as published; preparation only reads numbers as numbers and turns cells the agency marks as suppressed into nulls.

Interpretation metadata

Dataset descriptions and query results carry what it takes to read the numbers: units and scale, how missing cells read, the members recognised as totals, data-quality flags, warnings such as a total sitting beside its own parts, reading notes, the agency’s own footnotes, and the citation its licence asks for.

Query history and data dates

Every call is recorded with its exact arguments and outcome in your History, and each successful query’s run — the result as returned — opens at a private link, so you can see how an analysis was done. Running a query again reads each table as SaysData holds it then — only the current copy is kept, so the numbers can differ. Each dataset’s profile states the period the table covers, measured from the file; results carry the file’s own date where it has one, and each citation the date SaysData downloaded the file.

Guardrails

The checks run before Claude sees a number.

Two messages SaysData returned on 27 October 2026, word for word.

Refused Nothing runs. Claude gets the fix.

A query for Niger’s tax revenue that held every unit of measure the table publishes — rows in CFA francs beside rows with no currency, at two different scales:

The query was refused and nothing ran:
- slice "ner", UNIT_MULT [mixed-units]: slice ner holds 2 values in UNIT_MULT ("0", "6") and UNIT_MULT is neither pinned nor set to varying Fix: put UNIT_MULT in the slice's attributes: pin one value ({"values": ["0"]}) or set it to {"varying": true} and keep each value as its own series
- slice "ner", CURRENCY [mixed-units]: slice ner holds 2 values in CURRENCY ("XOF", null) and CURRENCY is neither pinned nor set to varying Fix: put CURRENCY in the slice's attributes: pin one value ({"values": ["XOF"]}) or set it to {"varying": true} and keep each value as its own series

A query for national currency only, with each scale and currency kept as its own series, ran.

  • A category the query selects by id that doesn’t exist in the dataset
  • A breakdown the query never says how to handle
  • Rows mixing the units, scales or currencies the dataset declares, without saying how to keep them apart
  • Rows or columns made up from typed-in numbers instead of read from the data
Warned It runs. The risk comes back measured.

A query for wholesale sales in Canada and every province and territory in August 2026 returned Canada beside its provinces — right for a table, wrong for a sum or a ranking:

slice ws, "GEO_ID": "Canada" (1) is held beside members it contains. Summed with them, those members count more than once; ranked with them, it ranks among its own parts.
  • A total summed with its own parts
  • A total the agency’s hierarchy places over its parts, or flags as an aggregate, returned beside them from a breakdown the query keeps whole, where adding up or ranking the rows would count it more than once
  • Selections with different numbers of periods joined, such as annual against quarterly
  • Suppressed or missing values a total or average silently skips
  • Series ending in different years compared as “latest”
  • Averages of rates or percentages, recognized by their unit codes
  • Selections in different declared units compared as like
  • Rows in different units, scales or currencies added up, averaged, ranked or picked from as one
  • A date range chosen for a query that some series don’t fill, such as “the 2000–2024 average” over a series that starts in 2005
  • A date filter in the query’s SQL written in a different period format than the data’s, such as a year bound on monthly data

Traceable

Every call behind an answer leaves a trail.

Your history keeps each call Claude made — the search, the dataset, the exact query — with its outcome, including anything refused or warned. For each successful query it also keeps the run — the result as Claude received it — at a link that opens only for you. Each result’s citation gives the date SaysData downloaded the table, so you can tell whether the agency has published since.

Request history for the calls behind the refusal example
ToolDatasetOutcomeDetails
execute_sqlRevenue Statistics in Africa - Comparative tax and non-tax revenuesAnswered
execute_sqlRevenue Statistics in Africa - Comparative tax and non-tax revenuesRefused — failed a query checkRefusal: mixed-units

The calls behind the refusal example above.

As the agency published it

The numbers, codes and flags are the agency’s own. Preparation does only two things:

  • Reads a column as numbers when every value in it is a plain number. Dates, periods and codes stay as written, and an empty cell, or one that says “NaN”, becomes null.
  • Turns a cell the agency flags as suppressed or not available into null, and keeps the flag.

Codes stay as the agency wrote them; their labels come with the dataset’s profile.

When a query reads a table, rows with no date and no value — placeholders some agencies publish for series without observations — are left out. If such a row ever carried a value, the result warns.

Sign up, add the connector, and ask your first question.

Connect SaysData to Claude