Vermittlerregister Scraper - German Insurance Broker Lookup avatar

Vermittlerregister Scraper - German Insurance Broker Lookup

Pricing

from $4.00 / 1,000 per full register records

Go to Apify Store
Vermittlerregister Scraper - German Insurance Broker Lookup

Vermittlerregister Scraper - German Insurance Broker Lookup

Look up German insurance brokers, tied agents and financial-investment intermediaries in the statutory Vermittlerregister (DIHK). Turn a list of company names, people or registration numbers into full permit records: GewO paragraph, permit scope, named directors and the supervising IHK.

Pricing

from $4.00 / 1,000 per full register records

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

🇩🇪 Vermittlerregister Scraper — German insurance broker & intermediary lookup

Turn a list of names into statutory permit records. Feed this actor German company names, people (Surname, Firstname) or registration numbers, and it returns each one's entry in the Vermittlerregister — the public register the DIHK runs for Germany's 79 chambers of commerce under §§ 34d / 34f / 34h / 34i GewO.

Every row carries the layer no company-data source has: which permit the intermediary actually holds, decoded to its paragraph of the Gewerbeordnung, what that permit covers, the named authorised representatives and senior staff, and the licensing authority and IHK that supervise them.

⚠️ Read this before you buy: this is a batch lookup, not a bulk export

The register publishes no list. There is no listing route, no page parameter (a seite=2 query returns byte-identical bytes to page 1 — measured), and any query it considers too broad comes back TOO_MANY_HITS with zero rows. Even a full German name like Peter Müller is refused. The site says so in its own words, which this actor quotes back to you verbatim on every refused row:

„Ihre Suche hat zu viele Treffer ergeben. Bitte grenzen Sie die Suche weiter ein oder verwenden Sie eine Registrierungsnummer als Suchkriterium."

So: bring a list. This actor is built for verifying, enriching and monitoring intermediaries you can already name. It cannot and does not dump the register.


🎯 What you get per row

🆔Registration numberD-F-141-WS31-91, D-URTZ-05QX4-94, D-W-139-JLDM-77
📜Permit type, decoded„Versicherungsmakler mit Erlaubnis nach § 34d Abs. 1 GewO", „Gebundener Versicherungsvertreter nach § 34d Abs. 7 GewO", „Honorar-Finanzanlagenberater"
⚖️GewO paragraph as its own field — § 34d Abs. 1 / Abs. 2 / Abs. 6 / Abs. 7, § 34f, § 34h, § 34i, § 34c
🧾Permit scope — the three Erlaubnisumfang booleans (open-ended funds, closed-ended funds, other investments) plus their statutory wording
🏢Entity — company name, or the sole trader's full name with salutation, titles and affixes
📍Business address — street, house number, postcode, town, district
👤Named peoplevertretungsberechtigte (directors/board, with their function), leitendeAngestellte (§ 34d Abs. 10 GewO), unmittelbar Mitwirkende, managing partnerships
🏛️Licensing authority — the town or district office that issued the permit, with its address, and whether the permit is under review
🏦Supervising IHK — the chamber responsible for the entry, with its address and contact fields
🔗Cross-register links — the same intermediary's entries in the other sub-registers (optional; see the note under Repeat runs and the record cache)
📅letztesUpdate — the date the register itself published this data
🧭Provenance — the lookup that produced the row, whether it was narrowed, whether it is complete, and the register's own search URL

The four sub-registers, all searched in one request

CodeRegisterStatute
VVRVersicherungsvermittler — brokers, agents, tied agents, advisers§ 34d GewO
FAVFinanzanlagenvermittler & Honorar-Finanzanlagenberater§§ 34f / 34h GewO
WIKImmobiliardarlehensvermittler — mortgage-credit intermediaries§ 34i GewO
DVRDarlehensvermittler — loan intermediaries§ 34c GewO

A single lookup for Debeka comes back with the insurance record and the financial-investment record together, because the register answers all four sections in one call.


⌨️ Input

{
"lookups": [
"Debeka",
"Schmidt, Andreas",
"D-F-141-WS31-91"
],
"ort": "",
"registers": ["VVR", "FAV", "WIK", "DVR"],
"entityType": "ALL",
"resolvePreviews": true,
"maxPreviewsPerLookup": 5,
"autoNarrow": true,
"maxLookups": 25,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "DE" }
}

