@pipeworx/esef-filings

Connect: https://gateway.pipeworx.io/esef-filings/mcp · Install: one-click buttons

Tools: 3

XBRL filings index MCP — published company annual reports from the filings.xbrl.org index run by XBRL International, plus the IFRS financial facts inside each one. Keyless.

The European counterpart to sec-xbrl: same idea (accounting facts straight out of a regulator’s XBRL), different filing regime.

Tools

  • esef_search_filings(entity_name?, country?, regime?, year?, period_end?, with_errors?, sort?, limit?, page?) — search the index. Returns company name, LEI or national identifier, country, regime, period end, XBRL validation error/warning counts, report language and links to the xBRL-JSON, HTML report, viewer and package.
  • esef_entity_filings(entity, limit?) — every filing for one company, resolving a name, LEI or national registration number. Returns what it resolved to and how, plus filings grouped into distinct reports (see “Language editions” below).
  • esef_filing_facts(fxo_id? | entity?, year?, concept?, include_dimensioned?, limit?) — the second hop. Opens the filing’s xBRL-JSON report and returns named IFRS facts with value, currency, period and concept.

esef_search_filings and esef_entity_filings prove a filing exists; only esef_filing_facts returns money.

Scope

25,640 filings under exactly two reporting regimes:

RegimeFilingsCountries
ESEF~15,996AT BE CY CZ DK ES FI FR GB GR IS IT LT NL NO PL PT RO SE
UAIFRS~9,644UA

The pack name says ESEF; the source is broader than ESEF. Both country and regime are first-class filters so a caller can pin the scope they meant. See GOTCHA 2 below — this is not a cosmetic detail.

Auth

None. No key, no account, no rate-limit documentation published.

