@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:
| Regime | Filings | Countries |
|---|---|---|
ESEF | ~15,996 | AT BE CY CZ DK ES FI FR GB GR IS IT LT NL NO PL PT RO SE |
UAIFRS | ~9,644 | UA |
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:
- 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. Onlypage[size]/page[number]are bracketed, and they go throughbuildUrl(), which usesURLSearchParams(which percent-encodes brackets). verifyFilters()re-checks the returned rows against what was asked for. Everyesef_search_filingsresponse carriesfilters_applied,filters_verifiedandfilter_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, headerAccept: application/vnd.api+json) - Entities:
https://filings.xbrl.org/api/entities,…/api/entities/<identifier>/filings - Reports: root-relative
json_urlresolved againsthttps://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