Each line of lookups is auto-detected:

  • D-F-141-WS31-91 → a registration number. Spaces and missing dashes are repaired (DF141WS3191 works). Cheapest and most exact: one request, one full record.
  • Schmidt, Andreas → a person. The comma matters — the register refuses a surname on its own, so a line without a comma is read as a company name rather than guessed at.
  • Debeka → a company name.

For anything more precise — a person narrowed to a town, a street-level search, or your own CRM id echoed back in the output — use advancedLookups:

{
"advancedLookups": [
{ "name": "Schmidt", "vorname": "Andreas", "ort": "Berlin", "label": "crm-4711" },
{ "unternehmensname": "Debeka", "plz": "56073" }
]
}

🧩 How this actor beats TOO_MANY_HITS

Narrowing is the whole mechanic, and it is measured, not theoretical:

QueryResult
name=Schmidt&vorname=AndreasVVR → TOO_MANY_HITS, zero rows
name=Schmidt&vorname=Andreas&ort=BerlinVVR → one complete record, D-E93Q-TDOYE-63

With autoNarrow on (the default), a refused sub-register query is retried against your own narrowCities list first, then against any town the other sub-registers already named for that same identity in the same response — so a refused insurance query gets answered from a town the financial-investment register happened to reveal.

And then it tells you the truth about it. A narrowed answer covers one town only, so every row from it is marked lookupComplete: false, the run is marked runComplete: false, and a free row spells out which town was searched and that intermediaries of that name elsewhere in Germany are not in your output.


💸 Pricing — and what is deliberately free

EventPriceFires
Per full register record$0.004Once for each complete statutory record delivered — whether it came back directly or was upgraded from a match
Per match without the full record$0.001Once per bare match (number + name + town) delivered, and only when resolvePreviews is off or the register would not return the full record

Never charged, always delivered:

  • NO_HITS — the register holds no such entry
  • TOO_MANY_HITS — the register refused the query, with its own narrowing instruction quoted
  • NARROWED — the refused query was answered for one town only, and which town
  • TRUNCATED — your maxPreviewsPerLookup cap dropped matches, listing the registration numbers it dropped so you can feed them back in
  • INVALID_INPUT — the lookup could not be searched, and why
  • CHARGE_CAP — your maxTotalChargeUsd stopped rows being delivered, saying how many were fetched-but-not-delivered and listing their registration numbers
  • rows your entityType or registers filter removed
  • duplicate registration numbers

A match that gets upgraded to a full record is billed once, as a record — never also as a match. Both of Apify's auto-events (apify-default-dataset-item and apify-actor-start) are removed, so your bill shows only the two events above.

If you set a maximum charge on the run

Apify's per-run maxTotalChargeUsd is honoured by reserving money, not rows, before every push, so nothing is ever delivered unpaid or charged undelivered. When the cap bites mid-answer the actor says so instead of looking finished:

  • the surviving rows of the truncated batch are marked lookupComplete: false — the rest of that lookup's answer was fetched and thrown away;
  • runComplete goes false, and RUN_SUMMARY carries chargeCapReached and rowsDroppedByChargeCap;
  • a free CHARGE_CAP row lists the registration numbers that were dropped;
  • the run's status message names maxTotalChargeUsd as the reason it is short.

🔐 How the register's gate is handled

vermittlerregister.info protects its search routes with FriendlyCaptcha — and it is important to be precise about what that is. It is not an interactive human challenge: there is no image grid, no checkbox, no "select all the traffic lights". The page downloads a puzzle and its own background workers grind a BLAKE2b proof-of-work; the visitor just watches a progress bar. This actor performs exactly that computation, reading the site's sitekey from the page at the start of every run and solving the puzzle against the same public issuer the page uses.

