Zurück zum Blog

Google-Maps-Leads mit n8n automatisieren: Polling-Workflow

Ilyas Yıldırım
Ilyas Yıldırım
12 Min. Lesezeit

Ja, n8n kann die Gewinnung von Google-Maps-Leads ohne Browserarbeit automatisieren. Der Workflow startet einen BasedOnB Job per HTTP, prüft seinen Status in begrenzten Abständen, lädt anschließend alle Ergebnisseiten und überträgt die Unternehmen in Google Sheets oder ein CRM.

Eine zuverlässige Automation besteht nicht nur aus dem ersten POST. Sie braucht sichere Wiederholungen, ein Ende für die Polling-Schleife, eigene Fehlerzweige, Cursor-Paginierung und Schutz vor doppelten CRM-Einträgen. Das folgende Beispiel sucht Solarteure in Hamburg und bildet den gesamten Ablauf mit verständlichen Nodes ab.

Wenn Zielgruppe und Datenfelder noch nicht feststehen, hilft zuerst der Leitfaden zum Aufbau einer Google-Maps-Leadliste. Hier gehen wir davon aus, dass Kategorie, Ort, Zielmenge und Zielsystem bereits festgelegt sind.

Voraussetzungen

Bereiten Sie diese Dinge vor:

  • Eine n8n Instanz mit ausgehenden HTTPS-Verbindungen.
  • Einen BasedOnB API-Schlüssel mit Lese- und Schreibrecht für Scrapes. Er beginnt mit bdb_live_.
  • Ein Sheet, eine CRM-Liste oder eine Tabelle mit einer Spalte für einen stabilen externen Schlüssel.
  • Eine Kampagnen- oder Quellereignis-ID für den Idempotency-Key.
  • Suchbegriff, Land, Bundesland, Stadt und gewünschte Leadzahl.

Erstellen Sie den Schlüssel im Dashboard und speichern Sie ihn als Header Auth Credential in n8n. Ein echter Schlüssel gehört nicht in Edit Fields, einen Workflow-Export, einen Screenshot oder eine Supportnachricht. Die aktuellen Endpunkte und Antwortfelder stehen in der lokalisierten BasedOnB API-Dokumentation.

Planen Sie Batches innerhalb der Produktgrenzen. Ein Job darf höchstens 10 Suchanfragen enthalten. Ein Konto kann höchstens 2 offene Jobs haben. Die Ziele aller offenen Jobs dürfen zusammen nicht mehr als 5.000 Leads umfassen. pending, submitted und running zählen als offen. Ein Zeitplan ohne diese Prüfung erhält irgendwann eine Kapazitätsantwort mit 429.

Der Ablauf in zehn Schritten

Die Polling-Variante sieht so aus:

  1. Schedule Trigger oder Webhook erhält die Kampagne.
  2. Edit Fields bereinigt die Eingaben und setzt einen stabilen Idempotency-Key.
  3. HTTP Request sendet POST /api/v1/scrapes.
  4. IF prüft statusCode; aus einer erfolgreichen Antwort entsteht ein Polling-Status mit einem Element.
  5. Wait und Edit Fields erhöhen attempt in diesem Statuszweig.
  6. HTTP Request ruft GET /api/v1/scrapes/:id auf; Merge verbindet die Antwort mit dem Status.
  7. Switch trennt HTTP-Fehler, done, failed, cancelled und offene Zustände.
  8. Nach done lädt HTTP Request die Ergebnisseiten und prüft zuerst statusCode.
  9. Jede Seite verzweigt einmal: body.results geht zu Split Out, ein Kontroll-Element prüft body.page.
  10. Der Kontrollzweig ruft den nächsten Cursor einmal ab und führt die Antwort zum selben Verzweigungspunkt zurück.

Die offizielle Anleitung zum HTTP Request Node erklärt Authentifizierung, Header, Query-Parameter und Antworten. Die Anleitung zum Wait Node beschreibt zeitgesteuertes Warten und das Fortsetzen einer Ausführung.

1. Eine stabile Anfrage bauen

Setzen Sie direkt hinter den Trigger einen Edit Fields Node. Für das Hamburger Beispiel reichen diese Felder:

{
  "query": "solar installers",
  "country": "DE",
  "state": "DE.04",
  "city": "Hamburg",
  "target_leads": 250
}

query enthält Kategorie oder Suchphrase. country, state und city begrenzen die Region. target_leads ist die gewünschte Zahl von Unternehmenseinträgen. Sie muss zu den verfügbaren Credits und den offenen Jobs passen.

Fügen Sie ein Feld idempotency_key hinzu. Ein Wert wie hamburg-solar-2026-08-batch-01 ist verständlich und wiederverwendbar. Noch besser ist eine gespeicherte CRM-Kampagnen-ID oder die ID des auslösenden Ereignisses. Nutzen Sie keinen Zeitstempel. Bei einer Wiederholung würde n8n sonst einen neuen Schlüssel erzeugen und möglicherweise einen zweiten Job starten.

