Cara Mengimpor Kontak CSV atau Excel ke Brevo dengan Script (Python, Node.js, cURL)

Impor kontak secara massal dari file CSV atau Excel ke Brevo menggunakan API import-contacts. Termasuk script Python dan Node.js siap pakai, satu baris cURL, penanganan error, dan tips untuk file lebih besar dari 10 MB.

Featured image for article: Cara Mengimpor Kontak CSV atau Excel ke Brevo dengan Script (Python, Node.js, cURL)

Jika Anda pernah perlu mendorong beberapa ribu kontak dari spreadsheet ke Brevo, jalur UI manual cepat menjadi melelahkan: pilih file, petakan kolom, pilih daftar, tunggu, ulangi. Sebuah script melakukan pekerjaan yang sama dalam hitungan detik dan, yang lebih penting, Anda dapat menjalankannya kembali sesuai jadwal, setelah ekspor database, atau kapan pun CRM Anda mengeluarkan CSV baru.

Panduan ini membahas endpoint API yang Brevo sediakan persis untuk ini, dengan script lengkap yang berfungsi di Python, Node.js, dan satu baris cURL. Semua di panduan ini menggunakan POST /v3/contacts/import.

Endpoint sekilas

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
}

Respons datang dengan cepat:

{ "processId": 78 }

Itu adalah 202 Accepted, Brevo telah menerima impor dan memprosesnya di latar belakang. processId adalah penanda pelacakan Anda jika Anda ingin melakukan polling untuk penyelesaian atau menyiapkan webhook notifyUrl.

Beberapa hal yang perlu diperhatikan sebelum menulis kode:

  • Body dapat berupa CSV (fileBody), JSON (jsonBody), atau URL remote (fileUrl). Pilih salah satu. Format CSV menggunakan titik koma (;) untuk memisahkan kolom, bukan koma, ini adalah jebakan umum.
  • fileBody dan jsonBody dibatasi pada 10 MB. Brevo merekomendasikan untuk tetap di sekitar 8 MB agar aman. Untuk yang lebih besar, unggah file ke S3, GCS, atau host HTTPS apa pun dan kirimkan fileUrl sebagai gantinya.
  • Atribut kustom yang tidak ada di akun Anda akan diabaikan secara diam-diam. Buat atribut tersebut di UI Brevo (atau melalui Attributes API) sebelum mengimpor baris yang menggunakannya, jika tidak data akan hilang begitu saja.
  • updateExistingContacts default-nya true. Jika Anda menyetelnya ke false, Brevo melewati kontak yang email-nya sudah ada, berguna untuk pekerjaan “tambahkan baru saja”.
  • emptyContactsAttributes mengontrol apakah sel kosong di CSV Anda menghapus nilai yang ada. Default false (sel kosong diabaikan). Setel ke true jika CSV adalah sumber kebenaran dan Anda ingin sel kosong menghapus data lama.

Dapatkan API key

Masuk ke Brevo, Settings -> SMTP & API -> API Keys, buat key baru. Tampilannya seperti xkeysib-.... Anda akan mengirimkannya sebagai header HTTP api-key di setiap request. Perlakukan seperti password, ia memiliki akses baca/tulis penuh ke akun Anda.

Pola yang bersih: simpan dalam variabel lingkungan agar tidak pernah masuk ke source tree Anda.

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

Python: baca CSV, kirim ke Brevo

Ini adalah script paling sederhana: baca CSV lokal dengan standard library, kirim body langsung ke Brevo. Tidak diperlukan SDK pihak ketiga selain 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}")

Jalankan:

Terminal window
python import_csv_to_brevo.py contacts.csv

Baris pertama CSV Anda adalah header. Nama kolom dipetakan ke atribut Brevo dengan kecocokan persis huruf besar: EMAIL, FIRSTNAME, LASTNAME, ditambah atribut kustom apa pun yang telah Anda definisikan. EMAIL wajib.

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

Itulah seluruh alurnya. Script kembali dalam satu detik; impor sebenarnya berjalan di sisi server dan memakan waktu mulai dari beberapa detik hingga beberapa menit tergantung pada volume.

File Excel: tiga jalur yang layak

Endpoint impor Brevo tidak membaca .xlsx secara langsung, jadi Anda memiliki tiga opsi nyata tergantung di mana pekerjaan harus dilakukan:

  1. Jalankan VBA macro di dalam workbook, sinkronisasi satu klik dari Excel itu sendiri, tanpa script eksternal. Ini adalah jawaban yang tepat ketika file berada di desktop dan tim Anda menginginkan tombol di sheet. Kode lengkap ada di panduan VBA macro Excel ke Brevo.
  2. Office Scripts + Power Automate, jika workbook ada di OneDrive/SharePoint dan Anda ingin sinkronisasi terjadwal tanpa pengawasan. Juga dibahas di panduan Excel.
  3. Konversi .xlsx ke CSV dari script, yang dibahas di sisa bagian ini. Terbaik jika Anda sudah menjalankan Python atau Node di server dan hanya perlu menarik workbook sekali sehari.

Untuk opsi 3, pandas membuatnya menjadi satu baris:

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

Jika Anda tidak ingin pandas sebagai dependensi, openpyxl saja sudah cukup:

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: pekerjaan yang sama, SDK resmi

