Slik importerer du CSV- eller Excel-kontakter til Brevo med et skript (Python, Node.js, cURL)

Bulk-importer kontakter fra en CSV- eller Excel-fil inn i Brevo med import-contacts API-et. Inkluderer ferdige Python- og Node.js-skript, en cURL-one-liner, feilhåndtering og tips for filer større enn 10 MB.

Featured image for article: Slik importerer du CSV- eller Excel-kontakter til Brevo med et skript (Python, Node.js, cURL)

Hvis du noen gang har trengt å sende noen tusen kontakter fra et regneark inn i Brevo, blir den manuelle UI-veien fort slitsom: velg filen, mapp kolonner, velg en liste, vent, gjenta. Et skript gjør samme jobb på sekunder, og (viktigere) du kan kjøre det på nytt etter en tidsplan, etter en database-eksport, eller når CRM-en din spytter ut en fersk CSV.

Denne guiden dekker API-endepunktet Brevo gir deg for nøyaktig dette, med fullt fungerende skript i Python, Node.js, og en cURL-one-liner. Alt i denne guiden treffer POST /v3/contacts/import.

Endepunktet i et øyekast

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
}

Responsen kommer raskt:

{ "processId": 78 }

Det er en 202 Accepted, Brevo har akseptert importen og prosesserer den i bakgrunnen. processId er ditt sporingshåndtak hvis du vil polle for ferdigstillelse eller sette opp en notifyUrl-webhook.

Noen ting verdt å merke seg før du skriver kode:

  • Body kan være CSV (fileBody), JSON (jsonBody), eller en ekstern URL (fileUrl). Velg én. CSV-formen bruker semikolon (;) for å skille kolonner, ikke komma, det er en vanlig felle.
  • fileBody og jsonBody er begrenset til 10 MB. Brevo anbefaler å holde seg rundt 8 MB for å være på den sikre siden. For noe større, last opp filen til S3, GCS, eller hvilken som helst HTTPS-host og send fileUrl i stedet.
  • Egendefinerte attributter som ikke finnes i kontoen din blir stille ignorert. Opprett dem i Brevos UI (eller via Attributes API) før du importerer rader som bruker dem, ellers forsvinner dataen bare.
  • updateExistingContacts er som standard true. Hvis du setter den til false, hopper Brevo over kontakter hvis e-post allerede finnes, nyttig for “legg bare til nye”-jobber.
  • emptyContactsAttributes styrer om tomme celler i CSV-en din sletter eksisterende verdier. Standard false (tomme celler ignoreres). Sett til true hvis CSV-en er sannhetskilden og du vil at blanke skal fjerne utdatert data.

Hent en API-nøkkel

Logg inn på Brevo -> Settings -> SMTP & API -> API Keys -> opprett en ny nøkkel. Den ser ut som xkeysib-.... Du sender den som api-key HTTP-header på hver forespørsel. Behandle den som et passord, den har full lese/skrive-tilgang på kontoen din.

Et rent mønster: legg den i en miljøvariabel slik at den aldri havner i kildetreet ditt.

Terminal window
export BREVO_API_KEY="xkeysib-..."

Python: les en CSV, send den til Brevo

Dette er det enklest mulige skriptet: les en lokal CSV med standardbiblioteket, send body-en rett til Brevo. Ingen tredjeparts-SDK nødvendig utover 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}")

Kjør det:

Terminal window
python import_csv_to_brevo.py contacts.csv

CSV-ens første rad er headeren. Kolonnenavn mappes til Brevo-attributter med store bokstaver, eksakt match: EMAIL, FIRSTNAME, LASTNAME, pluss eventuelle egendefinerte attributter du har definert. EMAIL er obligatorisk.

EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITY
[email protected],Jane,Doe,Acme,Berlin
[email protected],John,Smith,Globex,Paris

Det er hele flyten. Skriptet returnerer innen et sekund; selve importen kjører serverside og tar fra noen sekunder til noen minutter avhengig av volum.

Excel-filer: tre brukbare veier

Brevos import-endepunkt leser ikke .xlsx direkte, så du har tre reelle alternativer avhengig av hvor jobben må leve:

  1. Kjør en VBA-makro inne i arbeidsboken, ett-klikks synkronisering fra Excel selv, ingen ekstern skript. Dette er riktig svar når filen lever på en stasjonær PC og teamet ditt vil ha en knapp på arket. Full kode i Excel-til-Brevo VBA-makroguiden.
  2. Office Scripts + Power Automate, hvis arbeidsboken ligger i OneDrive/SharePoint og du vil ha uovervåket planlagt synkronisering. Også dekket i Excel-guiden.
  3. Konverter .xlsx til CSV fra et skript, det resten av denne seksjonen dekker. Best hvis du allerede kjører Python eller Node på en server og bare trenger å hente en arbeidsbok inn én gang om dagen.

For alternativ 3, gjør pandas det til en one-liner:

# 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")

Hvis du ikke vil ha pandas som avhengighet, fungerer openpyxl alene:

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: samme jobb, offisiell SDK

Brevo publiserer @getbrevo/brevo for Node. Den håndterer auth, retries og typet forespørselsform:

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