2. Den Scrape starten

Konfigurieren Sie den HTTP Request Node so:

  • Method: POST
  • URL: https://www.basedonb.com/api/v1/scrapes
  • Authentication: Header Auth mit Authorization: Bearer bdb_live_...
  • Zusätzlicher Header: Idempotency-Key aus Edit Fields
  • Body Content Type: JSON
  • Body: die fünf Felder aus dem Beispiel
  • Options > Response > Include Response Headers and Status: ein
  • Options > Response > Never Error: ein
  • Options > Response > Response Format: JSON

Mit diesen Einstellungen gibt n8n auch Antworten außerhalb von 2xx als Output aus. Das vollständige Element enthält statusCode, headers und body. Verzweigen Sie zuerst nach $json.statusCode. Nur eine 2xx-Antwort darf zur Joblogik weiterlaufen. Erzeugen Sie dafür mit Edit Fields ein Poll-State-Element mit job_id = {{$json.body.id}}, job_status = {{$json.body.status}} und attempt = 0. Andere Statuscodes gehen in den gemeinsamen HTTP-Fehlerzweig.

Ein neuer Auftrag liefert häufig submitted. Ein bereits nutzbares Ergebnis kann aber sofort done liefern. Lesen Sie deshalb nach der 2xx-Prüfung $json.body.status, bevor der Workflow zum Wait Node geht.

Wird derselbe Idempotency-Key mit derselben Nutzlast erneut gesendet, kommt der ursprüngliche Job zurück. Derselbe Schlüssel mit einer anderen Stadt, Suche oder Zielmenge ergibt einen Konflikt. Speichern Sie Schlüssel und Anfrage gemeinsam.

3. Begrenztes Polling mit Wait und Switch

Verwenden Sie das Poll-State-Element aus dem erfolgreichen POST. Routen Sie job_status so:

  • done: direkt zum Ergebnisabruf.
  • failed oder cancelled: zum terminalen Fehlerzweig.
  • pending, submitted oder running: zum Wait Node.

Stellen Sie Wait zum Beispiel auf 15 Sekunden. Behalten Sie danach mit Edit Fields job_id und setzen Sie attempt = {{$json.attempt + 1}}. Verbinden Sie dieses eine Status-Element mit zwei Zweigen. Input 1 eines Merge Nodes erhält es direkt. Der andere Zweig ruft diesen Endpunkt auf:

GET https://www.basedonb.com/api/v1/scrapes/{{$json.job_id}}

Verwenden Sie dasselbe Header Auth Credential und dieselben drei Response-Einstellungen wie beim POST. Führen Sie die Statusantwort zu Input 2 von Merge. Stellen Sie Merge auf Mode: Combine und Combine By: Position. So bleiben job_id und attempt neben statusCode und body erhalten und werden nicht von der GET-Antwort ersetzt.

Prüfen Sie nach Merge zuerst $json.statusCode. Antworten außerhalb von 2xx gehen in den gemeinsamen HTTP-Fehlerzweig. Bei 2xx liest Switch $json.body.status und trennt done, failed, cancelled und offene Zustände. Ein eigenes IF im offenen Zweig prüft $json.attempt, bevor der Ablauf zu Wait zurückkehrt.

Setzen Sie vor der Rückkehr zu Wait noch ein IF. Wenn attempt Ihr festgelegtes Maximum erreicht, beenden Sie das Polling und senden eine Meldung mit der Job-ID. 40 Versuche mit jeweils 15 Sekunden begrenzen zum Beispiel diese n8n Ausführung. Sie versprechen nicht, dass jeder Job nach zehn Minuten abgeschlossen ist. Brechen Sie den Serverjob am Versuchslimit nicht automatisch ab. Er kann später noch fertig werden.

Die Zweige failed und cancelled dürfen nicht in die Schleife zurücklaufen. Speichern Sie Status, Job-ID, Kampagnenkennung und nach Möglichkeit die Request-ID. Prüfen Sie Eingabe und Kontostand, bevor Sie bewusst einen neuen Job senden.

4. Alle Ergebnisse mit Cursor laden

Rufen Sie die Ergebnisse erst nach done ab:

GET https://www.basedonb.com/api/v1/scrapes/{{$json.id}}/results

Setzen Sie den Query-Parameter limit auf 500. Aktivieren Sie wie bei den anderen HTTP Request Nodes Include Response Headers and Status sowie Never Error und wählen Sie JSON als Response Format. Prüfen Sie zuerst $json.statusCode; Antworten außerhalb von 2xx gehen in den gemeinsamen Fehlerzweig. Eine erfolgreiche vollständige Antwort sieht so aus:

