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.

Featured image for article: Kako uvesti kontakte iz CSV ili Excel fajla u Brevo skriptom (Python, Node.js, cURL)

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/import
Content-Type: application/json
api-key: YOUR_API_KEY
{
"fileBody": "EMAIL;FIRSTNAME;LASTNAME\n[email protected];Jane;Doe",
"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.
  • fileBody i jsonBody su 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 prosledite fileUrl.
  • 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.
  • updateExistingContacts je podrazumevano true. Ako ga postavite na false, Brevo preskače kontakte čiji imejl već postoji. Korisno za poslove tipa „samo dodaj nove”.
  • emptyContactsAttributes kontroliše da li prazne ćelije u vašem CSV-u brišu postojeće vrednosti. Podrazumevano false (prazne ćelije se ignorišu). Postavite na true ako 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.

Terminal window
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_csv_to_brevo.py
import csv
import io
import os
import sys
import requests
API_KEY = os.environ["BREVO_API_KEY"]
LIST_ID = 42 # the Brevo list to add contacts to
INPUT_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:

Terminal window
python import_csv_to_brevo.py contacts.csv

Prvi 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,CITY
[email protected],Jane,Doe,Acme,Berlin
[email protected],John,Smith,Globex,Paris

To 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:

  1. 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.
  2. Office Scripts + Power Automate. Ako radna sveska živi u OneDrive/SharePoint i želite nenadgledanu sinhronizaciju po rasporedu. Takođe pokriveno u Excel vodiču.
  3. Konvertujte .xlsx u 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 openpyxl
import 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_workbook
import 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:

import-csv-to-brevo.mjs
// npm install @getbrevo/brevo
import 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 needed
const 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:

Terminal window
# Convert commas to semicolons, then send the file inline
csv=$(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": [
{
"email": "[email protected]",
"attributes": {
"FIRSTNAME": "Jane",
"LASTNAME": "Doe",
"COMPANY": "Acme",
},
},
{
"email": "[email protected]",
"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.

Dalje čitanje

Frequently Asked Questions

Koji je Brevo API endpoint za masovni uvoz kontakata?
POST https://api.brevo.com/v3/contacts/import. Prihvata sadržaj CSV (fileBody), URL udaljenog fajla (fileUrl) ili JSON niz (jsonBody). Endpoint je asinhron: odmah vraća processId i uvoz završava u pozadini.
Kolika je maksimalna veličina fajla?
10 MB za umetnuti CSV (fileBody) ili JSON (jsonBody). Za veće fajlove hostujte fajl negde gde je dostupan i umesto toga prosledite njegov URL preko fileUrl. Taj put nema dokumentovano ograničenje veličine.
Kako da uvezem .xlsx fajl?
Brevo API za uvoz prima samo .csv, .txt ili .json. Prvo konvertujte .xlsx u .csv. Pandas, openpyxl ili LibreOffice headless to mogu u jednoj liniji. Skripta u ovom vodiču sadrži konverziju iz xlsx u csv.
Da li će prepisati postojeće kontakte?
Po podrazumevanju, da. updateExistingContacts ima podrazumevanu vrednost true i poklapa po imejlu. Postavite na false da preskočite kontakte koji već postoje. Postavite emptyContactsAttributes na true ako želite da prazne ćelije CSV-a obrišu postojeće vrednosti (inače se prazne ćelije ignorišu).
Započnite besplatno sa Brevo