Eller, hvis du ikke vil ha SDK-en, fungerer vanlig fetch på samme måte:

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: en one-liner for ad-hoc-importer

Når du bare vil teste endepunktet eller dytte en liten fil inn for hånd:

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 . sluker hele filen og JSON-escaper den, sparer deg fra å måtte escape newlines og quotes manuelt.

Filer større enn 10 MB: bruk fileUrl

fileBody er begrenset til 10 MB. Hvis kontakteksporten din er større, host filen på en URL Brevo kan hente og send den:

body = {
"fileUrl": "https://files.example.com/contacts/2026-04-30.csv",
"listIds": [42],
"updateExistingContacts": True,
}

Alt som kan nås via en offentlig HTTPS-URL fungerer, pre-signerte S3-URL-er, GCS, din egen statiske host, til og med en GitHub raw-URL for engangsimporter. Brevo henter filen fra URL-en din, så kjører importen. Aksepterte formater: .csv, .txt, .json.

Send JSON i stedet for CSV

Hvis du allerede henter kontakter fra en database, trenger du ikke gå via CSV, send dem som JSON direkte:

body = {
"jsonBody": [
{
"email": "[email protected]",
"attributes": {
"FIRSTNAME": "Jane",
"LASTNAME": "Doe",
"COMPANY": "Acme",
},
},
{
"email": "[email protected]",
"attributes": {
"FIRSTNAME": "John",
"LASTNAME": "Smith",
"COMPANY": "Globex",
},
},
],
"listIds": [42],
}

Samme 10 MB-tak. Samme async-oppførsel.

Polling for ferdigstillelse

Importen er asynkron, processId er håndtaket ditt for å spore den. Det er et eget endepunkt for å sjekke prosess-status:

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}")

For lange jobber er det renere mønsteret notifyUrl: send en HTTPS-endepunkt Brevo POSTer til når importen er ferdig, og hopp over pollingen.

body["notifyUrl"] = "https://your-app.example.com/webhooks/brevo-import"

Vanlige feil og hvordan fikse dem

400 Bad Request uten åpenbar årsak. Nesten alltid semikolon vs komma i CSV-en. Brevos import forventer ;, ikke ,. Dobbeltsjekk fileBody etter konverteringssteget ditt.

Verdier for egendefinerte attributter forsvinner. Attributtet finnes ikke i kontoen din. Opprett det under Contacts -> Settings -> Contact attributes før import, eller bruk Attributes API-et for å opprette det som del av skriptet ditt.

401 Unauthorized. Feil header-navn. Det er api-key (små bokstaver, bindestrek), ikke Authorization eller X-API-Key.

Importen “lyktes” men kontaktene dukket ikke opp i listen. Sjekk at listIds inneholder riktig liste. Også: hvis updateExistingContacts er false og kontaktene allerede finnes, hopper Brevo stille over dem i stedet for å legge dem til på listen igjen.

Noen rader importerte, noen ikke. Brevo sender deg en e-post med en feilrapport per rad etter at importen er ferdig (med mindre du setter disableNotification: true). Rapporten forteller deg hvilke rader som hadde dårlige e-poster, manglende påkrevde felt eller formateringsproblemer.

Når skal du skrive skript vs bruke UI

UI-et fungerer fint for engangsimporter under tusen rader. Et skript vinner så snart:

  • Du importerer mer enn én gang (f.eks. ukentlig eksport fra CRM-en din)
  • Kildedataen trenger rensing før den lander i Brevo (deduplisering, formatering av telefonnumre, splitte fulle navn til fornavn/etternavn)
  • Du vil at den skal kjøre etter en tidsplan uten at noen klikker knapper
  • Filen er større enn UI-ets komfortsone

Pakk skriptet over i en cron-jobb eller en GitHub Action og du har automatisert kontaktsynkronisering. Neste innlegg i denne serien viser hvordan du gjør det samme rett fra et Google Sheet med Apps Script, ingen server nødvendig.

Videre lesning

Frequently Asked Questions

Hva er Brevos API-endepunkt for bulk-import av kontakter?
POST https://api.brevo.com/v3/contacts/import. Det aksepterer CSV-innhold (fileBody), en ekstern fil-URL (fileUrl), eller et JSON-array (jsonBody). Endepunktet er asynkront, det returnerer en processId umiddelbart og fullfører importen i bakgrunnen.
Hva er maksimal filstørrelse?
10 MB for inline CSV (fileBody) eller JSON (jsonBody). For større filer, host filen et sted som er tilgjengelig og send URL-en via fileUrl i stedet, den banen har ingen dokumentert størrelsesbegrensning.
Hvordan importerer jeg en .xlsx-fil?
Brevos import-API tar bare .csv, .txt eller .json. Konverter .xlsx til .csv først, pandas, openpyxl, eller LibreOffice headless kan gjøre det på én linje. Skriptet i denne guiden inkluderer en xlsx til csv-konvertering.
Vil det overskrive eksisterende kontakter?
Som standard, ja, updateExistingContacts er som standard true og matcher på e-post. Sett den til false for å hoppe over kontakter som allerede finnes. Sett emptyContactsAttributes til true hvis du vil at tomme CSV-celler skal slette eksisterende verdier (ellers ignoreres tomme celler).
Start gratis med Brevo