{
  "statusCode": 200,
  "body": {
    "id": "scrape-job-id",
    "status": "done",
    "results": [],
    "page": {
      "limit": 500,
      "next_cursor": "next-page-token",
      "has_more": true,
      "total": 1250
    }
  }
}

Behalten Sie die erfolgreiche Seitenantwort als ein Element und verbinden Sie sie mit zwei Zweigen. Im Datenzweig zerlegt Split Out body.results; danach werden die Zeilen zugeordnet und per Upsert geschrieben. Der Kontrollzweig läuft nicht durch Split Out. Sein IF liest $json.body.page.has_more und wird deshalb einmal pro Seite statt einmal pro Unternehmen ausgeführt.

Im true-Zweig setzt Edit Fields job_id = {{$json.body.id}} und cursor = {{$json.body.page.next_cursor}}. Ein HTTP Request namens Next Results ruft denselben Endpunkt mit limit=500, diesem Cursor und denselben Response-Einstellungen auf. Verbinden Sie seine Antwort wieder mit der statusCode-Prüfung und dem Verzweigungspunkt der ersten Seite. Der false-Zweig beendet die Paginierung. Für 1.250 Datensätze sind drei Abrufe nötig.

Verwechseln Sie total nicht mit der Zahl der Zeilen auf der ersten Seite. Erfinden Sie keine Seitennummer. Folgen Sie genau dem Cursor aus der Antwort. Schreiben Sie jede Seite vor dem nächsten Abruf, damit ein abgebrochener Lauf an einer klaren Stelle fortgesetzt werden kann.

5. Google Sheets oder CRM sauber befüllen

Zerlegen Sie im Datenzweig body.results mit Split Out. Danach legt Edit Fields die Zielstruktur fest:

BasedOnB FeldZielfeld
place_idexternal_id
titlecompany_name
categorycategory
addressaddress
phonephone
websitewebsite
ratinggoogle_rating
reviews_countgoogle_review_count

Nutzen Sie place_id zur Duplikatkontrolle. Im CRM sollte der Node einen Upsert statt eines blinden Create ausführen. In Google Sheets suchen Sie zuerst nach der ID und aktualisieren oder ergänzen die Zeile. Reines Append eignet sich für einen Wegwerftest, erzeugt bei einer wiederholten n8n Ausführung aber doppelte Einträge.

Nutzen Sie Upserts und speichern Sie Job-ID sowie Cursor für jede Seite. Der Kontrollzweig mit einem Element kann die nächste Seite abrufen, während der Datenzweig Zeilen schreibt. Wenn eine strikte Reihenfolge nötig ist, lassen Sie den Kontrollzweig auf genau ein Seite-fertig-Signal des Ziels warten. Verbinden Sie das Next-Page-IF nie hinter Split Out, sonst wird derselbe Cursor für jedes Ergebnis aufgerufen.

Nach dem Import hilft die Leadlisten-Qualitäts-Scorecard bei Vollständigkeit, Dubletten, Aktualität und Herkunft. Soll ein Assistent die Liste weiter auswerten, zeigt der Leitfaden zur KI-Leadgenerierung, welche Aufgabe zum Modell und welche zur Datenquelle gehört.

Häufige Fehler

Da Never Error aktiv ist, muss jeder HTTP Node zuerst nach $json.statusCode verzweigen. Bei Antworten außerhalb von 2xx lesen Sie $json.body.error.code und $json.body.error.details; der Fehlerkörper bleibt im vorgesehenen Zweig verfügbar.

401 Unauthorized: Der Schlüssel fehlt, ist falsch oder wurde widerrufen. Prüfen Sie das n8n Credential und schreiben Sie das Geheimnis nie in Ausführungsprotokolle.

403 Forbidden: Der Schlüssel ist gültig, hat aber nicht den nötigen Scrape-Scope. Wählen Sie ein Credential mit den kleinsten passenden Lese- und Schreibrechten.

402 Payment Required: Credits, Abo oder Zahlung brauchen Aufmerksamkeit. Stoppen Sie den Zweig und informieren Sie den Kontoinhaber.

409 Conflict: Der Idempotency-Key wurde mit einer anderen Nutzlast verwendet. Vergleichen Sie den gespeicherten Schlüssel und die Nutzlast. Wiederholen Sie denselben Schlüssel erst nach Wiederherstellung der ursprünglichen Nutzlast. Für einen bewusst neuen Job verwenden Sie einen neuen stabilen Schlüssel.

429 Too Many Requests: Prüfen Sie $json.body.error.details.reason. too_many_open_scrapes bedeutet, dass einer der 2 offenen Jobs enden muss. Bei open_leads_limit warten Sie oder senken das neue Ziel so, dass die offenen Jobs zusammen höchstens 5.000 Leads umfassen. Fehlt reason, gilt die Anfragefrequenz als Ursache. Nutzen Sie dann den Header Retry-After oder $json.body.error.details.retry_after_seconds in einem begrenzten Wait-Zweig.

