Why Most Fundamentals APIs Miss Foreign Filers

Your feed covers the S&P 500 perfectly and every spot check passes. Then someone asks for ASML or TSMC and the fundamentals come back empty — not because the data is missing, but because 3 pipeline shortcuts quietly drew the coverage boundary right through the foreign universe.

In this article, you will learn:

  • Which 3 pipeline shortcuts cause most fundamentals API foreign coverage to silently end at the US border
  • Why partial foreign coverage — annual but no interim — is worse than no coverage at all
  • How to audit your own feed for the gap with a simple probe across a basket of foreign names
  • How to get 20-F, 40-F and 6-K coverage by ticker in one normalized schema — with a free API

You wire up a fundamentals feed, it looks complete, and then an entire class of companies turns up empty. The European industrials. The Asian chipmakers. The Canadian pipelines.

It is very tempting at this point to assume the data does not exist. It does. What actually happened is that your feed took a handful of shortcuts that happen to exclude precisely these companies — and I want to be fair here, because every one of those shortcuts is a rational cost-saving decision. They just quietly draw the coverage boundary straight through the foreign universe. Understanding them tells you 2 useful things: why this gap is so common, and what closing it actually costs.

The looks-complete trap

Here is the part that should genuinely worry you: this gap is invisible until you go looking for one specific name. A feed can be 100 percent complete on the S&P 500 and 0 percent complete on foreign issuers, and every dashboard, every spot check, every demo will look perfect — because nobody demos with TSMC. You discover the hole the day a portfolio manager asks why the model has no view on ASML, or the day a screen silently drops every non-US name because they have no trailing-twelve-month revenue. By then the feed is load-bearing, and the gap is a production incident rather than a design choice. So it is worth naming the shortcuts precisely.

The first shortcut is US-GAAP XBRL only

The cheapest way to build fundamentals coverage is to consume the SEC’s structured XBRL for 10-K and 10-Q filings, tagged in the us-gaap taxonomy. It is structured, it is bulk-available, and a US-only universe is fully covered by it with almost no parsing.

But foreign private issuers do not file 10-Ks. They file 20-F annual reports, and Canadian cross-listed issuers file 40-F reports under the Multi-Jurisdictional Disclosure System. When those carry XBRL at all, it is frequently tagged under IFRS — the ifrs-full taxonomy, a different vocabulary with different concepts, not just different names. A pipeline whose concept map only knows us-gaap cannot place an ifrs-full concept, so the foreign annual either lands nowhere or is skipped outright. Covering it means building and maintaining a second concept map, which most feeds decline to do.

The second shortcut is skipping non-XBRL filings

The second shortcut is to treat XBRL as the only input. That is fine for domestic annuals and quarters — and fatal for foreign interims, because the 6-K, the filing foreign issuers use for interim results, carries no XBRL at all. It is furnished rather than filed, it has no fixed item structure, and its financials are HTML tables — the same positional-parsing problem as an 8-K earnings exhibit, with more currencies and more layout variety.

So a foreign company that a feed does manage to cover annually still has no interim data, because the quarters live in a filing the pipeline was built never to read. Between the 2 shortcuts, foreign filers lose their annuals to the wrong taxonomy and their interims to the missing parser.

A third, quieter shortcut is trusting bulk datasets

There is a subtler version of the first 2. Many feeds are built not on raw filings at all but on the SEC’s pre-packaged bulk products — the company-facts JSON and the financial-statement data sets — because they are convenient and free. Those products were built around domestic reporting. Their IFRS coverage is thinner and later than their US-GAAP coverage, and they do not cover the 6-K, because the 6-K has no structured data to package. A feed built on the bulk products inherits the bulk products’ boundary, which runs right through the foreign universe, without ever making an explicit decision to exclude foreign names.

The tempting patch is to fill the resulting holes with third-party aggregated or estimated figures rather than the primary filing. That closes the visible gap and opens an invisible one: the filled numbers do not reconcile against the actual 20-F or 6-K, they lag the source, and they carry no reporting currency you can trust, so any cross-border comparison built on them drifts quietly away from the filings. A gap you can see is safer than a gap you have papered over.

