Kako uvesti CSV ili Excel kontakte u Brevo pomoću skripte (Python, Node.js, cURL)
Skupno uvezite kontakte iz CSV ili Excel datoteke u Brevo pomoću API-ja import-contacts. Uključuje gotove skripte u Pythonu i Node.js, cURL jednoredac, obradu pogrešaka i savjete za datoteke veće od 10 MB.
Ako ste ikad trebali poslati nekoliko tisuća kontakata iz proračunske tablice u Brevo, znate kako brzo postaje mučan ručni put kroz sučelje: odaberite datoteku, mapirajte stupce, odaberite popis, čekajte, ponovite. Skripta isti posao obavi u sekundama, i (što je još važnije) možete je pokrenuti ponovno po rasporedu, nakon izvoza iz baze podataka ili kad god vaš CRM izbaci svježi CSV.
Ovaj vodič pokriva API endpoint koji vam Brevo daje upravo za to, s kompletnim radnim skriptama u Pythonu, Node.js i cURL jednorecu. Sve u ovom vodiču pogađa POST /v3/contacts/import.
Endpoint na prvi pogled
POST https://api.brevo.com/v3/contacts/importContent-Type: application/jsonapi-key: YOUR_API_KEY
{ "listIds": [42], "updateExistingContacts": true, "emailBlacklist": false, "smsBlacklist": false}Odgovor stiže brzo:
{ "processId": 78 }To je 202 Accepted. Brevo je prihvatio uvoz i obrađuje ga u pozadini. processId je vaš ručak za praćenje ako želite ispitivati dovršetak ili postaviti notifyUrl webhook.
Nekoliko stvari koje vrijedi znati prije nego što pišete kod:
- Tijelo može biti CSV (
fileBody), JSON (jsonBody) ili udaljeni URL (fileUrl). Odaberite jedno. CSV oblik koristi točku-zarez (;) za razdvajanje stupaca, ne zarez, to je česta zamka. fileBodyijsonBodyograničeni su na 10 MB. Brevo preporučuje da ostanete oko 8 MB radi sigurnosti. Za sve veće, učitajte datoteku na S3, GCS ili bilo koji HTTPS host i proslijeditefileUrl.- Prilagođeni atributi koji ne postoje u vašem računu tiho se ignoriraju. Stvorite ih u Brevo sučelju (ili putem Attributes API-ja) prije uvoza redaka koji ih koriste, inače podaci jednostavno nestaju.
updateExistingContactsje prema zadanim postavkamatrue. Ako postavite nafalse, Brevo preskače kontakte čija e-pošta već postoji, korisno za poslove tipa “dodaj samo nove”.emptyContactsAttributeskontrolira hoće li prazne ćelije u vašem CSV-u izbrisati postojeće vrijednosti. Zadano jefalse(prazne ćelije se ignoriraju). Postavite natrueako je vaš CSV izvor istine i želite da prazna polja očiste zastarjele podatke.
Nabavite API ključ
Prijavite se u Brevo → Postavke → SMTP & API → API Keys → stvorite novi ključ. Izgleda kao xkeysib-.... Proslijedit ćete ga kao HTTP zaglavlje api-key u svakom zahtjevu. Tretirajte ga kao lozinku, ima puni pristup čitanju i pisanju na vašem računu.
Čista praksa: stavite ga u varijablu okruženja kako nikada ne bi završio u vašem izvornom kodu.
export BREVO_API_KEY="xkeysib-..."Python: pročitajte CSV, pošaljite ga u Brevo
Ovo je najjednostavnija moguća skripta: pročitajte lokalni CSV pomoću standardne biblioteke i pošaljite tijelo izravno u Brevo. Osim requests, ne treba vam nijedan SDK treće strane.
import csvimport ioimport osimport sysimport requests
API_KEY = os.environ["BREVO_API_KEY"]LIST_ID = 42 # the Brevo list to add contacts toINPUT_FILE = sys.argv[1] if len(sys.argv) > 1 else "contacts.csv"
# Brevo wants semicolon-separated CSV. If your file uses commas (most do),# convert it on the fly so you don't have to edit the source spreadsheet.def to_brevo_csv(path: str) -> str: with open(path, newline="", encoding="utf-8") as f: reader = csv.reader(f) # default: comma-separated out = io.StringIO() writer = csv.writer(out, delimiter=";") for row in reader: writer.writerow(row) return out.getvalue()
body = { "fileBody": to_brevo_csv(INPUT_FILE), "listIds": [LIST_ID], "updateExistingContacts": True, "emptyContactsAttributes": False, "emailBlacklist": False, "smsBlacklist": False,}
resp = requests.post( "https://api.brevo.com/v3/contacts/import", json=body, headers={"api-key": API_KEY, "Content-Type": "application/json"}, timeout=60,)
if resp.status_code != 202: print(f"Import failed: {resp.status_code} {resp.text}") sys.exit(1)
process_id = resp.json()["processId"]print(f"Import accepted. Process ID: {process_id}")Pokrenite:
python import_csv_to_brevo.py contacts.csvPrvi redak vašeg CSV-a je zaglavlje. Imena stupaca mapiraju se na Brevo atribute prema velikim slovima, točnom podudaranju: EMAIL, FIRSTNAME, LASTNAME, plus svaki prilagođeni atribut koji ste definirali. EMAIL je obavezan.
EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITYTo je cijeli tijek. Skripta se vraća unutar sekunde, stvarni uvoz radi se na strani poslužitelja i traje od nekoliko sekundi do nekoliko minuta, ovisno o količini.
Excel datoteke: tri realna puta
Brevo endpoint za uvoz ne čita .xlsx izravno, pa imate tri stvarne opcije, ovisno o tome gdje posao treba biti:
- Pokrenite VBA makronaredbu unutar radne knjige, sinkronizacija jednim klikom iz samog Excela, bez vanjske skripte. Pravi odgovor kad datoteka živi na desktopu i vaš tim želi gumb na listi. Cijeli kod u vodiču za VBA makronaredbu Excel-u-Brevo.
- Office Scripts + Power Automate, ako je radna knjiga u OneDriveu/SharePointu i želite zakazanu, neaduzevu sinkronizaciju. Također obrađeno u Excel vodiču.
- Pretvorite
.xlsxu CSV iz skripte, što pokriva ostatak ovog odjeljka. Najbolje ako već pokrećete Python ili Node na poslužitelju i samo trebate povući radnu knjigu jednom dnevno.
Za opciju 3, pandas to radi u jednoj liniji:
# pip install pandas openpyxlimport pandas as pd
def xlsx_to_brevo_csv(path: str, sheet_name: str | int = 0) -> str: df = pd.read_excel(path, sheet_name=sheet_name) return df.to_csv(sep=";", index=False)
body["fileBody"] = xlsx_to_brevo_csv("contacts.xlsx")Ako ne želite pandas kao ovisnost, dovoljan je openpyxl sam:
from openpyxl import load_workbookimport csv, io
def xlsx_to_brevo_csv(path: str) -> str: wb = load_workbook(path, read_only=True) ws = wb.active out = io.StringIO() writer = csv.writer(out, delimiter=";") for row in ws.iter_rows(values_only=True): writer.writerow(["" if v is None else v for v in row]) return out.getvalue()Node.js: isti posao, službeni SDK
Brevo objavljuje @getbrevo/brevo za Node. Brine se o autentifikaciji, ponovnim pokušajima i tipiziranom obliku zahtjeva:
// npm install @getbrevo/brevoimport fs from 'node:fs/promises';import { BrevoClient } from '@getbrevo/brevo';
const API_KEY = process.env.BREVO_API_KEY;const LIST_ID = 42;const INPUT_FILE = process.argv[2] ?? 'contacts.csv';
const client = new BrevoClient({ apiKey: API_KEY });
// Read the CSV and convert commas to semicolons if neededconst raw = await fs.readFile(INPUT_FILE, 'utf-8');const csv = raw.includes(';') ? raw : raw.replace(/,/g, ';');
const result = await client.contacts.importContacts({ fileBody: csv, listIds: [LIST_ID], updateExistingContacts: true, emptyContactsAttributes: false,});
console.log(`Import accepted. Process ID: ${result.processId}`);Ili, ako ne želite SDK, običan fetch radi na isti način:
const resp = await fetch('https://api.brevo.com/v3/contacts/import', { method: 'POST', headers: { 'api-key': process.env.BREVO_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ fileBody: csv, listIds: [42], updateExistingContacts: true, }),});
if (resp.status !== 202) { throw new Error(`Import failed: ${resp.status} ${await resp.text()}`);}
const { processId } = await resp.json();console.log(`Process ID: ${processId}`);cURL: jednoredac za ad-hoc uvoze
Kad samo želite testirati endpoint ili ručno gurnuti malu datoteku:
# Convert commas to semicolons, then send the file inlinecsv=$(sed 's/,/;/g' contacts.csv | jq -Rs .)
curl -X POST https://api.brevo.com/v3/contacts/import \ -H "api-key: $BREVO_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"fileBody\": $csv, \"listIds\": [42], \"updateExistingContacts\": true}"jq -Rs . proguta cijelu datoteku i JSON-escape je, što vas spašava od ručnog escapeanja prijeloma redaka i navodnika.
Datoteke veće od 10 MB: koristite fileUrl
fileBody je ograničen na 10 MB. Ako je vaš dump kontakata veći, hostajte datoteku na URL-u koji Brevo može dohvatiti i proslijedite ga:
body = { "fileUrl": "https://files.example.com/contacts/2026-04-30.csv", "listIds": [42], "updateExistingContacts": True,}Sve što je dostupno preko javnog HTTPS URL-a radi, prepotpisani S3 URL-ovi, GCS, vaš vlastiti statički host, čak i GitHub raw URL za jednokratne uvoze. Brevo dohvaća datoteku s vašeg URL-a, a zatim pokreće uvoz. Prihvaćeni formati: .csv, .txt, .json.
Pošaljite JSON umjesto CSV-a
Ako kontakte ionako već povlačite iz baze podataka, ne morate ići okolo preko CSV-a, pošaljite ih izravno kao JSON:
body = { "jsonBody": [ { "attributes": { "FIRSTNAME": "Jane", "LASTNAME": "Doe", "COMPANY": "Acme", }, }, { "attributes": { "FIRSTNAME": "John", "LASTNAME": "Smith", "COMPANY": "Globex", }, }, ], "listIds": [42],}Isto ograničenje od 10 MB. Isto asinkrono ponašanje.
Ispitivanje dovršenosti
Uvoz je asinkron, processId je vaš ručak za praćenje. Postoji odvojen endpoint za provjeru statusa procesa:
def wait_for_import(process_id: int, timeout_s: int = 600) -> dict: import time deadline = time.time() + timeout_s while time.time() < deadline: r = requests.get( f"https://api.brevo.com/v3/processes/{process_id}", headers={"api-key": API_KEY}, timeout=30, ) r.raise_for_status() status = r.json() if status["status"] in ("completed", "failed"): return status time.sleep(5) raise TimeoutError(f"Import {process_id} did not finish in {timeout_s}s")
result = wait_for_import(process_id)print(f"Import {result['status']}: {result}")Za dugotrajne poslove, čistiji obrazac je notifyUrl: proslijedite HTTPS endpoint na koji će Brevo poslati POST kad uvoz završi i preskočite ispitivanje.
body["notifyUrl"] = "https://your-app.example.com/webhooks/brevo-import"Česte pogreške i kako ih popraviti
400 Bad Request bez očitog uzroka. Gotovo uvijek točke-zarezi protiv zareza u CSV-u. Brevo uvoz očekuje ;, ne ,. Dvostruko provjerite fileBody nakon koraka pretvorbe.
Vrijednosti prilagođenih atributa nestaju. Atribut ne postoji u vašem računu. Stvorite ga pod Kontakti → Postavke → Atributi kontakta prije uvoza ili koristite Attributes API da ga stvorite kao dio svoje skripte.
401 Unauthorized. Pogrešno ime zaglavlja. To je api-key (mala slova, crtica), ne Authorization ili X-API-Key.
Uvoz “uspio”, ali kontakti se nisu pojavili u popisu. Provjerite sadrži li listIds ispravan popis. Također: ako je updateExistingContacts na false i kontakti već postoje, Brevo ih tiho preskače umjesto da ih ponovno doda na popis.
Neki redci uvezeni, neki ne. Brevo vam šalje izvještaj o pogreškama po retku e-poštom nakon završetka uvoza (osim ako postavite disableNotification: true). Izvještaj vam govori koji su redci imali nevažeće e-pošte, nedostajuća obavezna polja ili probleme s formatiranjem.
Kada skripta, a kada sučelje
Sučelje je u redu za jednokratne uvoze ispod tisuću redaka. Skripta pobjeđuje čim:
- Uvozite više od jednom (npr. tjedni izvoz iz vašeg CRM-a)
- Izvorni podaci trebaju čišćenje prije nego što sleti u Brevo (deduplikacija, formatiranje telefonskih brojeva, dijeljenje punih imena u ime i prezime)
- Želite da se pokreće po rasporedu bez da netko pritišće gumbe
- Datoteka je veća od zone udobnosti sučelja
Omotajte skriptu iznad u cron job ili GitHub Action i imate automatsku sinkronizaciju kontakata. Sljedeći post u ovoj seriji pokazuje kako napraviti istu stvar izravno iz Google Sheeta pomoću Apps Script, bez poslužitelja.