Verifiability model.
Numbers come from typed code; language comes from a model. The two layers never cross, and every figure traces back to the EDINET document it came from.
The two layers
A financial-data API has to do two very different jobs: read numbers out of filings, and make Japanese text legible in English. Conflating them is how data products quietly ship hallucinated figures. Axiora keeps them apart by construction.
Typed XBRL → typed code. No model.
Every figure is lifted directly from a schema-validated XBRL element by deterministic code. Models are not in the path.
Translations and names are LLM-assisted.
JA→EN narrative and naturalised entity names go through a model. The Japanese original is always preserved alongside.
Numbers are deterministic
Revenue, net income, EPS, stake percentages, vote counts, every number comes from a typed XBRL element identified by its unitRef and context, parsed by deterministic code. There is no model in the path and no inference step. The XBRL document is the source of truth; the database is a cache of what the parser read.
- A blank cell is never silently a zero. For financial fields, a null is explained, e.g. a field like
ordinary_income(経常利益) is structurally null under IFRS, which has no such concept. - Monetary values are integer JPY; per-share fields reflect raw filings unless the response is explicitly split-adjusted.
- Replay the same query with an
as_oftoken and you get the same bytes.
Language is LLM-assisted
Translations of narrative sections and naturalisation of entity names (e.g. a reporter name rendered in English) are LLM-assisted. This is the one place a model touches the data, and it never touches a number. The Japanese source is preserved on the row (holder_raw, narrative_ja) so you can audit the language layer yourself, and English names carry a name_en_quality tier (verified / derived / machine) so you know how much to trust them.
Classification is revisable
Event labels, the trajectory_type a shareholding reads as (accumulating, exiting, stable, or new_position), are a separate labelling layer applied on top of the numbers.
It is a revisable labelling layer that may use models, and the classifier is moving to an LLM. Labels can change between ontology versions; pin as_of if you need a stable label. The underlying figures never change, and every label keeps the doc_id it was derived from.
Freeze tokens
Every response can be pinned. Capture meta.as_of and send it back as ?as_of=<token> and you get the exact same rows (bit-identical) even if EDINET later restates the filing, the schema changes upstream, or the ontology evolves. If two responses share a token, their bytes are identical; the SHA-256of the body is part of the token’s contract.
This is what makes a number citable in research: a frozen query is reproducible months later. The quickstart walks through capturing and replaying one.
doc_id on every row
Wherever a row maps to a single originating filing it carries that EDINET doc_id, which resolves at disclosure2.edinet-fsa.go.jp. Some rows aggregate across multiple filings. In those cases the per-row doc_id may be null, and meta.data_as_of records source freshness instead. You can always trace a number back to a public document.