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.

Featured image for article: Jak importovat kontakty z CSV nebo Excelu do Brevo pomocí skriptu (Python, Node.js, cURL)

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/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
}

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.
  • fileBody a jsonBody jsou 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 toho fileUrl.
  • 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í.
  • updateExistingContacts má výchozí hodnotu true. Pokud nastavíte na false, 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 na true, 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.

Terminal window
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_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}")

Spusťte to:

Terminal window
python import_csv_to_brevo.py contacts.csv

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

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

  1. 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.
  2. 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.
  3. Převod .xlsx na 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 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")

Pokud nechcete pandas jako závislost, samotné openpyxl funguje:

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: stejná úloha, oficiální SDK

Brevo publikuje @getbrevo/brevo pro Node. Řeší autentizaci, opakované pokusy a typovaný tvar požadavku:

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}`);

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:

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 . 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": [
{
"email": "[email protected]",
"attributes": {
"FIRSTNAME": "Jane",
"LASTNAME": "Doe",
"COMPANY": "Acme",
},
},
{
"email": "[email protected]",
"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.

Další čtení

Frequently Asked Questions

Jaký je API endpoint Brevo pro hromadný import kontaktů?
POST https://api.brevo.com/v3/contacts/import. Přijímá CSV obsah (fileBody), URL vzdáleného souboru (fileUrl) nebo JSON pole (jsonBody). Endpoint je asynchronní, vrací processId okamžitě a import dokončí na pozadí.
Jaká je maximální velikost souboru?
10 MB pro inline CSV (fileBody) nebo JSON (jsonBody). U větších souborů hostujte soubor někde, kam je dostupný, a předejte jeho URL přes fileUrl. Tato cesta nemá zdokumentovaný limit velikosti.
Jak naimportuji soubor .xlsx?
Import API Brevo přijímá pouze .csv, .txt nebo .json. Nejprve převeďte .xlsx na .csv. pandas, openpyxl nebo LibreOffice headless to zvládnou jedním řádkem. Skript v této příručce zahrnuje konverzi xlsx na csv.
Přepíše to existující kontakty?
Ve výchozím nastavení ano. updateExistingContacts má výchozí hodnotu true a párování probíhá podle e-mailu. Nastavte na false, abyste přeskočili kontakty, které již existují. Nastavte emptyContactsAttributes na true, pokud chcete, aby prázdné buňky CSV vymazaly existující hodnoty (jinak jsou prázdné buňky ignorovány).
Začněte zdarma s Brevo