Jak importovat kontakty z CSV nebo Excelu do Brevo pomocí skriptu (Python, Node.js, cURL)
Hromadně importujte kontakty z CSV nebo Excel souboru do Brevo pomocí API import-contacts. Obsahuje hotové skripty v Pythonu a Node.js, jednořádkový cURL, ošetření chyb a tipy pro soubory větší než 10 MB.
Pokud jste někdy potřebovali poslat několik tisíc kontaktů z tabulky do Brevo, ruční cesta přes UI začne brzy bolet: vybrat soubor, namapovat sloupce, vybrat seznam, počkat, opakovat. Skript zvládne stejnou práci za pár sekund a, což je důležitější, můžete ho spustit znovu podle plánu, po exportu z databáze nebo kdykoli vaše CRM vyplivne čerstvé CSV.
Tato příručka pokrývá API endpoint, který vám Brevo dává přesně pro tohle, s plně funkčními skripty v Pythonu, Node.js a jednořádkovým cURL. Vše v této příručce volá POST /v3/contacts/import.
Endpoint v kostce
POST https://api.brevo.com/v3/contacts/importContent-Type: application/jsonapi-key: YOUR_API_KEY
{ "listIds": [42], "updateExistingContacts": true, "emailBlacklist": false, "smsBlacklist": false}Odpověď přijde rychle:
{ "processId": 78 }Je to 202 Accepted. Brevo přijalo import a zpracovává ho na pozadí. processId je vaše sledovací rukojeť, pokud chcete dotazovat na dokončení nebo nastavit notifyUrl webhook.
Pár věcí, které stojí za zmínku, než začnete psát kód:
- Tělo může být CSV (
fileBody), JSON (jsonBody) nebo vzdálená URL (fileUrl). Vyberte jedno. CSV forma používá středníky (;) jako oddělovače sloupců, ne čárky. To je častý zádrhel. fileBodyajsonBodyjsou omezeny na 10 MB. Brevo doporučuje zůstat kolem 8 MB pro jistotu. Pro cokoli většího nahrajte soubor na S3, GCS nebo jakýkoli HTTPS hosting a předejte místo tohofileUrl.- Vlastní atributy, které ve vašem účtu neexistují, jsou tiše ignorovány. Vytvořte je v UI Brevo (nebo přes Attributes API) ještě před importem řádků, které je používají, jinak data prostě zmizí.
updateExistingContactsmá výchozí hodnotutrue. Pokud nastavíte nafalse, Brevo přeskočí kontakty, jejichž e-mail již existuje. Užitečné pro úlohy „pouze nové”.emptyContactsAttributesřídí, zda prázdné buňky ve vašem CSV smažou existující hodnoty. Výchozífalse(prázdné buňky jsou ignorovány). Nastavte natrue, pokud je CSV zdrojem pravdy a chcete, aby prázdné hodnoty vyčistily zastaralá data.
Získejte API klíč
Přihlaste se do Brevo → Settings → SMTP & API → API Keys → vytvořte nový klíč. Vypadá jako xkeysib-.... Předáváte ho jako HTTP hlavičku api-key při každém požadavku. Zacházejte s ním jako s heslem, má plný čtecí i zápisový přístup k vašemu účtu.
Čistý vzor: dejte ho do proměnné prostředí, aby se nikdy nedostal do vašeho zdrojového stromu.
export BREVO_API_KEY="xkeysib-..."Python: načtěte CSV a pošlete ho do Brevo
Toto je nejjednodušší možný skript: načtete lokální CSV pomocí standardní knihovny a tělo pošlete přímo do Brevo. Není potřeba žádné SDK třetí strany kromě 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}")Spusťte to:
python import_csv_to_brevo.py contacts.csvPrvní řádek vašeho CSV je hlavička. Názvy sloupců se mapují na atributy Brevo podle velkých písmen, přesné shody: EMAIL, FIRSTNAME, LASTNAME, plus jakýkoli vlastní atribut, který jste definovali. EMAIL je povinný.
EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITYTo je celý postup. Skript se vrátí během sekundy, samotný import běží na straně serveru a trvá od několika sekund po několik minut podle objemu.
Excel soubory: tři životaschopné cesty
Import endpoint Brevo nečte .xlsx přímo, takže máte tři reálné možnosti podle toho, kde má práce probíhat:
- Spustit VBA makro uvnitř sešitu. Synchronizace jedním kliknutím přímo z Excelu, bez externího skriptu. To je správná odpověď, když soubor leží na pracovní stanici a váš tým chce tlačítko v listu. Plný kód v příručce VBA makra Excel do Brevo.
- Office Scripts + Power Automate. Pokud sešit žije v OneDrive/SharePoint a chcete bezobslužnou plánovanou synchronizaci. Také pokryto v příručce Excelu.
- Převod
.xlsxna CSV ze skriptu. To pokrývá zbytek této sekce. Nejlepší, pokud již na serveru spouštíte Python nebo Node a potřebujete jen jednou denně stáhnout sešit.
Pro variantu 3 je pandas jednořádkovka:
# 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")Pokud nechcete pandas jako závislost, samotné openpyxl funguje:
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: stejná úloha, oficiální SDK
Brevo publikuje @getbrevo/brevo pro Node. Řeší autentizaci, opakované pokusy a typovaný tvar požadavku:
// 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}`);Nebo, pokud nechcete SDK, obyčejný fetch funguje stejně:
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: jednořádkovka pro ad-hoc importy
Když chcete jen otestovat endpoint nebo ručně poslat malý soubor:
# 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 . načte celý soubor a JSON-escapuje ho, ušetří vám ruční escapování nových řádků a uvozovek.
Soubory větší než 10 MB: použijte fileUrl
fileBody je omezen na 10 MB. Pokud je váš export kontaktů větší, hostujte soubor na URL, kterou Brevo dokáže stáhnout, a předejte tu:
body = { "fileUrl": "https://files.example.com/contacts/2026-04-30.csv", "listIds": [42], "updateExistingContacts": True,}Cokoli dostupné přes veřejnou HTTPS URL funguje, předem podepsané S3 URL, GCS, váš vlastní statický hosting, dokonce GitHub raw URL pro jednorázové importy. Brevo si stáhne soubor z vaší URL a pak spustí import. Přijímané formáty: .csv, .txt, .json.
Pošlete JSON místo CSV
Pokud už táháte kontakty z databáze, nemusíte je posílat oklikou přes CSV. Pošlete je rovnou jako JSON:
body = { "jsonBody": [ { "attributes": { "FIRSTNAME": "Jane", "LASTNAME": "Doe", "COMPANY": "Acme", }, }, { "attributes": { "FIRSTNAME": "John", "LASTNAME": "Smith", "COMPANY": "Globex", }, }, ], "listIds": [42],}Stejný limit 10 MB. Stejné asynchronní chování.
Dotazování na dokončení
Import je asynchronní, processId je vaše rukojeť pro sledování. Existuje samostatný endpoint pro kontrolu stavu procesu:
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}")Pro dlouho běžící úlohy je čistší vzor notifyUrl: předejte HTTPS endpoint, na který Brevo POSTne, až import skončí, a vynechte dotazování.
body["notifyUrl"] = "https://your-app.example.com/webhooks/brevo-import"Časté chyby a jak je opravit
400 Bad Request bez zjevné příčiny. Téměř vždy středníky vs. čárky v CSV. Import Brevo očekává ;, ne ,. Po konverzním kroku zkontrolujte fileBody znovu.
Hodnoty vlastních atributů mizí. Atribut ve vašem účtu neexistuje. Vytvořte ho pod Contacts → Settings → Contact attributes před importem nebo použijte Attributes API k jeho vytvoření jako součást vašeho skriptu.
401 Unauthorized. Špatný název hlavičky. Je to api-key (malá písmena, pomlčka), ne Authorization ani X-API-Key.
Import „uspěl”, ale kontakty se v seznamu neobjevily. Zkontrolujte, že listIds obsahuje správný seznam. Také: pokud je updateExistingContacts false a kontakty již existují, Brevo je tiše přeskočí místo toho, aby je znovu přidalo do seznamu.
Některé řádky se naimportovaly, jiné ne. Brevo vám po dokončení importu pošle e-mail s chybovou zprávou pro každý řádek (pokud nenastavíte disableNotification: true). Zpráva říká, které řádky měly špatné e-maily, chybějící povinná pole nebo formátovací problémy.
Kdy skriptovat a kdy použít UI
UI je v pohodě pro jednorázové importy pod tisíc řádků. Skript vyhrává, jakmile:
- Importujete víckrát než jednou (např. týdenní export z vašeho CRM)
- Zdrojová data potřebují čištění, než se dostanou do Brevo (deduplikace, formátování telefonů, rozdělení celých jmen na křestní/příjmení)
- Chcete, aby běžel podle plánu bez toho, aby někdo klikal na tlačítka
- Soubor je větší, než je pohodlná zóna UI
Zabalte výše uvedený skript do cron úlohy nebo GitHub Action a máte automatizovanou synchronizaci kontaktů. Další článek v této sérii ukazuje, jak udělat totéž přímo z Google Sheet pomocí Apps Script, bez serveru.