---
title: "Google-Sheets-Kontakte mit Apps Script in Brevo importieren"
description: "Ein wiederholbares Google-Apps-Script-Beispiel mit Einwilligungsprüfung, Feldzuordnung, Schutz vor erneuter Anmeldung und sichtbaren API-Fehlern."
url: https://tajo.io/de/blog/import-google-sheet-contacts-to-brevo-apps-script/
markdown: https://tajo.io/de/blog/import-google-sheet-contacts-to-brevo-apps-script.md
lang: de-DE
author: "Set Noa"
author_url: https://tajo.io/de/set-noa/
published: 2026-04-30
updated: 2026-10-09
alternates: {en-US: /blog/import-google-sheet-contacts-to-brevo-apps-script.md, bg-BG: /bg/blog/import-google-sheet-contacts-to-brevo-apps-script.md, cs-CZ: /cz/blog/import-google-sheet-contacts-to-brevo-apps-script.md, da-DK: /dk/blog/import-google-sheet-contacts-to-brevo-apps-script.md, es-ES: /es/blog/import-google-sheet-contacts-to-brevo-apps-script.md, fr-FR: /fr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, el-GR: /gr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, hi-IN: /hi/blog/import-google-sheet-contacts-to-brevo-apps-script.md, id-ID: /in/blog/import-google-sheet-contacts-to-brevo-apps-script.md, hr-HR: /hr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, hu-HU: /hu/blog/import-google-sheet-contacts-to-brevo-apps-script.md, it-IT: /it/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ja-JP: /jp/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ko-KR: /kr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ar-SA: /ar/blog/import-google-sheet-contacts-to-brevo-apps-script.md, nl-NL: /nl/blog/import-google-sheet-contacts-to-brevo-apps-script.md, no-NO: /no/blog/import-google-sheet-contacts-to-brevo-apps-script.md, pl-PL: /pl/blog/import-google-sheet-contacts-to-brevo-apps-script.md, pt-BR: /pt/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ro-RO: /ro/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ru-RU: /ru/blog/import-google-sheet-contacts-to-brevo-apps-script.md, sv-SE: /se/blog/import-google-sheet-contacts-to-brevo-apps-script.md, zh-CN: /zh-cn/blog/import-google-sheet-contacts-to-brevo-apps-script.md, sl-SI: /sl/blog/import-google-sheet-contacts-to-brevo-apps-script.md, sr-Latn-RS: /sr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, th-TH: /th/blog/import-google-sheet-contacts-to-brevo-apps-script.md, tr-TR: /tr/blog/import-google-sheet-contacts-to-brevo-apps-script.md, ur-PK: /ur/blog/import-google-sheet-contacts-to-brevo-apps-script.md, vi-VN: /vi/blog/import-google-sheet-contacts-to-brevo-apps-script.md, sk-SK: /sk/blog/import-google-sheet-contacts-to-brevo-apps-script.md}
---