Why partial coverage is worse than none

So those are the 3 shortcuts, and together they drop the 20-F annual, the 40-F annual and the 6-K interim — which is everything a foreign company actually files. But the damage is not just missing data. It is the shape of what remains.

I want to argue something that sounds backwards. An empty cell is honest — you can see it, and you know to handle it. A half-covered foreign name is not, and it does far more damage. Consider the common case where a feed manages the 20-F annual but misses the 6-K interims. Now the company has annual revenue but no quarters. Any trailing-twelve-month figure you compute is missing 3/4 of its inputs, so it either comes out as the last annual number frozen in place or as a nonsense partial sum. Any year-over-year growth on a quarterly basis has nothing to compare. A screen that ranks on TTM margins silently mis-ranks every foreign name, and a backtest quietly treats stale annual data as if it were current.

None of this throws an error. The number is present, plausible, and wrong — which is the most expensive failure mode there is, because you trust it. This is why “we have foreign coverage” is not a yes-or-no question. The right question is whether both the annual and the interim come back, with a reporting currency attached, for the names you actually hold.

The gap is structural, which is why it persists

If the gap were just missing code, somebody would have patched it years ago. The reason it persists is that closing it is not a one-time fix — it is a permanent cost increase.

I want to be fair to the feeds here, because none of this is laziness. Each shortcut avoids a real cost, and a permanent one. An IFRS concept map is a whole second normalization surface, and it is not written once: the IFRS Foundation revises ifrs-full every year and the FASB revises us-gaap every year, so you are maintaining 2 mappings on 2 release cycles indefinitely. A non-XBRL parser is a months-long build that never stops needing attention, because every issuer lays out its 6-K tables differently and redesigns them over time. Closing the foreign gap means paying both costs, and paying them forever. That is why the gap is so common and so durable: it is expensive to close and, thanks to the looks-complete trap, invisible until you go looking for a specific company.

Audit your current feed for the gap

Before you decide whether any of this matters to you, go and measure it. Do not take my word for it, and do not take your vendor’s. The test is simple and I would run it against any feed, including one you already pay for: probe a basket of foreign names for both frequencies, and check that the annual series, the interim series and the reporting currency all come back populated. A feed with this gap will fail on the interim, the currency, or both.

import requests

API_KEY  = "YOUR_API_KEY"
BASE_URL = "https://data.businessquant.com"

def coverage_probe(ticker):
    out = {}
    for freq in ("Annual", "Quarter"):        # annual from 20-F/40-F, interim from 6-K
        url = (f"{BASE_URL}/statements"
               f"?ticker={ticker}&statement=IS"
               f"&frequency={freq}&period=5y&api_key={API_KEY}")
        r = requests.get(url).json()
        dates = []
        for category in r["data"].values():
            for section in category["sections"].values():
                if section["metadata"]["slug"] == "revenue":
                    dates = [v["normalizedDate"] for v in section["values"]]
        out[freq] = {"currency": r["metadata"]["currency"], "periods": dates[:3]}
    return out

# A basket spanning taxonomies, currencies and filing forms.
basket = ["TSM", "ASML", "TM", "CNI", "ENB", "NVO", "SAP"]
for t in basket:                              # Taiwan, NL, Japan, Canada x2, Denmark, Germany
    p = coverage_probe(t)
    annual_ok  = bool(p["Annual"]["periods"])
    interim_ok = bool(p["Quarter"]["periods"])
    print(f"{t:5} annual={annual_ok!s:5} interim={interim_ok!s:5} "
          f"currency={p['Annual']['currency']}")

Run the same probe against whatever you use today. The names that come back annual-only, or with no currency, are the ones silently corrupting every TTM, growth and cross-sectional calculation downstream — and now you have a list instead of a surprise.

Get the coverage the shortcuts drop

Now you have a list of names your current feed misses, or half-covers. The next question is what closing that gap actually looks like.

