# Changelog of German Company KYC 🇩🇪 $5/1k: Handelsregister, HRB, Officers (`tagadanar/german-company-kyc`) Actor

- **URL**: https://apify.com/tagadanar/german-company-kyc/changelog.md
- **Full Actor documentation**: https://apify.com/tagadanar/german-company-kyc.md

## Changelog

### 2026-09-19

- **A different company is never billed as a match.** When the only register
  entry a search returned did not resemble the name you gave (for example
  "Henkel AG" answered with an angling club named Henkel), the row used to ship
  as `found: true` flagged `nameMismatch`, and was charged. It is now answered as
  not found, free, with the register's entry attached as `candidate`. Next to a
  genuine match, such look-alike entries are simply no longer delivered.

### 2026-09-08

- **A run can no longer spend more time than it has.** Every request to the
  Registerportal booked a flat 60-second timeout with nothing above it counting
  the clock: one company walks up to five search submissions (two requests
  each) and up to ten structured-document downloads, and a failed lookup
  replayed the whole thing on a fresh session — about 3,800 seconds of worst
  case for a SINGLE company against the 3600-second default run timeout, over a
  company list with no upper bound. One slow company could therefore end the run
  by platform kill, which delivers no status message at all.
- Every request now plans against the time actually left, politeness delays are
  never slept past the budget, and no company is started that the run cannot
  pay for. Companies already looked up stay delivered.
- **A company the clock never reached is now its own answer.** It is no longer
  reported as "not in the Handelsregister" (a claim only a completed search
  earns) nor as a portal failure: the run says plainly that the result is
  PARTIAL and names the queries it never asked.
- The structured-document (SI) download is now the part that loses its time
  first. Near the end of the budget a company still ships with its register
  facts — number, court, status, seat, former names — and the status says the
  officers and capital were skipped for time, not missing from the register.
- A portal response that is not a rate limit and not a server error (a 403, a
  404\) now leaves the retry ladder immediately instead of buying the same
  answer three times at the expense of the companies further down the list.

### 2026-08-30

- **Price cut: $0.15 -> $0.005 per company extracted ($150 -> $5 per 1,000).**
  The run start fee stays $0.001, not-found queries stay free, and Bronze /
  Silver / Gold now take 10 / 20 / 30% off the per-company price.
- **New `exactMatch` input.** Off (the default) keeps today's behaviour: the
  portal's keyword containment, so "Zalando SE" also returns Zalando BTD 003
  GmbH and Connected Retail GmbH. On, the actor uses the register's own
  exact-name mode (*genaue Firmenbezeichnung*), which returns only entries
  registered under exactly that name. Verified live on 2026-08-30: "Zalando SE"
  returns 1 row in exact mode against 25 in containment mode, and the bare word
  "Zalando" returns 0 — the register really is matching the whole registered
  name, so leave it off when you are unsure of the spelling.
- Every run now ends with a status message built by a pure function from
  explicit counters. A portal that never opened, a run that gave up after
  repeated portal failures, and a set of queries the register genuinely has no
  entry for are now three different messages instead of one: "none of these
  queries matched a register entry" is reachable only when the portal actually
  answered. Not-found queries, failed lookups, rows delivered without the
  structured document and rows flagged `nameMismatch` are each counted and named.
- README: the pricing table now carries the real numbers, and `exactMatch`,
  `includeDeleted` and `pushNotFoundRecords` are documented for the first time.

### 2026-07-17 v0.1.0

- Initial release. Per-company lookups on the Registerportal der Länder
  (handelsregister.de, free tier, no account): JSF session dance (JSESSIONID +
  ViewState) isolated in one client module, "Normale Suche" form search,
  free structured register content (XJustiz "SI" XML) download and parse.
- Record: legal name, register type/number/court, EUID, status, legal form,
  seat, address, phone, business purpose, share capital, founding and articles
  dates, representation rule, officers with roles (Geschäftsführer, Vorstand,
  Prokura), former names, entry counts. Unknown XJustiz role codes are
  delivered raw, never dropped.
- Search robustness: register-number + court pinning, exact-phrase then fuzzy
  name attempts, legal-form expansion and stripping ("AG" vs
  "Aktiengesellschaft"), result ranking with legal-form synonym normalization.
  A register-number hit that does not resemble the given name falls back to
  name search and any delivered doubt is flagged `nameMismatch` (KYC must
  never silently return the wrong company). Not-found rows are pushed
  uncharged.
- Resilience: sequential polite pacing (1.6 s), one session per run, one
  fresh-session retry per company, SI download retried once, run aborts after
  3 consecutive portal failures.
- Fleet conventions: list-input coercion with alias unwrapping, bad input
  exits SUCCEEDED with a status message, fail-closed billing (charge before
  push), actor-start floor charged before validation.
- Live-tested 2026-07-17: Zalando SE (HRB 158855 B, Charlottenburg, 10
  officers, share capital parsed), BioNTech SE resolved to HRB 48720 Mainz by
  name fallback from a deliberately wrong number (12 officers), fantasy
  company cleanly not-found, session-retry path exercised live. Peak RSS
  109 MB, memory 512 MB (3x measured, plain HTTP, no browser).
- Intended pricing (Console, at publish): `company-extract` $0.15 list with
  Bronze/Silver/Gold tiers (anchor: registry resellers charge EUR 4.50+ per
  document), `actor-start` $0.001 floor. No proxy input exists; the portal
  serves plain HTTP clients.
- Published 2026-07-17 (we pay platform usage, all-in pricing): `company-extract` $0.15, `actor-start` $0.001, Bronze/Silver/Gold discounts. Measured platform cost $0.34 per 1,000 extracts.