filings.xbrl.org is a nonprofit index, so the pack is deliberately quiet with it: one index request per call, at most one report fetch, no probing or fan-out, and a real contactable User-Agent (Pipeworx/1.0 (+https://pipeworx.io; [email protected])).

Gotchas

GOTCHA 1 — a wrong filter param is silently ignored, not rejected

This is the dangerous one and it is why filter construction lives in exactly one helper.

The API takes JSON:API filters in two forms:

?filter%5Bcountry%5D=FI     bracketed shortcut, percent-encoded  -> meta.count 1168, Finnish records
?country=FI                 bracketed form written WITHOUT the brackets
                            -> HTTP 200, meta.count 25640, first record Ukrainian

The second is the entire unfiltered index wearing a successful filtered query’s clothes. Same status code, same envelope, same field names — nothing to catch.

Two defences, both in src/index.ts:

  1. The pack never uses the bracketed shortcut for filters. It uses the flask-rest-jsonapi complex form, ?filter=[{"name":"country","op":"eq","val":"FI"}] — a single unbracketed param, so there is no bracket encoding to get wrong, and a bad attribute name is rejected loudly (HTTP 400 "FilingSchema has no attribute bogus") instead of ignored. Only page[size] / page[number] are bracketed, and they go through buildUrl(), which uses URLSearchParams (which percent-encodes brackets).
  2. verifyFilters() re-checks the returned rows against what was asked for. Every esef_search_filings response carries filters_applied, filters_verified and filter_mismatches, so a filter that somehow failed to bite shows up in the payload rather than quietly widening the answer.

GOTCHA 2 — this index is not ESEF-only, and saying otherwise is a wrong answer

An unfiltered probe’s first record is Ukrainian: EDRPOU-32033791-2020-12-31-UAIFRS-UA-0. 38% of the index is UAIFRS. Describing the pack as “European filings” while it can return Ukraine is the resolver-grain trap — the caller gets a confident answer at the wrong grain.

So: every tool description names both regimes out loud, regime is a filter, every returned row states its own country and regime, and search responses carry a scope_note. If you edit a description, keep the scope sentence in it.

GOTCHA 3 — the same annual report is indexed once per language edition

Citycon’s FY2022 appears twice — …-ESEF-FI-1 (Finnish) and …-ESEF-FI-0 (English) — identical figures, different fxo_id. Counting index rows as reports inflates a company’s filing history: Citycon has 11 filings but 6 distinct financial years.

esef_entity_filings therefore returns both filing_count (index rows) and report_count (distinct years), and groups editions under a preferred_edition — English when available, since the narrative facts are then readable. esef_filing_facts picks the English edition when resolving from a company name.

Note this is not what the language dimension inside a report does. Each xBRL-JSON document is single-language; the language variance is one level up, across filings.

GOTCHA 4 — one figure is tagged many times inside a report

Citycon’s FY2022 ifrs-full:ProfitLoss of EUR 5,100,000 appears three times with byte-identical dimensions (primary statement, notes, equity reconciliation), plus further copies broken down by ComponentsOfEquityAxis. Returned naively that is one profit figure looking like six different ones.

esef_filing_facts groups on every dimension except language, collapses identical values, and reports occurrences (how many taggings backed the value) and dedup.repeat_taggings_collapsed. Axis-dimensioned breakdowns are excluded by default (include_dimensioned: false) and counted in dedup.dimensioned_facts_excluded. Genuinely contradictory values for one dimension set surface in conflicting_values rather than being silently picked between.

GOTCHA 5 — json_url can be null

About 1.5% of index rows have no machine-readable report, and in the sampled cases report_url and viewer_url were null too — the row is metadata only (e.g. Cloetta AB 549300CSLHPO6Y1AZN37-2021-12-31-ESEF-SE-1, which has error_count: 1 and only a package zip). esef_filing_facts returns {found: false, reason: 'no_machine_readable_report'} naming whatever URL did survive, instead of throwing.

GOTCHA 6 — entities are addressed by identifier, not by id

A JSON:API entity record carries both id: "1597" and attributes.identifier: "549300P8N0P6KDGTJ206". Only the identifier is addressable: /api/entities/1597 returns 404.

Worse, the identifier is not always the fxo_id prefix. Ukrainian filings use EDRPOU-32033791-… in the fxo_id but are addressed as plain 32033791. Joining filings to entity names on the fxo_id prefix left every Ukrainian filing with entity_name: null; the pack joins on the tail of relationships.entity.links.related instead.

GOTCHA 7 — xBRL instants are stamped one day late

An xBRL-JSON instant period of 2023-01-01T00:00:00 is the 2022-12-31 balance sheet — the instant is the start of the following day. Reading the raw string is a full year of error. describePeriod() normalises both forms, so period_end and period_label (“as at 2022-12-31”, “2022-01-01 to 2022-12-31”) are already corrected.

GOTCHA 8 — narrative notes are tagged as facts

DisclosureOfShareCapitalReservesAndOtherEquityInterestExplanatory in Citycon’s FY2022 report is 2,500 characters of prose. Text values are clipped at 600 characters with value_truncated / value_length set, and within a period measured figures sort ahead of narrative, so concept: "Equity" leads with the EUR 2,310,300,000 balance rather than pages of note text.

Data sources

  • Index: https://filings.xbrl.org/api/filings (JSON:API, header Accept: application/vnd.api+json)
  • Entities: https://filings.xbrl.org/api/entities, …/api/entities/<identifier>/filings
  • Reports: root-relative json_url resolved against https://filings.xbrl.org — xBRL-JSON (OIM), {documentInfo, facts}, typically 500 KB–1 MB

Tools

  • esef_search_filings — Search the XBRL International filings index (filings.xbrl.org) for published company annual reports. Answers “which European companies have filed an annual report for 2023”, “does Nokia have an ESEF f
  • esef_entity_filings — List every annual report one company has published to the XBRL International filings index, resolving a company name, LEI, or national registration number to the filer. Answers “what years has Citycon
  • esef_filing_facts — Read the actual IFRS financial facts out of one published annual report — revenue, profit or loss, total assets, equity, operating cash flow, earnings per share and every other tagged figure, with the

Tools

  • esef_entity_filings — List every annual report one company has published to the XBRL International filings index, resolving a company name, LEI, or national registration number to the filer. Answers what years has Citycon
  • esef_filing_facts — Read the actual IFRS financial facts out of one published annual report — revenue, profit or loss, total assets, equity, operating cash flow, earnings per share and every other tagged figure, with the
  • esef_search_filings — Search the XBRL International filings index (filings.xbrl.org) for published company annual reports. Answers which European companies have filed an annual report for 2023 , does Nokia have an ESEF fil

Regenerated from source · build August 4, 2026