Leere Ergebnisse: Prüfen Sie den Jobstatus. Solange ein Job aktiv ist, kann der Ergebnis-Endpunkt eine leere Seite liefern. Er ersetzt die Statusabfrage nicht.

Für umfangreichere ereignisbasierte Abläufe bietet der Zapier-und-Make-Leitfaden weitere Beispiele. Stabile Job-IDs, Endzustände und duplikatsichere Zielschreibvorgänge gelten in jedem Werkzeug.

Polling oder Webhook?

Polling ist leicht zu bauen und zu prüfen. Es passt zu einzelnen Jobs, seltenen Zeitplänen und Teams, die den Ablauf erst kennenlernen. Dafür entstehen wiederholte Statusabfragen, und die n8n Ausführung muss den Versuchszähler verwalten.

Ein Webhook ist sinnvoller, wenn Jobs häufig laufen oder unterschiedlich lange dauern. n8n erhält das Abschlussereignis, prüft die Signatur und startet erst dann den Ergebniszweig. Fehlerbehandlung, Paginierung und Upserts bleiben trotzdem nötig. Der Webhook ändert nur, wie der Abschluss erkannt wird.

Beginnen Sie mit begrenztem Polling, wenn das Zustandsmodell dadurch klarer wird. Wechseln Sie zum Webhook, sobald die wiederholten Abfragen unnötige Betriebslast erzeugen.

FAQ

Kann n8n Google-Maps-Leads automatisch sammeln?

Ja. n8n kann über die BasedOnB REST API eine Suche nach Unternehmen starten, während der Verarbeitung warten, alle Ergebnisseiten abrufen und die Datensätze an Google Sheets oder ein CRM senden. Speichern Sie den API-Schlüssel in n8n Credentials und nicht in einem normalen Workflow-Feld.

Welche n8n Nodes braucht dieser Workflow?

Verwenden Sie Trigger, Edit Fields, HTTP Request, Wait, IF oder Switch, Merge, Split Out und den Ziel-Node. Merge bewahrt den Polling-Zähler neben der Statusantwort. Split Out gehört nur in den Datenzweig, damit der Kontrollzweig der Paginierung genau ein Element behält.

Warum sollte ich einen Idempotency-Key senden?

Er macht einen wiederholten POST sicher. Derselbe Schlüssel mit derselben Anfrage liefert den ursprünglichen Job, statt einen zweiten anzulegen. Verwenden Sie eine gespeicherte Kampagnen- oder Ereignis-ID und keinen Wert aus der aktuellen Uhrzeit.

Wie oft sollte n8n den Jobstatus abfragen?

Setzen Sie einen Wait Node zwischen die Statusabfragen und begrenzen Sie die Zahl der Versuche. Ein festes Intervall von 10 bis 20 Sekunden ist ein brauchbarer Startwert, aber keine Zusage für die Laufzeit. Stoppen Sie am Limit und informieren Sie einen Verantwortlichen.

Wie lade ich mehr als 500 Ergebnisse?

Fordern Sie höchstens 500 Zeilen an und lesen Sie body.page.has_more sowie body.page.next_cursor aus der vollständigen HTTP-Antwort. Lassen Sie die Paginierung in einem Kontrollzweig mit genau einem Element laufen, bis has_more false ist.

Soll ich in n8n Polling oder einen Webhook nutzen?

Polling ist für den ersten Workflow und seltene Jobs einfacher. Ein signierter Webhook passt besser zu häufigen oder langen Abläufen, weil n8n auf das Abschlussereignis warten kann. Die Ergebnisse müssen bei beiden Varianten mit Cursor-Paginierung geladen werden.

Was passiert bei failed oder cancelled?

Behandeln Sie failed und cancelled als Endzustände. Rufen Sie keine Ergebnisse ab und führen Sie diesen Zweig nicht zurück zum Wait Node. Speichern Sie Job-ID und Status, informieren Sie den Besitzer und entscheiden Sie danach bewusst über einen neuen Auftrag.

Mit einem kleinen Job beginnen

Starten Sie mit einer Kategorie, einer Stadt und einer Zieltabelle, die Sie von Hand prüfen können. Ein neues BasedOnB-Konto erhält einmalig 50 Export-Credits ohne Karte. Damit testen Sie Feldzuordnung, Idempotency-Key, Fehlerzweig und Upsert-Regel, bevor Sie die Zielmenge erhöhen.

Legen Sie das Credential an, führen Sie den ersten begrenzten Workflow aus und speichern Sie die Job-ID neben den importierten Zeilen. Diese kleine Herkunftsangabe macht spätere Prüfungen und Reparaturen deutlich einfacher.