Vermittlerregister Scraper - German Insurance Broker Lookup
Pricing
from $4.00 / 1,000 per full register records
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
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=2query returns byte-identical bytes to page 1 — measured), and any query it considers too broad comes backTOO_MANY_HITSwith 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 number — D-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 people — vertretungsberechtigte (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
| Code | Register | Statute |
|---|---|---|
| VVR | Versicherungsvermittler — brokers, agents, tied agents, advisers | § 34d GewO |
| FAV | Finanzanlagenvermittler & Honorar-Finanzanlagenberater | §§ 34f / 34h GewO |
| WIK | Immobiliardarlehensvermittler — mortgage-credit intermediaries | § 34i GewO |
| DVR | Darlehensvermittler — 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 (DF141WS3191works). 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:
| Query | Result |
|---|---|
name=Schmidt&vorname=Andreas | VVR → TOO_MANY_HITS, zero rows |
name=Schmidt&vorname=Andreas&ort=Berlin | VVR → 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
| Event | Price | Fires |
|---|---|---|
| Per full register record | $0.004 | Once for each complete statutory record delivered — whether it came back directly or was upgraded from a match |
| Per match without the full record | $0.001 | Once 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 entryTOO_MANY_HITS— the register refused the query, with its own narrowing instruction quotedNARROWED— the refused query was answered for one town only, and which townTRUNCATED— yourmaxPreviewsPerLookupcap dropped matches, listing the registration numbers it dropped so you can feed them back inINVALID_INPUT— the lookup could not be searched, and whyCHARGE_CAP— yourmaxTotalChargeUsdstopped rows being delivered, saying how many were fetched-but-not-delivered and listing their registration numbers- rows your
entityTypeorregistersfilter 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; runCompletegoes false, andRUN_SUMMARYcarrieschargeCapReachedandrowsDroppedByChargeCap;- a free
CHARGE_CAProw lists the registration numbers that were dropped; - the run's status message names
maxTotalChargeUsdas 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 seen | Work per solve | Solve time, one desktop core |
|---|---|---|
| 139 | ~8.2 M hashes | 2.1 s |
| 155 | ~30.6 M hashes | 8.2 s |
| 166 | ~84.7 M hashes | 23–25 s |
| 170 | ~112 M hashes | 40 s |
| 194 (Apify's own datacenter egress — i.e. no proxy) | ~839 M hashes | 296.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
maxCaptchaDifficultyof 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):
| Run | Result | Wall clock | Rows | Charged | Platform cost |
|---|---|---|---|---|---|
| 1 (cold, empty cache) | SUCCEEDED | 51.4 s | 15 full records + 1 free NO_HITS row | 15 × register-record | $0.0195 |
| 2 | SUCCEEDED | 35.5 s | 16 | 15 | $0.0129 |
| 3 | SUCCEEDED | 38.6 s | 16 | 15 | $0.0139 |
4 — empty input {} | SUCCEEDED | 39.3 s | 16 | 15 | $0.0141 |
5 — empty input {} | SUCCEEDED | 40.2 s | 16 | 15 | $0.0144 |
- Delivered == billed, exactly. 15 full records delivered,
chargedEventCounts= 15register-record. TheNO_HITSrow 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:
| What | Measured, 2026-09-08, Apify RESIDENTIAL/DE |
|---|---|
| Difficulty the register applied | 166 on every run of the day (170 twice) |
| One solve | 22 – 34 s of one vCPU |
| Requests one solve carried | up to 8 gated requests with zero 403s (14 requests, 6 of them the captcha-free querverweis route) |
| Gap between requests | the 1.2 s floor + ~0.4 s of network |
| A three-name demo run | 30 – 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
TRUNCATEDrows + 1 freeNO_HITSrow - 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_HITSrows - 2
TOO_MANY_HITSrefusals rescued by narrowing to Hamburg — and both flaggedlookupComplete: falsewith a freeNARROWEDrow 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
| Field | Filled |
|---|---|
| registrierungsnummer · register · taetigkeitsart · taetigkeitsartText · gewoParagraph · entityType · displayName · firma · strasse · plz · ort · registerbehoerde name/street/no./postcode/town · letztesUpdate · sourceUrl | 100 % |
| vertretungsberechtigte (named directors) | 87 % |
| hausnummer · querverweisAnfrage | 80 % |
| registerbehoerdeBundesland | 73 % |
| registerbehoerdeLand | 53 % |
| ortsteil · erlaubnisbehoerde (name + full address + under-review flag) | 47 % |
| erlaubnisumfang — all three permit-scope booleans | 33 % (the register publishes these on FAV/HOF records only) |
| unmittelbarMitwirkende | 20 % |
| name / vorname (sole traders only — 2 of these 15 entries) | 13 % |
| anrede · auslandstaetigkeiten | 7 % |
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:
entityTypeis a first-class input. Set it toCOMPANYto keep only registered firms, orPERSONto 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.- 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: /inhaltDisallow: /recherche?Disallow: /en/recherche?Disallow: /bekanntmachungen/versicherungsvermittler-nichtanonymisierteDisallow: /bekanntmachungen/immobiliardarlehensvermittler-nichtanonymisierteDisallow: /bekanntmachungen/versicherungsvermittler-anonymisierteDisallow: /bekanntmachungen/immobiliardarlehensvermittler-anonymisierteDisallow: /en/notices/insurance-brokers-non-anonymisedDisallow: /en/notices/mortgage-brokers-non-anonymisedDisallow: /en/notices/insurance-brokers-anonymisedDisallow: /en/notices/mortgage-brokers-anonymisedSitemap: 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.