{
  "openapi": "3.0.1",
  "info": {
    "title": "Vermittlerregister Scraper - German Insurance Broker Lookup",
    "description": "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.",
    "version": "0.1",
    "x-build-id": "pASFK1CemYoi5lNA2"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~vermittlerregister-de-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-vermittlerregister-de-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/scrapersdelight~vermittlerregister-de-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-vermittlerregister-de-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/scrapersdelight~vermittlerregister-de-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-vermittlerregister-de-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "lookups": {
            "title": "Lookups (one per line)",
            "type": "array",
            "description": "The identities to resolve, one per line. Each line is auto-detected:\n\n• **A registration number** — `D-F-141-WS31-91`, `D-W-139-JLDM-77` or `D-URTZ-05QX4-94` (spaces and missing dashes are fixed for you). This is the cheapest and most exact lookup: one request, one full record.\n• **A person, as `Surname, Firstname`** — `Schmidt, Andreas`. 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.\n• **Anything else** — a company name, e.g. `Debeka` or `Allianz Beratungs`.\n\nEach lookup is searched across all three sub-registers in ONE request, so `Debeka` can return an insurance record and a financial-investment record together.\n\n**This is a batch lookup tool, not a bulk exporter.** The register has no listing route and no page parameter, and it refuses any query that is too broad with `TOO_MANY_HITS` — see the FAQ in the README.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "advancedLookups": {
            "title": "Advanced lookups (JSON)",
            "type": "array",
            "description": "Full control over each lookup, for when auto-detection is not enough — a person whose name contains a comma, a company narrowed to one town, or a street-level search. Every key is optional except that you need `unternehmensname`, OR both `name` and `vorname`, OR `registrierungsnummer`.\n\n```json\n[\n  { \"name\": \"Schmidt\", \"vorname\": \"Andreas\", \"ort\": \"Berlin\" },\n  { \"unternehmensname\": \"Debeka\", \"plz\": \"56073\" },\n  { \"registrierungsnummer\": \"D-F-141-WS31-91\" },\n  { \"unternehmensname\": \"M\\u00fcller\", \"strasse\": \"Hauptstr\", \"hausnummer\": \"1\", \"label\": \"my CRM id 4711\" }\n]\n```\n\n`label` is echoed back in `queryInput` so you can join the output to your own records. These run BEFORE the plain `lookups` list. Accepted keys: `unternehmensname`, `name`, `vorname`, `registrierungsnummer`, `ort`, `plz`, `strasse`, `hausnummer`, `label` — the exact parameter set the register's own search form uses.",
            "default": []
          },
          "ort": {
            "title": "Narrow every lookup to this town",
            "type": "string",
            "description": "Applied to every lookup that does not carry its own `ort`. This is the single most useful knob on this actor: a common surname comes back `TOO_MANY_HITS` with no rows, and adding a town collapses it to one full record (measured: `Schmidt, Andreas` refused, `Schmidt, Andreas` + Berlin returned the complete record D-E93Q-TDOYE-63).",
            "default": ""
          },
          "plz": {
            "title": "Narrow every lookup to this postcode",
            "type": "string",
            "description": "Applied to every lookup that does not carry its own `plz`. Use it instead of `ort` when you know the postcode — it is stricter and more reliable than a town name.",
            "default": ""
          },
          "strasse": {
            "title": "Narrow every lookup to this street",
            "type": "string",
            "description": "Street name without the house number, applied to every lookup that does not carry its own `strasse`. Rarely needed; useful for a very common company name at a known address.",
            "default": ""
          },
          "hausnummer": {
            "title": "Narrow every lookup to this house number",
            "type": "string",
            "description": "House number, applied to every lookup that does not carry its own `hausnummer`. Only meaningful together with `strasse`.",
            "default": ""
          },
          "autoNarrow": {
            "title": "Retry a TOO_MANY_HITS query with a town",
            "type": "boolean",
            "description": "When one sub-register refuses a lookup as too broad, retry it narrowed to a town — first the towns from **Narrowing cities** below, then any town the OTHER sub-registers already named for that same identity in the same response. This is how a refused insurance query gets answered from a hit that the financial-investment register happened to return. Each retry costs one extra register request. Rows found this way carry `narrowedBy`, so the narrowing is never hidden from you.",
            "default": true
          },
          "narrowCities": {
            "title": "Narrowing cities (tried in order)",
            "type": "array",
            "description": "Towns to try first when auto-narrowing a refused query — e.g. the cities your target list is drawn from. Tried before the towns discovered from the register's own response.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxNarrowAttempts": {
            "title": "Maximum narrowing retries per refused query",
            "minimum": 0,
            "maximum": 25,
            "type": "integer",
            "description": "How many towns to try before giving up on one refused sub-register query and writing an honest, unbilled TOO_MANY_HITS row instead. Each attempt is one register request. Set 0 to never retry.",
            "default": 3
          },
          "registers": {
            "title": "Sub-registers to return",
            "uniqueItems": true,
            "type": "array",
            "description": "Which of the register's four sections to keep. All four are searched in the same single request, so excluding one does not save a request — but it does stop this actor spending extra requests resolving that section's matches, and rows it removes are never billed.\n\n• **VVR** — Versicherungsvermittler (insurance brokers, agents, tied agents, advisers; § 34d GewO)\n• **FAV** — Finanzanlagenvermittler and Honorar-Finanzanlagenberater (§§ 34f / 34h GewO)\n• **WIK** — Immobiliardarlehensvermittler (mortgage-credit intermediaries; § 34i GewO)\n• **DVR** — Darlehensvermittler (loan intermediaries). Present in the register's own client contract; we have not seen it in a live response, and pass through whatever the register returns.",
            "items": {
              "type": "string",
              "enum": [
                "VVR",
                "FAV",
                "WIK",
                "DVR"
              ],
              "enumTitles": [
                "VVR — Insurance intermediaries (§ 34d GewO)",
                "FAV — Financial-investment intermediaries (§§ 34f/34h GewO)",
                "WIK — Mortgage-credit intermediaries (§ 34i GewO)",
                "DVR — Loan intermediaries"
              ]
            },
            "default": [
              "VVR",
              "FAV",
              "WIK",
              "DVR"
            ]
          },
          "entityType": {
            "title": "Sole traders, companies, or both",
            "enum": [
              "ALL",
              "PERSON",
              "COMPANY"
            ],
            "type": "string",
            "description": "**Read this before you run a person-level export.** The Vermittlerregister is overwhelmingly made up of NAMED NATURAL PERSONS — sole-trader intermediaries published under a statutory transparency mandate (§§ 34d/34f/34h/34i GewO). Choose deliberately:\n\n• **ALL** — every entry the register returns.\n• **COMPANY** — only entries held by a firm (`firma`). The named directors on those records still appear in `vertretungsberechtigte`.\n• **PERSON** — only entries held by a named individual.\n\nApplied before billing: rows this filter removes are not delivered and not charged, and when the filter is set this actor will not even spend a request resolving a match it can already see will be dropped.",
            "default": "ALL"
          },
          "resolvePreviews": {
            "title": "Upgrade every match to a full record",
            "type": "boolean",
            "description": "When a lookup matches more than one entry, the register returns a short preview list (registration number, name, town) instead of full records. With this on, each preview is fetched as a complete record — permit type, permit scope, address, named directors, supervising authority — at the cost of one extra register request per match.\n\nTurn it off to pay $0.001 per bare match instead of $0.004 per full record, e.g. when you only want to know WHETHER someone is registered.",
            "default": true
          },
          "maxPreviewsPerLookup": {
            "title": "Maximum matches to expand per lookup",
            "minimum": 0,
            "maximum": 500,
            "type": "integer",
            "description": "A ceiling on how many matches of ONE lookup in ONE sub-register are turned into rows. It bounds both cost and runtime on a name that matches many people. When it truncates, the run writes a free `TRUNCATED` row listing the registration numbers it dropped, and every row of that lookup is marked `lookupComplete: false`. Set 0 for no ceiling.",
            "default": 5
          },
          "includeCrossReferences": {
            "title": "Fetch cross-register links",
            "type": "boolean",
            "description": "For each full record, follow the register's own `querverweis` link, which reports the same intermediary's entries in the other sub-registers. It costs one extra request but NO proof-of-work budget. Off by default because most records answer with an empty list.\n\nNote: the register only serves a record's cross-references to a session that fetched the record itself, so turning this on makes the run READ nothing from the 24-hour record cache (it still writes to it). Measured: with the cache in play, 5 of 6 cross-reference calls came back HTTP 400.",
            "default": false
          },
          "emitStatusRows": {
            "title": "Deliver free status rows",
            "type": "boolean",
            "description": "Write an unbilled row for every lookup that produced no record — `NO_HITS`, `TOO_MANY_HITS`, `INVALID_INPUT`, `TRUNCATED` — carrying the reason in plain language plus the register's own narrowing instruction. These rows are NEVER charged; they exist so you can audit a run and see exactly which of your inputs failed and why. Turn them off only if your pipeline cannot tolerate mixed row types.",
            "default": true
          },
          "deduplicate": {
            "title": "Suppress duplicate registration numbers",
            "type": "boolean",
            "description": "Deliver each `register + registration number` at most once per run, so overlapping lookups (a company name and its registration number, say) do not bill twice for identical data.",
            "default": true
          },
          "maxLookups": {
            "title": "Maximum lookups per run",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "How many lookups from your list this run will attempt. It is a hard stop on runtime and cost, and it is applied to the FRONT of the list. If it truncates your input the run says so in its status message and marks itself incomplete. Set 0 for no limit — but read the throughput note in the README first: this actor deliberately honours the register's rate limit and runs at roughly 25-30 lookups a minute.",
            "default": 25
          },
          "maxRequests": {
            "title": "Maximum register requests per run",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "A second, lower-level ceiling: the total number of HTTP requests this run may send to vermittlerregister.info, counting searches, record fetches and narrowing retries. Useful when you want a hard bound regardless of how many matches your lookups turn out to have. Set 0 for no limit.",
            "default": 0
          },
          "minRequestIntervalMs": {
            "title": "Minimum gap between requests",
            "minimum": 200,
            "maximum": 60000,
            "type": "integer",
            "description": "The floor between two register requests, in milliseconds. 1200 ms is what was measured as sustainable on a fresh German residential egress. Lowering it does not make a run faster in practice — the register answers by raising its proof-of-work difficulty for your IP, which costs far more time than it saves.",
            "default": 1200
          },
          "maxCaptchaDifficulty": {
            "title": "Stop above this proof-of-work difficulty",
            "minimum": 100,
            "maximum": 255,
            "type": "integer",
            "description": "The register's search gate is an automatic BLAKE2b proof-of-work — the same computation every visitor's browser runs, with no human interaction — and its difficulty is raised per IP against egresses that push hard. This actor reads the difficulty out of the puzzle BEFORE solving it, so this limit costs you nothing to enforce.\n\nMeasured 2026-09-08 on one Apify vCPU: Apify RESIDENTIAL country-DE was served 139–170 (2–40 s per solve). Apify's own datacenter egress, with no proxy, was served **194** — one solve is ~839 M hashes and took **296.9 s**, and a run there burned $0.20 of compute for 8 rows.\n\nAbove 185, or 6 points above where this run started, the actor slows to one request every 5 s. Above THIS number it stops the run cleanly, before spending the CPU, and tells you why — rather than grinding through the penalty or switching IPs to escape it. Raise it only if you are willing to pay for very long solves.",
            "default": 185
          },
          "cacheMaxAgeHours": {
            "title": "Reuse records fetched in earlier runs for",
            "minimum": 0,
            "maximum": 720,
            "type": "integer",
            "description": "Full records fetched by registration number are kept in a named key-value store and reused for this many hours, so a daily run over the same watchlist does not re-ask the register for records that have not changed. The register republishes once a day (see `letztesUpdate` on every row), so 24 hours is the natural setting. Cached rows are marked `fromCache: true` with the timestamp they were fetched.\n\nSearches are NEVER cached — every lookup still queries the register live, so a run always reflects today's register. Set 0 to disable the cache entirely.\n\nThe cache is not READ on a run with `includeCrossReferences` on — see that field.",
            "default": 24
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "The register applies its proof-of-work gate and its rate limit per egress IP, so a German residential proxy is strongly recommended — that is what the measurements in the README were taken through. Bring your own egress here if you need more throughput than one polite session gives you.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ],
              "apifyProxyCountry": "DE"
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}