Kako uvesti kontakte iz CSV ili Excel fajla u Brevo skriptom (Python, Node.js, cURL)
Masovno uvezite kontakte iz CSV ili Excel fajla u Brevo pomoću API-ja import-contacts. Sadrži spremne skripte u Pythonu i Node.js, jednu liniju cURL, obradu grešaka i savete za fajlove veće od 10 MB.
Ako ste ikada morali da pošaljete par hiljada kontakata iz tabele u Brevo, znate da ručna staza kroz korisnički interfejs brzo postaje mučna: izaberite fajl, mapirajte kolone, izaberite listu, čekajte, ponovite. Skripta isti posao radi za sekunde i, što je važnije, možete je pokretati ponovo po rasporedu, posle izvoza iz baze ili svaki put kada vaš CRM izbaci svežu CSV.
Ovaj vodič pokriva API endpoint koji vam Brevo daje upravo za to, sa potpuno funkcionalnim skriptama u Pythonu, Node.js i jednom linijom cURL. Sve u ovom vodiču pogađa POST /v3/contacts/import.
Endpoint ukratko
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ša ručka za praćenje ako želite da proveravate završetak ili postavite webhook notifyUrl.
Nekoliko stvari vrednih pomena pre nego što počnete da pišete kod:
- Telo može biti CSV (
fileBody), JSON (jsonBody) ili udaljeni URL (fileUrl). Izaberite jedno. CSV oblik koristi tačku-zarez (;) za razdvajanje kolona, ne zareze. To je česta zamka. fileBodyijsonBodysu ograničeni na 10 MB. Brevo preporučuje da radi sigurnosti ostanete oko 8 MB. Za bilo šta veće, otpremite fajl na S3, GCS ili bilo koji HTTPS hosting i umesto toga prosleditefileUrl.- Prilagođeni atributi koji ne postoje u vašem nalogu se tiho ignorišu. Kreirajte ih u korisničkom interfejsu Brevo (ili preko Attributes API) pre nego što uvezete redove koji ih koriste, inače podaci jednostavno nestaju.
updateExistingContactsje podrazumevanotrue. Ako ga postavite nafalse, Brevo preskače kontakte čiji imejl već postoji. Korisno za poslove tipa „samo dodaj nove”.emptyContactsAttributeskontroliše da li prazne ćelije u vašem CSV-u brišu postojeće vrednosti. Podrazumevanofalse(prazne ćelije se ignorišu). Postavite natrueako je vaš CSV izvor istine i želite da prazne vrednosti očiste zastarele podatke.
Pribavite API ključ
Prijavite se na Brevo, idite na Settings → SMTP & API → API Keys i kreirajte novi ključ. Izgleda kao xkeysib-.... Prosleđivaćete ga kao HTTP zaglavlje api-key pri svakom zahtevu. Tretirajte ga kao lozinku. Ima pun pristup za čitanje i pisanje na vašem nalogu.
Čist obrazac: stavite ga u promenljivu okruženja da nikad ne završi u vašem izvornom drvetu.
export BREVO_API_KEY="xkeysib-..."Python: pročitajte CSV i pošaljite ga u Brevo
Ovo je najjednostavnija moguća skripta: pročitajte lokalni CSV pomoću standardne biblioteke i telo pošaljite pravo u Brevo. Nije potreban nijedan SDK treće strane osim requests.
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 je:
python import_csv_to_brevo.py contacts.csvPrvi red vašeg CSV-a je zaglavlje. Imena kolona se mapiraju na Brevo atribute preko velikih slova, tačnog poklapanja: EMAIL, FIRSTNAME, LASTNAME, plus svaki prilagođeni atribut koji ste definisali. EMAIL je obavezan.
EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITYTo je ceo tok. Skripta se vraća za sekundu. Sam uvoz radi na strani servera i traje od nekoliko sekundi do nekoliko minuta u zavisnosti od količine.
Excel fajlovi: tri razumna puta
Brevo endpoint za uvoz ne čita .xlsx direktno, pa imate tri stvarne opcije u zavisnosti od toga gde posao mora da živi:
- Pokrenite VBA makro unutar radne sveske. Sinhronizacija iz samog Excela jednim klikom, bez spoljne skripte. Ovo je pravi odgovor kada fajl živi na desktopu i vaš tim želi dugme na listu. Pun kod u vodiču za Excel-u-Brevo VBA makro.
- Office Scripts + Power Automate. Ako radna sveska živi u OneDrive/SharePoint i želite nenadgledanu sinhronizaciju po rasporedu. Takođe pokriveno u Excel vodiču.
- Konvertujte
.xlsxu CSV iz skripte. Ovo pokriva ostatak ovog odeljka. Najbolje ako već pokrećete Python ili Node na serveru i samo treba da povučete radnu svesku jednom dnevno.
Za opciju 3, pandas je pitanje jedne linije:
# 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 zavisnost, sam openpyxl radi:
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, zvaničan SDK
Brevo objavljuje @getbrevo/brevo za Node. Brine se o autentifikaciji, ponovnim pokušajima i tipiziranom obliku zahteva:
// 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 isto:
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: jedna linija za ad hoc uvoze
Kada samo želite da testirate endpoint ili ručno ugurate mali fajl:
# 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 ceo fajl i JSON-eskejpuje ga, što vas štedi ručnog eskejpovanja prelomova reda i navodnika.
Fajlovi veći od 10 MB: koristite fileUrl
fileBody je ograničen na 10 MB. Ako je vaš izvoz kontakata veći, hostujte fajl na URL-u koji Brevo može da preuzme i prosledite to:
body = { "fileUrl": "https://files.example.com/contacts/2026-04-30.csv", "listIds": [42], "updateExistingContacts": True,}Sve dostupno preko javnog HTTPS URL-a radi: prepotpisani S3 URL-ovi, GCS, vaš sopstveni statički hosting, čak i GitHub raw URL za jednokratne uvoze. Brevo preuzima fajl sa vašeg URL-a, a zatim pokreće uvoz. Prihvaćeni formati: .csv, .txt, .json.
Pošaljite JSON umesto CSV
Ako već vučete kontakte iz baze, ne morate ići okolo kroz CSV. Pošaljite ih kao JSON direktno:
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 asinhrono ponašanje.
Proveravanje završetka
Uvoz je asinhron. processId je vaša ručka za praćenje. Postoji poseban endpoint za proveru 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 poslove koji dugo traju, čistiji obrazac je notifyUrl: prosledite HTTPS endpoint kojem će Brevo poslati POST kada se uvoz završi i preskočite proveravanje.
body["notifyUrl"] = "https://your-app.example.com/webhooks/brevo-import"Česte greške i kako ih rešiti
400 Bad Request bez očiglednog razloga. Skoro uvek tačke-zarez umesto zareza u CSV-u. Brevo uvozu treba ;, ne ,. Dvaput proverite fileBody posle vašeg koraka konverzije.
Vrednosti prilagođenih atributa nestaju. Atribut ne postoji u vašem nalogu. Kreirajte ga pod Contacts → Settings → Contact attributes pre uvoza, ili koristite Attributes API da ga kreirate kao deo svoje skripte.
401 Unauthorized. Pogrešno ime zaglavlja. To je api-key (mala slova, crtica), ne Authorization ni X-API-Key.
Uvoz je „uspeo”, ali se kontakti nisu pojavili na listi. Proverite da li listIds sadrži tačnu listu. I: ako je updateExistingContacts false i kontakti već postoje, Brevo ih tiho preskače umesto da ih ponovo doda na listu.
Neki redovi su uvezeni, drugi nisu. Brevo vam šalje imejl sa izveštajem o greškama po redu posle završetka uvoza (osim ako ste postavili disableNotification: true). Izveštaj vam govori koji redovi su imali loše imejlove, nedostajuća obavezna polja ili probleme sa formatiranjem.
Kada skriptirati i kada koristiti korisnički interfejs
Korisnički interfejs je u redu za jednokratne uvoze ispod hiljadu redova. Skripta pobeđuje čim:
- Uvezite više od jednom (npr. nedeljni izvoz iz vašeg CRM-a)
- Izvorni podaci treba da se očiste pre nego što završe u Brevu (deduplikacija, formatiranje brojeva telefona, podela punih imena na ime i prezime)
- Želite da se izvršava po rasporedu bez da neko klikće na dugmad
- Fajl je veći od zone udobnosti korisničkog interfejsa
Umotajte gornju skriptu u cron posao ili GitHub Action i imate automatizovanu sinhronizaciju kontakata. Sledeći post u seriji pokazuje kako uraditi istu stvar direktno iz Google Sheet pomoću Apps Script. Bez servera.