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.

Featured image for article: Kako uvesti CSV ili Excel kontakte u Brevo pomoću skripte (Python, Node.js, cURL)

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/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š 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.
  • fileBody i jsonBody ogranič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 proslijedite fileUrl.
  • 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.
  • updateExistingContacts je prema zadanim postavkama true. Ako postavite na false, Brevo preskače kontakte čija e-pošta već postoji, korisno za poslove tipa “dodaj samo nove”.
  • emptyContactsAttributes kontrolira hoće li prazne ćelije u vašem CSV-u izbrisati postojeće vrijednosti. Zadano je false (prazne ćelije se ignoriraju). Postavite na true ako 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.

Terminal window
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_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:

Terminal window
python import_csv_to_brevo.py contacts.csv

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

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

  1. 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.
  2. Office Scripts + Power Automate, ako je radna knjiga u OneDriveu/SharePointu i želite zakazanu, neaduzevu sinkronizaciju. Također obrađeno u Excel vodiču.
  3. Pretvorite .xlsx u 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 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 ovisnost, dovoljan je openpyxl sam:

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, službeni SDK

Brevo objavljuje @getbrevo/brevo za Node. Brine se o autentifikaciji, ponovnim pokušajima i tipiziranom obliku zahtjeva:

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

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 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": [
{
"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 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.

Daljnje čitanje

Frequently Asked Questions

Koji je Brevo API endpoint za skupni uvoz kontakata?
POST https://api.brevo.com/v3/contacts/import. Prihvaća CSV sadržaj (fileBody), URL udaljene datoteke (fileUrl) ili JSON polje (jsonBody). Endpoint je asinkron, odmah vraća processId i dovršava uvoz u pozadini.
Koja je maksimalna veličina datoteke?
10 MB za inline CSV (fileBody) ili JSON (jsonBody). Za veće datoteke, hostajte datoteku na nekom dostupnom mjestu i proslijedite njezin URL preko fileUrl, taj put nema dokumentirano ograničenje veličine.
Kako uvozim .xlsx datoteku?
Brevo API za uvoz prima samo .csv, .txt ili .json. Najprije pretvorite .xlsx u .csv, to mogu pandas, openpyxl ili LibreOffice headless u jednoj liniji. Skripta u ovom vodiču uključuje pretvorbu xlsx u csv.
Hoće li prepisati postojeće kontakte?
Prema zadanim postavkama, da, updateExistingContacts je postavljen na true i uspoređuje po e-pošti. Postavite na false kako biste preskočili postojeće kontakte. Postavite emptyContactsAttributes na true ako želite da prazne CSV ćelije obrišu postojeće vrijednosti (inače se prazne ćelije ignoriraju).
Započnite besplatno s Brevo