B2B-Leadliste nach Excel: Firmen mit WZ-Filtern suchen und anreichern

6 Min. Lesezeit

Ein begrenzter Python-Workflow für die Firmensuche, eindeutige Zuordnung, optionale Kontaktanreicherung und den Excel-Export – mit geprüften Filtern und Kosten.

Eine B2B-Firmenliste lässt sich mit der handelsregister.ai API nach Standort und Branche eingrenzen, anhand eindeutiger Unternehmens-IDs anreichern und nach Excel exportieren. Du brauchst keinen allgemeinen Suchbegriff wie „GmbH“, wenn du passende Filter übergibst. Telefonnummern und Websites können im angereicherten Profil enthalten sein; ihre Verfügbarkeit ist nicht für jede Firma garantiert.

Diese Anleitung verwendet einen begrenzten Probelauf mit zehn Unternehmen. Die verwendete WZ-Filtersuche und ein anschließender Profilabruf wurden am 5. September 2026 erfolgreich gegen die API geprüft. Daraus folgt keine Zusage für eine bestimmte Laufzeit oder Kontaktabdeckung einer größeren Liste.

1. Standort und WZ-Branche festlegen

Für IT-Unternehmen in München verwenden wir die dokumentierten Filter city, industry_code und industry_scheme:

{
  "city": "München",
  "industry_code": ["62.10.1", "62.10.3"],
  "industry_scheme": "WZ2025"
}

Die Codes werden innerhalb der gewählten Klassifikation ausgewertet. Verwende WZ-2008- und WZ-2025-Codes nicht ungeprüft austauschbar. Unser WZ-Leitfaden erklärt die Einordnung; die aktuelle Suchdokumentation beschreibt die zulässigen API-Filter.

Für Größenkriterien stehen beispielsweise Bereiche unter financial_filters zur Verfügung. Ein Umsatzfilter sieht so aus: {"financial_filters":{"pl_revenue":{"gte":1000000}}}. Fehlende Finanzdaten können die Treffermenge einschränken. Eine gefilterte Liste ist deshalb keine vollständige Bestandsaufnahme aller wirtschaftlich passenden Firmen.

2. Treffer eindeutig identifizieren

GET /api/v1/search-organizations liefert Treffer unter results. Jeder Treffer enthält eine entity_id. Übergib diese ID als q an fetch-organization, statt erneut einen möglicherweise mehrdeutigen Namen zu suchen. Prüfe anschließend, ob die zurückgegebene ID mit deiner Auswahl übereinstimmt.

Für die Suche sind q oder Filter erforderlich. Die folgenden Abfragen arbeiten ausschließlich mit Filtern. Das Skript verwendet kleine Seiten und begrenzt die Gesamtzahl. Falls die Antwort total_exact: false ausweist, behandle total nicht als exakte Zahl aller passenden Unternehmen.

3. Python-Skript: filtern, anreichern, exportieren

Voraussetzungen: Python, requests, pandas und openpyxl. Installiere die Pakete mit pip install requests pandas openpyxl. Stelle deinen API-Schlüssel als Umgebungsvariable HANDELSREGISTER_API_KEY bereit. Das Skript übermittelt ihn ausschließlich im Header.

import json
import os
import time

import pandas as pd
import requests

BASE = 'https://handelsregister.ai/api/v1'
FILTERS = {
    'city': 'München',
    'industry_code': ['62.10.1', '62.10.3'],
    'industry_scheme': 'WZ2025',
}
MAX_COMPANIES = 10  # Start small: enriched profiles can cost 25 credits each.
PAGE_SIZE = 10


def get_json(session, endpoint, params):
    for attempt in range(4):
        response = session.get(BASE + endpoint, params=params, timeout=120)
        if response.status_code == 429 and attempt < 3:
            time.sleep(min(2 ** (attempt + 1), 30))
            continue
        response.raise_for_status()
        return response.json()
    raise RuntimeError('Request did not complete')


def find_companies(session, maximum=MAX_COMPANIES):
    selected, seen = [], set()
    skip = 0
    for _ in range(5):  # Cap search pages even if a page repeats.
        if len(selected) >= maximum:
            break
        limit = min(PAGE_SIZE, maximum - len(selected))
        page = get_json(session, '/search-organizations', {
            'filters': json.dumps(FILTERS), 'limit': limit, 'skip': skip,
        })
        hits = page.get('results') or []
        if not hits:
            break
        for hit in hits:
            entity_id = hit.get('entity_id')
            if entity_id and entity_id not in seen:
                seen.add(entity_id)
                selected.append(hit)
        skip += len(hits)
        if len(hits) < limit:
            break
    return selected[:maximum]


