registercheckby openlaw group
Guides

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 results

That 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 recordSkip the call when
counts.financial_statements0 — nothing has been filed
counts.documents0 — no document is on file to download
counts.current_roles0 — 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.

On this page