Get European company financial statements in Python
A runnable Python example using a public dated sample or an authenticated REST request. Preserve nulls, currency, sources and annual reporting periods.
Use the public ASML sample to test the calculation without an account, then switch deliberately to the authenticated income-statement endpoint for current available records. These are company financial statements, not live stock prices. MCP remains the primary option for interactive agent research.
Jump to a section
Start with a public financial-statement sample
This example uses the same dated ASML EU-IFRS records as the margin walkthrough. It computes an operating margin, not a stock return or live quote. Python’s standard library is sufficient.
Download the sample JSON and Python script. The public sample requires no account and consumes no account allowance. Its amounts are in full EUR units; the display table elsewhere rounds to billions.
python3 european-financial-data.py --input european-financial-data-python-sample.json
The output identifies a dated sample, retains reporting periods and source labels, and calculates approximately 36.9% for 2025. Missing or zero revenue produces an unavailable margin, not a division error or invented zero.
The complete calculation
"""Read a dated public sample or explicitly request available live statements.
No third-party Python packages are required. Store real account keys privately.
"""
import argparse
import json
import os
from decimal import Decimal
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
parser = argparse.ArgumentParser()
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--input", type=Path, help="Downloaded public sample JSON")
mode.add_argument("--live", action="store_true", help="Use account allowance")
args = parser.parse_args()
if args.live:
key = os.environ.get("EUROFINDATA_API_KEY")
if not key:
raise SystemExit("Set EUROFINDATA_API_KEY privately before using --live.")
url = ("https://api.eurofindata.com/v1/financials/income-statements"
"?ticker=ASML&period=annual&limit=5")
request = Request(url, headers={"Authorization": "Bearer " + key})
try:
with urlopen(request, timeout=30) as response:
payload = json.load(response)
except HTTPError as error:
raise SystemExit(f"Data request failed: HTTP {error.code}. Check access and allowance.")
except URLError:
raise SystemExit("Data request failed. Check connectivity and try later.")
rows = payload.get("income_statements", [])
print("LIVE REQUEST: verify source freshness, accounting basis and report versions.")
else:
payload = json.loads(args.input.read_text(encoding="utf-8"))
if payload.get("is_live") is not False or payload.get("issuer") != "ASML":
raise SystemExit("Expected the dated ASML public sample.")
rows = payload.get("records", [])
print("DATED SAMPLE:", payload["sample_as_of"], payload["accounting_basis"])
if not isinstance(rows, list) or not rows:
raise SystemExit("No statements returned. An empty result is not zero revenue.")
seen = set()
for row in sorted(rows, key=lambda value: value.get("report_period", value.get("period_end", ""))):
period = row.get("report_period", row.get("period_end"))
currency = row.get("currency")
identity = (period, currency)
if not period or not currency or identity in seen:
raise SystemExit("Missing period/currency or duplicate period. Select a report version first.")
seen.add(identity)
revenue, income = row.get("revenue"), row.get("operating_income")
margin = None
if revenue is not None and income is not None and Decimal(str(revenue)) != 0:
margin = Decimal(str(income)) / Decimal(str(revenue)) * 100
display = "not available" if margin is None else f"{margin:.1f}%"
print(period, currency, "revenue=", revenue, "operating_income=", income,
"margin=", display, "source=", row.get("source", "not supplied"))
The script refuses duplicate period/currency records so you can choose a report version deliberately. That is a basic guard, not a complete point-in-time or accounting-comparability check.
Switch deliberately to an authenticated request
For current available records, create an account key and set EUROFINDATA_API_KEY privately in your environment. Run the script with --live. This makes an authenticated request to the annual income-statement endpoint and uses the account’s shared allowance.
python3 european-financial-data.py --live
The REST response uses income_statements and report_period; the curated sample uses records and period_end. The script handles both intentionally. A new live response can have different periods, values or gaps from the static sample.
Read the endpoint reference and freshness guide. A returned statement still needs its source and accounting-basis checks. The code has been tested with the public sample and fixture responses; this page does not claim a fresh customer-authenticated live run.
For an interactive question, use MCP
A script is useful for an explicit calculation or application. If you want to ask follow-up questions in a compatible agent, start with MCP setup and let the client discover the available tools. Both interfaces share account usage and underlying coverage.
The same rule applies either way: preserve periods, units, currency and source, and distinguish a missing observation from a zero value.
Take the numbers with you.
Dated sample · 4 October 2026 · Public download · No account required
EU-IFRS/ESEF; not the US GAAP series. Amounts use full currency units; CSV empty numeric cells mean missing values, not zero. These downloads use the same stored inputs as the article.