def excel_safe(value):
    # Treat company-supplied text as text, not as an Excel formula.
    if isinstance(value, str) and value.lstrip().startswith(('=', '+', '-', '@')):
        return "'" + value
    return value


def build_rows(session, candidates):
    rows = []
    for candidate in candidates:
        entity_id = candidate['entity_id']
        profile = get_json(session, '/fetch-organization', {
            'q': entity_id, 'ai_search': 'on-default',
        })
        if profile.get('entity_id') != entity_id:
            raise ValueError('Returned company does not match the selected entity_id')
        address = profile.get('address') or {}
        registration = profile.get('registration') or {}
        contact = profile.get('contact_data') or {}
        row = {
            'entity_id': entity_id,
            'name': profile.get('name'),
            'register_type': registration.get('register_type'),
            'register_number': registration.get('register_number'),
            'court': registration.get('court'),
            'city': address.get('city'),
            'postal_code': address.get('postal_code'),
            'phone': contact.get('phone_number'),
            'website': contact.get('website'),
        }
        rows.append({key: excel_safe(value) for key, value in row.items()})
    return rows


def main():
    with requests.Session() as session:
        session.headers.update({'x-api-key': os.environ['HANDELSREGISTER_API_KEY']})
        rows = build_rows(session, find_companies(session))
    if not rows:
        print('No matching companies; no file exported.')
        return
    pd.DataFrame(rows).to_excel('leads.xlsx', index=False, sheet_name='Companies')
    print(f'{len(rows)} companies exported; missing contact fields remain empty.')


if __name__ == '__main__':
    main()

Speichere den Code als build_leads.py und starte ihn mit python build_leads.py. MAX_COMPANIES begrenzt den Probelauf. Erhöhe den Wert erst nach Prüfung von Ergebnisqualität und Kosten. Das Skript exportiert nur erfolgreich abgefragte, eindeutig zugeordnete Profile. Bei einem nicht erfolgreich behandelten HTTP-Fehler bricht es ab, statt still eine vollständige Liste vorzutäuschen.

Telefonnummern und Websites kommen aus contact_data, soweit vorhanden. Leere Werte bleiben leer. Die Schutzfunktion für Excel sorgt dafür, dass Text mit einem führenden Formelzeichen als Text behandelt wird. Weder eine vorhandene Telefonnummer noch ein aktiver Registerstatus sind allein ein Qualitätsnachweis für einen Vertriebslead.

4. Credits richtig kalkulieren

Beim Test am 5. September 2026 wurden folgende Kosten in meta.request_credit_cost zurückgegeben:

Anfrage Credits pro erfolgreichem Testaufruf
Suche mit WZ- und Standortfilter 1
Firmenprofil mit ai_search=on-default, ohne weitere Features 25

Beim zweiten Aufruf setzen sich die Kosten aus 5 Basis-Credits und 20 Credits für den KI-Modus zusammen. Zehn angereicherte Profile plus eine Suchseite ergeben unter diesen Bedingungen 251 Credits. Das ist eine Beispielrechnung für diese Konfiguration, kein Pauschalpreis für jede Anfrage. Weitere Features, Wiederholungen und andere Modi können die Kosten ändern. Prüfe die Abrufdokumentation und die Metadaten deiner Antworten.

5. Vor der Nutzung der Liste prüfen

  • Identität: entity_id sowie Registerart, Nummer und Gericht gemeinsam dokumentieren. Ähnlich benannte Firmen nicht zusammenführen, nur weil sie zur selben Gruppe gehören.
  • Kontaktabdeckung: An einer kleinen Stichprobe kontrollieren, ob Website und Telefonnummer zur gewünschten Gesellschaft gehören. Die API kann fehlende Quellen nicht durch eine garantierte Telefonnummer ersetzen.
  • Aktualität: Abrufdatum festhalten und den Aktualisierungsrhythmus am konkreten Verwendungszweck ausrichten.
  • Kontaktaufnahme: Die Verfügbarkeit von Daten ist keine Werbeeinwilligung. Prüfe die Voraussetzungen des gewählten Kontaktkanals, insbesondere § 7 UWG, sowie die anwendbaren Datenschutzpflichten für deinen Fall.

Ohne eigenen Python-Workflow weiterarbeiten

Für eine bereits vorhandene Firmenliste ist der CSV-Enricher eine mögliche Alternative; für einzelne Recherchen eignet sich der Explorer. Wenn du die Abfragen in einen bestehenden Automationsprozess integrieren möchtest, beginne mit der n8n-Anleitung.

Starte mit wenigen Datensätzen, prüfe Zuordnung und Abdeckung und erweitere die Liste erst danach. So wird aus einer Suchabfrage ein nachvollziehbarer Datenprozess.