The register also rate-limits by IP, and raises the puzzle's difficulty against an egress that pushes too hard. This actor honours that instead of evading it:

  • one request in flight at a time, never parallel;
  • a hard floor of 1.2 s between requests (minRequestIntervalMs);
  • one sticky proxy session for the whole run — there is deliberately no IP rotation anywhere in this actor;
  • a puzzle is solved only when the register asks for one (its own 403), never speculatively;
  • if the difficulty climbs 6 points above where the run started, the actor slows to one request every 5 s;
  • above maxCaptchaDifficulty (185 by default) it stops the run cleanly and says so, rather than grinding through the penalty — and it reads the difficulty out of the puzzle before solving it, so refusing costs you no CPU at all.

proxyConfiguration is exposed so you can bring your own German egress if you need more throughput than one polite session gives you.

Measured proof-of-work cost (Apify RESIDENTIAL, country DE, 2026-09-07/08)

Difficulty seenWork per solveSolve time, one desktop core
139~8.2 M hashes2.1 s
155~30.6 M hashes8.2 s
166~84.7 M hashes23–25 s
170~112 M hashes40 s
194 (Apify's own datacenter egress — i.e. no proxy)~839 M hashes296.9 s
195 (a heavily hammered IP, not a normal egress)~1,050 M hashes~200 s

Use a German residential proxy. It is not decoration. Measured 2026-09-08: the same single-name lookup that takes 45 s through Apify RESIDENTIAL/DE took 592 s with no proxy, because the register served difficulty 194 and each of the two solves cost ~290 s of a full vCPU — $0.20 of compute for 8 rows. The default maxCaptchaDifficulty of 185 now refuses that egress before a single hash is computed.

One solve is worth far more than one request: on a pinned session on 2026-09-08 a single solve carried 20 consecutive register requests before the register asked for another.


📊 Measured performance

On Apify, at the default 4096 MB, through Apify RESIDENTIAL country-DE (2026-09-08)

The documented demo input — lookups: ["Debeka", "Gothaer", "HanseMerkur"] — run five times. Platform cost is the settled usageTotalUsd, read after the counters stopped moving (it lags 20–60 s and reads low if you take it at exit):

RunResultWall clockRowsChargedPlatform cost
1 (cold, empty cache)SUCCEEDED51.4 s15 full records + 1 free NO_HITS row15 × register-record$0.0195
2SUCCEEDED35.5 s1615$0.0129
3SUCCEEDED38.6 s1615$0.0139
4 — empty input {}SUCCEEDED39.3 s1615$0.0141
5 — empty input {}SUCCEEDED40.2 s1615$0.0144
  • Delivered == billed, exactly. 15 full records delivered, chargedEventCounts = 15 register-record. The NO_HITS row was delivered free, as designed.
  • The cold run spent 14 register requests and 1 proof-of-work solve. Runs 2-5 spent 3 requests — the three searches, which are never cached — because the eleven record fetches came back from the 24-hour record cache. That is a 60-70 % cut in wall clock on a repeated watchlist, with identical output.
  • An empty input does not fail: it falls back to the documented demo and returns rows.

A further 19 platform runs were made on 2026-09-08 to check every input against a paired control (filters, caps, narrowing, dedupe, cross-references, the charge cap, a zero-row run). All 19 SUCCEEDED, none was slower than 75 s, and settled platform cost ran $0.011 – $0.026 per run.

How fast this actually goes

Wall clock is dominated by one proof-of-work solve, and that is a near-fixed cost per run:

WhatMeasured, 2026-09-08, Apify RESIDENTIAL/DE
Difficulty the register applied166 on every run of the day (170 twice)
One solve22 – 34 s of one vCPU
Requests one solve carriedup to 8 gated requests with zero 403s (14 requests, 6 of them the captcha-free querverweis route)
Gap between requeststhe 1.2 s floor + ~0.4 s of network
A three-name demo run30 – 52 s end to end

So on one polite session at difficulty 166 the sustained ceiling is roughly 8 register requests per solve-cycle of ~40 s ≈ 12 requests/minute ≈ 700 per hour, and a lookup costs one search plus one request per full record. On a fresh, unpenalised egress (difficulty 139, a 2.1 s solve) the same cycle takes ~12 s, so several thousand requests an hour is arithmetically possible — but that is the register's headroom to give, not ours to take, and the actor will not chase it by rotating IPs. Split large watchlists across runs, or bring your own German egress with proxyConfiguration.

Repeat runs and the record cache

cacheMaxAgeHours (24 by default) keeps full records fetched by registration number in a named key-value store, so a daily run over the same watchlist re-asks the register only for the searches. Searches are never cached, so every run still reflects today's register.

One deliberate exception: the register serves a record's querverweis cross-references only to a session that fetched that record itself. Measured on a warm cache, 5 of 6 cross-reference calls came back HTTP 400 for exactly that reason. So a run with includeCrossReferences on does not read the cache — it re-fetches each record and the links come back populated (verified: 6 of 6). Rows that do reach you from the cache with cross-references requested say so in crossReferencesError rather than reporting a bare HTTP error.

Local live run, 5 lookups, no cache

Debeka, Gothaer, HanseMerkur, Schmidt, Andreas, ERGO Beratung with maxPreviewsPerLookup: 3, at difficulty 166:

  • 27 rows: 24 full register records + 2 free TRUNCATED rows + 1 free NO_HITS row
  • 24 register requests · 2 proof-of-work solves (47.2 s of CPU) · 90 s wall clock
  • an even split across the three live sub-registers (9 VVR / 9 FAV / 9 WIK; the fourth, DVR, did not appear)

Local live run, filters and narrowing

Mueller, Peter, Gothaer, a deliberately fake company; entityType: "COMPANY", resolvePreviews: false, narrowCities: ["Hamburg"]:

  • 2 full records + 8 bare matches + 3 free NO_HITS rows
  • 2 TOO_MANY_HITS refusals rescued by narrowing to Hamburg — and both flagged lookupComplete: false with a free NARROWED row naming the town
  • 4 rows removed by the entityType filter, none of them billed
  • 7 register requests · 2 solves · 65 s wall clock

Field fill, across the 15 full records of the cold platform run

FieldFilled
registrierungsnummer · register · taetigkeitsart · taetigkeitsartText · gewoParagraph · entityType · displayName · firma · strasse · plz · ort · registerbehoerde name/street/no./postcode/town · letztesUpdate · sourceUrl100 %
vertretungsberechtigte (named directors)87 %
hausnummer · querverweisAnfrage80 %
registerbehoerdeBundesland73 %
registerbehoerdeLand53 %
ortsteil · erlaubnisbehoerde (name + full address + under-review flag)47 %
erlaubnisumfang — all three permit-scope booleans33 % (the register publishes these on FAV/HOF records only)
unmittelbarMitwirkende20 %
name / vorname (sole traders only — 2 of these 15 entries)13 %
anrede · auslandstaetigkeiten7 %

Fields at 0 % in that sample — trittAufAls, gebundenerVermittler, namenszusatz, leitendeAngestellte, personenhandelsgesellschaften, weitereRegistrierungen, the authority phone and email — are ones the register simply leaves empty on most entries. They are parsed and delivered whenever the register fills them.

👤 Personal data — stated plainly

The Vermittlerregister is overwhelmingly made up of named natural persons. Most entries are sole-trader intermediaries, published by name under a statutory transparency mandate (§§ 34d / 34f / 34h / 34i GewO) so that consumers can check whether the person advising them holds a permit. Company entries additionally name their directors, senior employees and directly involved staff.

Two consequences you should design around:

  1. entityType is a first-class input. Set it to COMPANY to keep only registered firms, or PERSON to keep only individuals. The filter runs before anything is billed, and the actor will not even spend a request resolving a match it can already see the filter will discard.
  2. The register publishes no contact details for intermediaries. No email, no phone — the only phone and email fields in the data belong to the supervising chamber, and in every record captured during development they came back empty. That is exactly why this is a verification and enrichment product, not a contactable lead list. If someone sells you the German register as an email list, they did not get the emails from the register.

⚖️ Source, robots.txt and fair use

Data source: https://www.vermittlerregister.info — the Vermittlerregister of the DIHK (Deutscher Industrie- und Handelskammertag), the statutory public register under § 11a GewO.

robots.txt as served on 2026-09-07, quoted in full:

User-agent: *
Disallow: /inhalt
Disallow: /recherche?
Disallow: /en/recherche?
Disallow: /bekanntmachungen/versicherungsvermittler-nichtanonymisierte
Disallow: /bekanntmachungen/immobiliardarlehensvermittler-nichtanonymisierte
Disallow: /bekanntmachungen/versicherungsvermittler-anonymisierte
Disallow: /bekanntmachungen/immobiliardarlehensvermittler-anonymisierte
Disallow: /en/notices/insurance-brokers-non-anonymised
Disallow: /en/notices/mortgage-brokers-non-anonymised
Disallow: /en/notices/insurance-brokers-anonymised
Disallow: /en/notices/mortgage-brokers-anonymised
Sitemap: https://www.vermittlerregister.info/sitemap.xml

The full file is also written into every run's RUN_SUMMARY record, so you always have the version that was live when your data was collected. Read it and make your own call. Note that the register's data routes under /suche/ are not listed in it, and that the disallowed /bekanntmachungen/* notice lists are not touched by this actor.

This is public register data published for public verification. You are responsible for how you use it — in particular for GDPR compliance when the entry is a natural person, and for respecting the register's own rate limits, which is why this actor is deliberately slow and never rotates IPs. Not affiliated with, endorsed by, or connected to the DIHK or any IHK.


❓ FAQ

Can I export all ~190,000 registered intermediaries? No, and neither can anyone else, including through the register's own website. There is no listing endpoint, no pagination parameter, and the register refuses broad queries by design. Anything promising you a full dump of this register is not getting it from this register.

What exactly happens when a query is "too broad"? The register returns HTTP 200 with status: "TOO_MANY_HITS" and data: null for that sub-register. This actor writes a free row carrying that status, the register's own narrowing instruction, and a list of the towns it tried on your behalf. You are never charged for a refusal.

How do I search for a person? Write Surname, Firstname — the comma is what tells the actor it is a person, and the register needs both halves (at least two characters each). Without a comma the line is treated as a company name. advancedLookups gives you explicit name / vorname fields if you prefer.

Why did my common surname return nothing? Because the register refused it. Add an ort or plz, put likely towns in narrowCities, or look the person up by registration number. See the TOO_MANY_HITS row in your dataset for the specifics.

Does it get email addresses or phone numbers? No. The register does not publish them. See Personal data, above.

What is taetigkeitsart and why does it matter? It is the code for the exact statutory permission, and it is the single most valuable field here. A 3 on a VVR record means gebundener Versicherungsvertreter under § 34d Abs. 7 GewO — a tied agent, not an independent broker — which is precisely the distinction a pool operator, an MGA or a compliance team needs and which no Handelsregister extract, Maps listing or CRM enrichment gives you. The actor decodes it into taetigkeitsartText and gewoParagraph using the register's own wording.

Why is it slow? Because it is polite on purpose. The register applies a per-IP proof-of-work and a per-IP rate limit; the fastest way to make a run slower is to push harder, which makes the register raise the difficulty. One sticky session, one request at a time, 1.2 s apart. Use proxyConfiguration to bring your own egress if you need more throughput.

Can I run it daily over the same watchlist? Yes — that is what cacheMaxAgeHours is for. Full records fetched by registration number are kept in a named key-value store and reused for 24 hours by default, so a daily run does not re-ask the register for records that have not changed. Searches are never cached, so every run still reflects today's register. Cached rows are marked fromCache: true. The one exception is includeCrossReferences — see Repeat runs and the record cache.

How do I know whether my run is complete? Every row carries lookupComplete, and the RUN_SUMMARY record carries runComplete plus the exact counts: refusals, narrowed answers, matches dropped by your cap, rows removed by your filters, duplicates suppressed, requests spent and puzzles solved. If anything truncated your run, the run's status message names the cap that did it.

What happens if the register changes its gate? The sitekey and the search base URL are read from the live page at the start of every run, never hard-coded, so a rotated key is a non-event. If the register ever switches to reCAPTCHA — an interactive human challenge — this actor stops and tells you that in plain language rather than returning half-data. It does not attempt human-verification challenges.

Is a run that finds nothing a failed run? No. It exits cleanly with a status message naming the real cause — your cap, your budget, the register's refusal or a genuine absence — and the free status rows in the dataset explain each lookup individually.