The Business Quant Financial Statements API pays both costs on the ingestion side: it maps IFRS and US-GAAP into one standard line-item set, and it parses the non-XBRL 6-K. So the 20-F, 40-F and 6-K — the 3 filings the shortcuts skip — come back by ticker in the same schema as a domestic 10-K, with both the annual and the interim populated and the reporting currency attached. In the probe above, that is the difference between a row that reads annual=True interim=True currency=EUR and one that reads annual=True interim=False currency=None.

If you would rather see the raw materials the shortcuts skip, the SEC suite reaches them too: the Section Extraction API returns the 20-F financial-statement section as clean HTML by item key, and the Documents and Exhibits API returns the 40-F exhibit and the 6-K interim by document type — the same source documents, before normalization, with inline XBRL already stripped. The gap other feeds leave is a choice not to parse these filings, not an absence of data.

Edge cases to keep in mind

The normalized data and the raw filings cover the main path. A few details are worth keeping in mind as you integrate.

Coverage is per filing, so completeness follows what a company files. An issuer that reports interims semi-annually will show that cadence; the series reflects the filings, not a fixed grid. That is honest coverage, not a hole — but it means your audit should distinguish “semi-annual by convention” from “missing.”

Always read the currency. Foreign filers report in their home currency, and it cannot be inferred from the country. The metadata.currency field is the guardrail against silently mixing currencies in a comparison.

Identify by US ticker. Query foreign issuers and Canadian filers by their US-listed ticker; no CIK required.

Start Building

So, to put it all in one place. Foreign filers go missing from most fundamentals APIs because of 3 pipeline shortcuts — US-GAAP-only XBRL, no non-XBRL parser, and reliance on bulk datasets that were never complete abroad. Together those drop the 20-F, the 40-F and the 6-K, which is to say: everything foreign companies actually report in.

2 things I would leave you with. The gap stays invisible until you deliberately go looking for it, so go look. And partial coverage is genuinely worse than none, because you cannot see it failing.

Closing it properly means a second concept map plus a non-XBRL parser, and both are permanent costs rather than projects. Once somebody has paid them, foreign coverage stops being a special case and becomes just another ticker.

Get an API key, run the audit probe on a basket of foreign names, and stop treating foreign filers as a gap.

Frequently asked questions

Why do most fundamentals APIs miss foreign companies?

Pipeline shortcuts. Ingesting only US-GAAP XBRL skips the IFRS-tagged 20-F and 40-F annual reports, never building a non-XBRL parser skips the 6-K interim filings entirely, and relying on the SEC bulk datasets inherits their thin foreign coverage. Foreign filers report in exactly those filings, so they fall through every gap.

Which foreign filings get skipped?

The 20-F annual report for foreign private issuers, the 40-F for Canadian cross-listed issuers under the MJDS, and the 6-K interim update. The first 2 are often IFRS-tagged rather than US-GAAP, and the 6-K carries no XBRL at all.

Why is partial foreign coverage worse than no coverage?

An empty cell is obvious and you handle it; a foreign name with annual data but no interims silently corrupts every trailing-twelve-month, growth and cross-sectional calculation, because the number is present and plausible but wrong. The failure throws no error, so you trust it, which makes it the most expensive kind.

How do I audit my feed for the foreign gap?

Probe a basket of foreign names by ticker for both Annual and Quarter frequencies, and check that revenue periods come back for each along with the reporting currency. Names that return annual-only, or with no currency, are the ones corrupting downstream calculations. Populated annual and interim series with a currency are the coverage a shortcut-built feed cannot provide.

What does it take to close the foreign-coverage gap?

A second concept map so IFRS concepts map to the same standard line items as US-GAAP, maintained on the yearly IFRS and US-GAAP taxonomy release cycles, plus a non-XBRL parser for the 6-K interims that survives per-issuer layout drift. Both are permanent maintenance costs, which is why the gap is common and persistent.

Is the Financial Statements API free to use?

Yes. Sign up for a free API key and foreign filers, including Canadian 40-F issuers, read by ticker in the same normalized schema as US companies, with both annual and interim series and the reporting currency.