Build a company report
Assembling the register entry, management, ownership and financials into one view.
A full report is four calls on top of the company record. Fire them concurrently — they do not depend on each other.
import os, requests
from concurrent.futures import ThreadPoolExecutor
BASE = "https://api.registercheck.de/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['REGISTERCHECK_API_KEY']}"
def report(company_id: str) -> dict:
parts = {
"company": f"/companies/{company_id}",
"roles": f"/companies/{company_id}/roles",
"shareholders": f"/companies/{company_id}/shareholders",
"history": f"/companies/{company_id}/events",
}
def get(path):
r = session.get(BASE + path, timeout=60)
return r.json() if r.ok else None
with ThreadPoolExecutor(max_workers=4) as pool:
results = dict(zip(parts, pool.map(get, parts.values())))
company = results["company"] or {}
# Financials cost 10 credits whether or not anything is filed - check first.
if (company.get("counts") or {}).get("financial_statements"):
results["financials"] = get(f"/companies/{company_id}/financial-statements")
return resultsThat is 40 credits, or 50 with financials.
GET /companies/{company_id}/kyb-report is the same register entry, current roles,
shareholders and ownership tree to depth 3 in one response, for 50 credits. It does not
include the event history or the financial statements, and it returns document references
rather than the files. A next_cursor on its roles or shareholders page is read from those
lists. The report is computed for that request. It is not an extract from the
Transparenzregister, and it is no substitute for your own due diligence.
Cost control
Check before you fetch. The company record already tells you whether there is anything to find:
| Field on the company record | Skip the call when |
|---|---|
counts.financial_statements | 0 — nothing has been filed |
counts.documents | 0 — no document is on file to download |
counts.current_roles | 0 — there is no current role holder to list |
That second row is worth taking: representation on the company record covers most of
what /management/contact returns, and the record costs 10 credits against the contact
endpoint's 25.
Presenting it honestly
Three things will make your report defensible, and their absence will make it wrong:
Date every claim
Show source.date next to shareholdings, period.fiscal_year next to financial
figures, and last_register_entry_date next to the register entry. A cap table with no as-of
date is a claim you cannot support when challenged.
Distinguish "no data" from "no such thing". An empty shareholders array on an AG
means AGs do not file shareholder lists. The same empty array on a GmbH whose address
and purpose are also null means no extract has been parsed. Render those two differently
or your users will draw the wrong conclusion.
Show liquidation as its own state. A company in liquidation still has a live register
entry, but it is being wound up; folding it into active hides exactly what a counterparty
check looks for. unknown means the register data does not say, not "inactive" — see
Companies.
Keeping it current
Do not re-run the report on a schedule. Create a monitor and rebuild only when an event says something moved.