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}",
"management": f"/companies/{company_id}/officer-contacts",
"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.
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_officers | 0 — there is no management 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_document_date next to shareholdings, fiscal_year next to financial
figures, and last_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.
Take the status from search. The status field on the company record is currently
unreliable — see Companies. Until that is fixed, read
is_active from the search result you used to find the company.
Keeping it current
Do not re-run the report on a schedule. Create a monitoring subscription and rebuild only when an event says something moved.