Brevo menerbitkan @getbrevo/brevo untuk Node. Ia menangani autentikasi, retry, dan bentuk request bertipe:

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

Atau, jika Anda tidak ingin SDK, fetch biasa juga berfungsi dengan cara yang sama:

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: satu baris untuk impor ad-hoc

Ketika Anda hanya ingin menguji endpoint atau mendorong file kecil secara manual:

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 . menyerap seluruh file dan melakukan JSON-escape, menyelamatkan Anda dari melakukan escape newline dan tanda kutip secara manual.

File lebih besar dari 10 MB: gunakan fileUrl

fileBody dibatasi pada 10 MB. Jika dump kontak Anda lebih besar, host file di URL yang dapat diambil Brevo dan kirimkan URL tersebut:

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

Apa pun yang dapat dijangkau melalui URL HTTPS publik akan berfungsi: pre-signed S3 URL, GCS, host statis Anda sendiri, bahkan URL raw GitHub untuk impor satu kali. Brevo mengambil file dari URL Anda, lalu menjalankan impor. Format yang diterima: .csv, .txt, .json.

Kirim JSON alih-alih CSV

Jika Anda sudah menarik kontak dari database, Anda tidak perlu putar balik melalui CSV, kirim sebagai JSON langsung:

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

Batas 10 MB yang sama. Perilaku async yang sama.

Polling untuk penyelesaian

Impor bersifat asynchronous, processId adalah penanda Anda untuk melacaknya. Ada endpoint terpisah untuk memeriksa status proses:

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

Untuk pekerjaan yang berjalan lama, pola yang lebih bersih adalah notifyUrl: kirim endpoint HTTPS yang akan di-POST oleh Brevo saat impor selesai, dan lewati polling.

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

Error umum dan cara mengatasinya

400 Bad Request tanpa penyebab yang jelas. Hampir selalu titik koma vs koma di CSV. Impor Brevo mengharapkan ;, bukan ,. Periksa kembali fileBody setelah langkah konversi Anda.

Nilai atribut kustom menghilang. Atribut tidak ada di akun Anda. Buat di bawah Contacts -> Settings -> Contact attributes sebelum mengimpor, atau gunakan Attributes API untuk membuatnya sebagai bagian dari script Anda.

401 Unauthorized. Nama header salah. Itu adalah api-key (huruf kecil, tanda hubung), bukan Authorization atau X-API-Key.

Impor “berhasil” tetapi kontak tidak muncul di daftar. Periksa bahwa listIds berisi daftar yang benar. Juga: jika updateExistingContacts adalah false dan kontak sudah ada, Brevo diam-diam melewatinya alih-alih menambahkannya kembali ke daftar.

Beberapa baris terimpor, beberapa tidak. Brevo mengirim email laporan error per baris kepada Anda setelah impor selesai (kecuali Anda menyetel disableNotification: true). Laporan tersebut memberi tahu baris mana yang memiliki email buruk, field wajib yang hilang, atau masalah pemformatan.

Kapan harus membuat script vs menggunakan UI

UI baik-baik saja untuk impor sekali pakai di bawah seribu baris. Script menang segera setelah:

  • Anda mengimpor lebih dari sekali (misalnya ekspor mingguan dari CRM Anda)
  • Data sumber perlu dibersihkan sebelum masuk ke Brevo (deduplikasi, pemformatan nomor telepon, memisahkan nama lengkap menjadi nama depan/belakang)
  • Anda ingin script berjalan sesuai jadwal tanpa siapa pun mengklik tombol
  • File lebih besar dari zona nyaman UI

Bungkus script di atas dalam cron job atau GitHub Action dan Anda mendapatkan sinkronisasi kontak otomatis. Postingan berikutnya dalam seri ini menunjukkan cara melakukan hal yang sama langsung dari Google Sheet menggunakan Apps Script, tanpa server.

Bacaan lanjutan

Frequently Asked Questions

Apa endpoint Brevo API untuk mengimpor kontak secara massal?
POST https://api.brevo.com/v3/contacts/import. Endpoint ini menerima konten CSV (fileBody), URL file remote (fileUrl), atau array JSON (jsonBody). Endpoint ini bersifat asynchronous, mengembalikan processId segera dan menyelesaikan impor di latar belakang.
Berapa ukuran file maksimum?
10 MB untuk CSV inline (fileBody) atau JSON (jsonBody). Untuk file yang lebih besar, host file di lokasi yang dapat dijangkau dan kirimkan URL-nya melalui fileUrl, jalur tersebut tidak memiliki batas ukuran yang didokumentasikan.
Bagaimana cara mengimpor file .xlsx?
API impor Brevo hanya menerima .csv, .txt, atau .json. Konversi .xlsx ke .csv terlebih dahulu, pandas, openpyxl, atau LibreOffice headless dapat melakukannya dalam satu baris. Script di panduan ini sudah menyertakan konversi xlsx ke csv.
Apakah akan menimpa kontak yang sudah ada?
Secara default, ya, updateExistingContacts default-nya true dan mencocokkan berdasarkan email. Setel ke false untuk melewati kontak yang sudah ada. Setel emptyContactsAttributes ke true jika Anda ingin sel CSV kosong menghapus nilai yang sudah ada (jika tidak, sel kosong akan diabaikan).
Brevo के साथ मुफ्त में शुरू करें