[Datensynchronisierung](https://tajo.io/de/blog/?category=data-sync)

# Google-Sheets-Kontakte mit Apps Script in Brevo importieren

Ein wiederholbares Google-Apps-Script-Beispiel mit Einwilligungsprüfung, Feldzuordnung, Schutz vor erneuter Anmeldung und sichtbaren API-Fehlern.

google apps script brevo

Wie importierst du Sheets-Kontakte in Brevo?

Lege in Google Sheets die Spalten email, firstName, lastName und consent an. Hinterlege BREVO\_API\_KEY in den Skripteigenschaften und die Listen-ID im Code. syncSheetToBrevo berücksichtigt nur Zeilen mit Einwilligung, prüft Abmeldungen in Brevo, entfernt doppelte Adressen und stoppt bei API-Fehlern. Das ist ein selbst gepflegtes Apps Script, kein nativer Connector.

[Mehr erfahren](https://tajo.io/de/blog/import-google-sheet-contacts-to-brevo-apps-script/#sheet-und-liste-vorbereiten)

| Quellfeld | Zielfeld | Beispielwert |
| --- | --- | --- |
| `email` | `email` | `alex@example.com` |
| `firstName` | `FNAME` | `Alex` |
| `lastName` | `LNAME` | `Example` |

Ein Sheet mit zwölf Event-Anmeldungen lässt sich einmalig leicht exportieren. Beim zweiten Import steht eine Adresse doppelt darin, während sich ein anderer Kontakt seitdem in Brevo abgemeldet hat. Genau diese beiden Fälle muss ein wiederholbarer Import beachten.

Das folgende Skript verwendet [Brevos Endpunkte zum Lesen, Anlegen und Aktualisieren von Kontakten](https://developers.brevo.com/reference/create-contact). Es ist für kleine, geplante Importe gedacht, nicht für Massenimporte oder einen nativen Google-Sheets-Connector. Es versendet keine E-Mails. Für Einwilligungsnachweise und Betrieb bist du selbst verantwortlich.

## Sheet und Liste vorbereiten

Lege einen Tab `Contacts` mit genau diesen Spalten an. Verwende Checkboxen für `consent` und setze `TRUE` nur, wenn du für die beabsichtigte Marketing-Nutzung eine Einwilligung hast.

| email | firstName | lastName | consent |
| --- | --- | --- | --- |
| [jane@example.com](mailto:jane@example.com) | Jane | Doe | TRUE |
| [alex@example.com](mailto:alex@example.com) | Alex | Lee | FALSE |

Erstelle oder wähle eine Brevo-Liste und trage ihre numerische ID unten als `LIST_ID` ein. Prüfe, ob die Kontaktattribute `FNAME` und `LNAME` in Brevo existieren. Nur diese beiden Felder werden zugeordnet. Bewahre den Nachweis auf, wann und wie die Person zugestimmt hat. Eine `TRUE`-Zelle allein ist kein Nachweis.

Öffne **Erweiterungen → Apps Script → Projekteinstellungen → Skripteigenschaften** und füge `BREVO_API_KEY` hinzu. Skripteigenschaften sind für Nutzer:innen des Skripts gemeinsam zugänglich. Begrenze daher die Bearbeitungsrechte. Schreibe den Schlüssel weder in eine Zelle noch in den Quelltext. Beachte die [Zugriffsregeln von Google](https://developers.google.com/apps-script/guides/properties).

## Das Skript

Ersetze `Code.gs` durch dieses Beispiel und setze vor dem ersten Lauf `LIST_ID`.

```javascript
const LIST_ID = 42; // Replace with your existing Brevo list ID.
const TAB = 'Contacts';
const BASE = 'https://api.brevo.com/v3';

function syncSheetToBrevo() {
  const key = PropertiesService.getScriptProperties().getProperty('BREVO_API_KEY');
  if (!key) throw new Error('Set BREVO_API_KEY in Script Properties first');
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TAB);
  if (!sheet) throw new Error('Contacts tab missing');
  const values = sheet.getDataRange().getValues();
  const headers = values.shift().map(h => String(h).trim());
  const columns = ['email', 'firstName', 'lastName', 'consent']
    .map(name => headers.indexOf(name));
  if (columns.includes(-1)) throw new Error('Required header missing');
  const [emailCol, firstCol, lastCol, consentCol] = columns;
  const seen = new Set();
  const result = { written: 0, skipped: 0 };

  for (const row of values) {
    const email = String(row[emailCol] || '').trim().toLowerCase();
    if (row[consentCol] !== true || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || seen.has(email)) {
      result.skipped++;
      continue;
    }
    seen.add(email);
    const path = '/contacts/' + encodeURIComponent(email);
    const current = brevo_(key, path, 'get');
    if (current.code !== 200 && current.code !== 404) {
      throw new Error(`Contact read failed (${current.code}) for row ${result.written + result.skipped + 2}`);
    }
    if (current.code === 200) {
      const contact = JSON.parse(current.text);
      if (contact.emailBlacklisted || (contact.listUnsubscribed || []).includes(LIST_ID)) {
        result.skipped++;
        continue;
      }
    }
    const attributes = {};
    if (row[firstCol] !== '') attributes.FNAME = String(row[firstCol]);
    if (row[lastCol] !== '') attributes.LNAME = String(row[lastCol]);
    const body = { attributes, listIds: [LIST_ID] };
    const saved = current.code === 404
      ? brevo_(key, '/contacts', 'post', { email, ...body })
      : brevo_(key, path, 'put', body);
    if (![201, 204].includes(saved.code)) {
      throw new Error(`Contact write failed (${saved.code}) for ${email}: ${saved.text.slice(0, 200)}`);
    }
    result.written++;
  }
  Logger.log(JSON.stringify(result));
  return result;
}

function brevo_(key, path, method, body) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const response = UrlFetchApp.fetch(BASE + path, {
      method,
      contentType: 'application/json',
      headers: { 'api-key': key },
      payload: body ? JSON.stringify(body) : undefined,
      muteHttpExceptions: true,
    });
    const code = response.getResponseCode();
    if (code !== 429) return { code, text: response.getContentText() };
    if (attempt === 2) return { code, text: response.getContentText() };
    const reset = Object.entries(response.getHeaders()).find(
      ([name]) => name.toLowerCase() === 'x-sib-ratelimit-reset'
    );
    const raw = Number(reset && reset[1]);
    const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt;
    const waitSeconds = seconds + 2 ** attempt;
    if (waitSeconds > 60) return { code, text: response.getContentText() };
    Utilities.sleep(waitSeconds * 1000);
  }
}
```

Speichere das Projekt, wähle `syncSheetToBrevo` und klicke auf **Ausführen**. Google fragt nach Berechtigungen für Sheets und externe Anfragen. Unter **Ausführungen** findest du am Ende das Protokoll `{written, skipped}`. Ein 404 beim Lesen führt zum Anlegen, ein 200 zum Aktualisieren. Andere Lesefehler stoppen den Lauf. Ein erfolgreicher Schreibvorgang bedeutet, dass die Kontaktanfrage abgeschlossen ist; es wurde keine Kampagne versendet.

Das Skript entfernt doppelte E-Mail-Adressen innerhalb eines Laufs und fragt Brevo beim nächsten Lauf erneut ab. Es setzt `emailBlacklisted` nie auf `false` und ändert keine E-Mail-Adresse. Meldet Brevo eine globale Sperre oder eine Abmeldung von dieser Liste, überspringt es die Zeile auch bei `TRUE` im Sheet. Verwende es nicht, um eine Abmeldung rückgängig zu machen. [Brevo warnt](https://developers.brevo.com/reference/update-contact), dass eine Adressänderung bei gesperrten Kontakten die Sperre aufheben kann.

## Einen Lauf prüfen und dann planen

Verwende eine Testliste und eigene Testkontakte. Starte das Skript mit einer `TRUE`-Zeile, einer `FALSE`-Zeile und einer doppelten Adresse: Erwartet werden ein Schreibvorgang und zwei übersprungene Zeilen. Melde den Testkontakt in Brevo ab und starte erneut: Für ihn darf kein Schreibvorgang erfolgen. Setze für einen Fehlertest kurz eine ungültige `LIST_ID`, prüfe den HTTP-Fehler unter **Ausführungen**, stelle die ID wieder her und starte erneut. Nutze dafür keine echten Kundendaten. Der Code dieses Artikels wurde lokal mit simulierten Brevo-Antworten geprüft; es gab keinen Live-Schreibtest.

Für automatische Läufe wähle **Apps Script → Trigger → Trigger hinzufügen**, `syncSheetToBrevo` und einen zeitgesteuerten Intervall. [Installierbare Trigger laufen unter dem Konto ihrer Ersteller:innen](https://developers.google.com/apps-script/guides/triggers/installable). Dieses Konto braucht dauerhaft Zugriff auf das Sheet. Beginne bei kleinen Sheets täglich und kontrolliere fehlgeschlagene Ausführungen. Google [begrenzt Laufzeit und URL-Fetch-Aufrufe](https://developers.google.com/apps-script/guides/services/quotas). Teile große Quellen in begrenzte Jobs auf.

Bei 429 liest das Skript `x-sib-ratelimit-reset`, versucht höchstens zweimal erneut und gibt bei einer nötigen Wartezeit über 60 Sekunden 429 für einen späteren Lauf zurück. Andere Fehler stoppen den Lauf. Beim nächsten manuellen oder geplanten Start beginnt es wieder oben. Zwischen Lesen und Schreiben kann sich ein Kontakt abmelden; dieses Beispiel eignet sich nur, wenn dieses kleine Zeitfenster akzeptabel ist. Für strengere Sperrgarantien brauchst du einen ereignisbasierten Abgleich mit einer maßgeblichen Einwilligungsquelle und einer letzten Prüfung vor dem Versand.

Für große Sheets bietet Brevo einen [asynchronen Import-Endpunkt](https://developers.brevo.com/reference/import-contacts). Dessen `processId` bestätigt die Annahme eines Jobs, nicht seinen Abschluss. Prüfe das Prozessergebnis, bevor du einen Massenimport als erfolgreich meldest. Bei diesem kleinen Kontaktpfad ist ein Fehler direkt an der betroffenen Zeile sichtbar.

## Häufig gestellte Fragen

**Brauche ich dafür einen nativen Google-Sheets-Connector?**

Nein. Das Beispiel läuft als an das Sheet gebundenes Google Apps Script und ruft die Brevo-Kontakt-API auf. Du pflegst Skript, API-Schlüssel und Zeittrigger selbst.

**Welche Zeilen werden an Brevo übertragen?**

Nur Zeilen mit TRUE in consent und gültiger E-Mail-Adresse. Das Skript entfernt doppelte Adressen pro Lauf und prüft Brevo vor jedem Schreibvorgang.

**Meldet ein erneuter Lauf abgemeldete Kontakte wieder an?**

Das Skript überspringt Kontakte, die Brevo als emailBlacklisted oder als von der Zielliste abgemeldet meldet. Es setzt emailBlacklisted nie auf false und ändert keine E-Mail-Adresse. Prüfe zusätzliche Einwilligungsquellen vor dem produktiven Einsatz.

**Wie wiederhole ich einen fehlgeschlagenen Lauf?**

Lies den Fehler in den Apps-Script-Ausführungen, behebe die Ursache und starte dieselbe Funktion erneut. Die Schreibvorgänge verwenden dieselbe E-Mail-Adresse und Listen-ID; eine zuvor erfolgreiche Zeile erzeugt deshalb keinen zweiten Kontakt.

## Verwandte Artikel

### [CSV- oder Excel-Kontakte mit einem Skript zu Brevo importieren (Python, Node.js, cURL)](https://tajo.io/de/blog/import-csv-contacts-to-brevo-with-a-script/)

30\. Apr. 2026 · 5 Min. Lesezeit

CSV → Brevo

### [Excel-Kontakte mit einem VBA-Makro zu Brevo schicken (und die Office-Scripts-Alternative)](https://tajo.io/de/blog/import-excel-contacts-to-brevo-with-vba-macro/)

30\. Apr. 2026 · 6 Min. Lesezeit

Microsoft Excel → Brevo

### [Brevo-Integrationen: eine überprüfbare Verbindung wählen](https://tajo.io/de/blog/brevo-connector-guide/)

19\. Aug. 2026 · 4 Min. Lesezeit

6 Abschnitte
