---
title: "Uvoz kontakata iz Google Sheetsa u Brevo pomoću Apps Scripta"
description: "Ponovljiv primer Google Apps Scripta koji proverava saglasnost, mapira polja, čuva odjave i prikazuje API greške."
url: https://tajo.io/sr/blog/import-google-sheet-contacts-to-brevo-apps-script/
markdown: https://tajo.io/sr/blog/import-google-sheet-contacts-to-brevo-apps-script.md
lang: sr-Latn-RS
author: "Set Noa"
author_url: https://tajo.io/sr/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, de-DE: /de/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, 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}
---

[Sinhronizacija podataka](https://tajo.io/sr/blog/?category=data-sync)

# Uvoz kontakata iz Google Sheetsa u Brevo pomoću Apps Scripta

Ponovljiv primer Google Apps Scripta koji proverava saglasnost, mapira polja, čuva odjave i prikazuje API greške.

google apps script brevo

Kako sinhronizovati kontakte iz Google Sheets u Brevo?

Dodajte kolone email, firstName, lastName i consent u list. Podesite BREVO\_API\_KEY u svojstvima skripte i ID liste u kodu, pa pokrenite syncSheetToBrevo. Skripta obrađuje samo redove sa saglasnošću, proverava odjave u Brevu, uklanja duplikate i staje kod API greške. To je Apps Script koji sami održavate, a ne ugrađeni konektor.

[Saznajte više](https://tajo.io/sr/blog/import-google-sheet-contacts-to-brevo-apps-script/#pripremite-list-i-listu)

| Izvorno polje | Odredišno polje | Primer vrednosti |
| --- | --- | --- |
| `email` | `email` | `alex@example.com` |
| `firstName` | `FNAME` | `Alex` |
| `lastName` | `LNAME` | `Example` |

List sa dvanaest prijava za događaj lako je jednom izvesti. Pri drugom uvozu jedna adresa se ponavlja, a druga osoba se u međuvremenu odjavila u Brevu. Ponovljiv uvoz mora da prepozna oba slučaja.

Skripta koristi [Brevo krajnje tačke za čitanje, pravljenje i ažuriranje kontakata](https://developers.brevo.com/reference/create-contact). Namenjena je malim zakazanim uvozima, ne masovnom učitavanju ili ulozi ugrađenog konektora za Google Sheets. Ne šalje e-poštu. Za dokaz saglasnosti i rad skripte odgovorni ste vi.

## Pripremite list i listu

Napravite karticu `Contacts` sa tačno ovim zaglavljima. Za `consent` koristite polja za potvrdu i stavite `TRUE` samo kad postoji saglasnost za nameravani marketing.

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

Napravite ili izaberite Brevo listu, pa njen brojčani ID upišite u `LIST_ID`. Proverite postoje li atributi `FNAME` i `LNAME` u Brevu. Samo ta polja se mapiraju. Dokaz o vremenu i načinu davanja saglasnosti čuvajte odvojeno ako list to ne pokazuje. Ćelija `TRUE` nije dokaz.

U **Proširenja → Apps Script → Podešavanja projekta → Svojstva skripte** dodajte `BREVO_API_KEY`. Svojstva dele korisnici skripte, pa ograničite pravo uređivanja. Ključ ne upisujte u ćeliju ni u kod. Pročitajte [Googleova pravila pristupa](https://developers.google.com/apps-script/guides/properties).

## Skripta

Zamenite `Code.gs` ovim primerom i podesite `LIST_ID` pre prvog pokretanja.

```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);
  }
}
```

Sačuvajte, izaberite `syncSheetToBrevo` i kliknite **Pokreni**. Google traži pristup Sheetsu i spoljnim zahtevima. U **Izvršavanjima** pogledajte završni zapis `{written, skipped}`. Odgovor 404 pri čitanju vodi pravljenju kontakta, 200 ažuriranju. Druge greške zaustavljaju rad. Uspešan upis potvrđuje zahtev za kontakt, a ne slanje kampanje.

Skripta uklanja ponovljene adrese u jednom pokretanju i ponovo proverava Brevo u sledećem. Nikad ne postavlja `emailBlacklisted` na `false` i ne menja e-mail. Ako Brevo prijavi blokadu ili odjavu sa te liste, red se preskače i uz `TRUE` u listu. Ne koristite skriptu da poništite odjavu. [Brevo upozorava](https://developers.brevo.com/reference/update-contact) da promena adrese blokiranog kontakta može skinuti blokadu.

## Proverite pokretanje pre zakazivanja

Koristite probnu listu i kontakte koje nadzirete. Pokrenite sa jednim redom `TRUE`, jednim `FALSE` i ponovljenom adresom: očekujte jedan upis i dva preskočena reda. Odjavite probni kontakt u Brevu i ponovite: za njega ne sme biti upisa. Za test greške privremeno postavite nevažeći `LIST_ID`, proverite HTTP grešku u **Izvršavanjima**, vratite ID i pokušajte opet. Ne testirajte na pravim podacima kupaca. Kod je lokalno proveravan i sa simuliranim Brevo odgovorima, bez stvarnog upisa.

Za raspored otvorite **Apps Script → Okidači → Dodaj okidač**, izaberite `syncSheetToBrevo` i vremenski interval. [Okidači rade pod nalogom osobe koja ih je napravila](https://developers.google.com/apps-script/guides/triggers/installable); taj nalog mora da zadrži pristup listu. Za mali list krenite jednom dnevno i pratite greške. Google [ograničava trajanje i URL Fetch pozive](https://developers.google.com/apps-script/guides/services/quotas); velike izvore podelite na ograničene zadatke.

Pri 429 skripta čita `x-sib-ratelimit-reset` i ponavlja najviše dvaput. Ako potrebno čekanje premašuje 60 sekundi, vraća 429 za kasnije pokretanje. Druge greške zaustavljaju rad; sledeći put kreće od početka. Kontakt može da se odjavi između čitanja i upisa. Primer koristite samo ako je taj kratki prozor prihvatljiv. Jača garancija traži usklađivanje događaja, pouzdan izvor saglasnosti i poslednju proveru pre slanja.

Za veće listove [asinhrona Brevo tačka za uvoz](https://developers.brevo.com/reference/import-contacts) vraća `processId`. To potvrđuje prihvatanje zadatka, ne završetak. Proverite rezultat pre nego što objavite uspeh. Put po pojedinačnim kontaktima pokazuje grešku na odgovarajućem redu.

## Često Postavljana Pitanja

**Da li je potreban ugrađeni konektor za Google Sheets?**

Ne. Primer radi u Google Apps Script projektu vezanom za list i poziva Brevo API za kontakte. Skriptu, API ključ i zakazani okidač održavate sami.

**Koji redovi se šalju u Brevo?**

Samo oni sa TRUE u consent i ispravnom e-mail adresom. Skripta uklanja duplikate pri svakom pokretanju i proverava Brevo pre upisa.

**Da li novo pokretanje ponovo prijavljuje odjavljen kontakt?**

Skripta preskače kontakte koje Brevo označi kao emailBlacklisted ili odjavljene sa ciljne liste. Nikada ne šalje emailBlacklisted: false i ne menja adresu. Pre produkcije proverite i druge izvore saglasnosti.

**Kako ponoviti neuspešno pokretanje?**

Pročitajte grešku u izvršavanjima Apps Scripta, otklonite uzrok i ponovo pokrenite istu funkciju. Upisi koriste istu adresu i ID liste, pa već uspešan red ne pravi drugi kontakt.

## Povezani članci

### [Kako uvesti kontakte iz CSV ili Excel fajla u Brevo skriptom (Python, Node.js, cURL)](https://tajo.io/sr/blog/import-csv-contacts-to-brevo-with-a-script/)

30\. apr 2026. · 5 min čitanja

CSV → Brevo

### [Kako gurnuti Excel kontakte u Brevo pomoću VBA makroa (i alternativa Office Scripts)](https://tajo.io/sr/blog/import-excel-contacts-to-brevo-with-vba-macro/)

30\. apr 2026. · 6 min čitanja

Microsoft Excel → Brevo

### [Brevo integracije: izaberite vezu koju možete da proverite](https://tajo.io/sr/blog/brevo-connector-guide/)

19\. avg 2026. · 4 min čitanja

6 odeljaka
