CSV veya Excel Kişilerini Bir Komut Dosyasıyla Brevo'ya Aktarma (Python, Node.js, cURL)
import-contacts API'sini kullanarak CSV veya Excel dosyasındaki kişileri Brevo'ya toplu olarak aktarın. Çalıştırılmaya hazır Python ve Node.js komut dosyaları, cURL tek satırlık komutu, hata yönetimi ve 10 MB'dan büyük dosyalar için ipuçları içerir.
Bir elektronik tablodan birkaç bin kişiyi Brevo’ya aktarmanız gerektiyse, manuel UI yolu hızla can sıkıcı hale gelir: dosyayı seçin, sütunları eşleyin, listeyi seçin, bekleyin, tekrarlayın. Bir komut dosyası aynı işi saniyeler içinde yapar ve daha da önemlisi, zamanlanmış olarak, bir veritabanı dışa aktarımından sonra veya CRM’iniz her yeni bir CSV ürettiğinde tekrar çalıştırabilirsiniz.
Bu kılavuz, Brevo’nun tam olarak bunun için sunduğu API uç noktasını, Python, Node.js ile tam çalışan komut dosyaları ve bir cURL tek satırlık komutuyla birlikte ele alır. Bu kılavuzdaki her şey POST /v3/contacts/import adresine gider.
Uç noktaya bir bakış
POST https://api.brevo.com/v3/contacts/importContent-Type: application/jsonapi-key: YOUR_API_KEY
{ "listIds": [42], "updateExistingContacts": true, "emailBlacklist": false, "smsBlacklist": false}Yanıt hızlıca geri döner:
{ "processId": 78 }Bu bir 202 Accepted: Brevo içe aktarmayı kabul etti ve arka planda işliyor. processId, tamamlanmayı sorgulamak veya bir notifyUrl webhook’u kurmak isterseniz takip tutamacınızdır.
Kod yazmadan önce dikkat edilmesi gereken birkaç şey:
- Gövde CSV (
fileBody), JSON (jsonBody) veya uzak bir URL (fileUrl) olabilir. Birini seçin. CSV biçimi sütunları ayırmak için noktalı virgül (;) kullanır, virgül değil. Bu yaygın bir tuzaktır. fileBodyvejsonBody10 MB ile sınırlıdır. Brevo, güvende olmak için 8 MB civarında kalmanızı önerir. Daha büyük herhangi bir şey için dosyayı S3, GCS veya herhangi bir HTTPS sunucusuna yükleyin ve bunun yerinefileUrliletin.- Hesabınızda bulunmayan özel öznitelikler sessizce yok sayılır. Bunları kullanan satırları içe aktarmadan önce Brevo’nun arayüzünde (veya Attributes API üzerinden) oluşturun, aksi halde veriler kaybolur.
updateExistingContactsvarsayılan olaraktruedeğerindedir.falseolarak ayarlarsanız Brevo, e-postası zaten mevcut olan kişileri atlar. “Yalnızca yeni ekle” işleri için kullanışlıdır.emptyContactsAttributes, CSV’nizdeki boş hücrelerin mevcut değerleri silip silmeyeceğini kontrol eder. Varsayılanfalse(boş hücreler yok sayılır). CSV’niz doğruluk kaynağıysa ve boşlukların eski verileri temizlemesini istiyorsanıztrueolarak ayarlayın.
API anahtarı edinin
Brevo’ya giriş yapın → Ayarlar → SMTP & API → API Anahtarları → yeni bir anahtar oluşturun. xkeysib-... gibi görünür. Onu her istekte api-key HTTP üst bilgisi olarak ileteceksiniz. Bir parola gibi davranın, hesabınızda tam okuma/yazma yetkisine sahiptir.
Temiz bir desen: kaynak ağacınıza asla düşmeyecek şekilde bir ortam değişkenine koyun.
export BREVO_API_KEY="xkeysib-..."Python: bir CSV okuyun, Brevo’ya gönderin
Bu olabildiğince basit bir komut dosyasıdır: standart kütüphane ile yerel bir CSV’yi okuyun, gövdeyi doğrudan Brevo’ya gönderin. requests dışında üçüncü taraf bir SDK gerekmez.
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}")Çalıştırın:
python import_csv_to_brevo.py contacts.csvCSV’nizin ilk satırı başlıktır. Sütun adları Brevo özniteliklerine büyük harfle, tam eşleşmeyle eşlenir: EMAIL, FIRSTNAME, LASTNAME, ayrıca tanımladığınız her özel öznitelik. EMAIL zorunludur.
EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITYİşte tüm akış. Komut dosyası bir saniye içinde geri döner; gerçek içe aktarma sunucu tarafında çalışır ve hacme bağlı olarak birkaç saniye ile birkaç dakika arasında sürer.
Excel dosyaları: üç uygulanabilir yol
Brevo’nun içe aktarma uç noktası .xlsx dosyasını doğrudan okumaz, bu yüzden işin nerede yaşaması gerektiğine bağlı olarak üç gerçek seçeneğiniz vardır:
- Çalışma kitabının içinde bir VBA makrosu çalıştırın: Excel’in kendisinden tek tıkla senkronizasyon, harici komut dosyası yok. Dosya bir masaüstünde olduğunda ve ekibinizin sayfada bir düğme istediğinde doğru cevap budur. Tam kod Excel’den Brevo’ya VBA makrosu kılavuzunda.
- Office Scripts + Power Automate: çalışma kitabı OneDrive/SharePoint’te ise ve gözetimsiz zamanlanmış senkronizasyon istiyorsanız. Ayrıca Excel kılavuzunda kapsanmıştır.
.xlsx’i bir komut dosyasından CSV’ye dönüştürün: bu bölümün geri kalanının kapsadığı şey. Zaten bir sunucuda Python veya Node çalıştırıyorsanız ve günde bir kez bir çalışma kitabını çekmeniz gerekiyorsa en iyisidir.
Seçenek 3 için pandas bunu tek satırlık yapar:
# 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")pandas’ı bağımlılık olarak istemiyorsanız, tek başına openpyxl da çalışır:
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: aynı iş, resmi SDK
Brevo, Node için @getbrevo/brevo paketini yayınlar. Kimlik doğrulama, yeniden denemeler ve türlenmiş istek şeklini yönetir:
// 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}`);Veya SDK istemiyorsanız, düz fetch aynı şekilde çalışır:
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: anlık içe aktarmalar için tek satırlık komut
Sadece uç noktayı test etmek veya küçük bir dosyayı elle göndermek istediğinizde:
# 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 . tüm dosyayı yutar ve JSON olarak escape eder. Yeni satırları ve tırnak işaretlerini elle escape etmekten kurtarır.
10 MB’dan büyük dosyalar: fileUrl kullanın
fileBody 10 MB ile sınırlıdır. Kişi dökümünüz daha büyükse, dosyayı Brevo’nun ulaşabileceği bir URL’de barındırın ve onu iletin:
body = { "fileUrl": "https://files.example.com/contacts/2026-04-30.csv", "listIds": [42], "updateExistingContacts": True,}Genel HTTPS URL’si üzerinden ulaşılabilen herhangi bir şey çalışır: önceden imzalanmış S3 URL’leri, GCS, kendi statik sunucunuz, hatta tek seferlik içe aktarmalar için bir GitHub raw URL’si. Brevo dosyayı URL’nizden alır, sonra içe aktarmayı çalıştırır. Kabul edilen biçimler: .csv, .txt, .json.
CSV yerine JSON gönderin
Kişileri zaten bir veritabanından çekiyorsanız, CSV üzerinden gidip gelmenize gerek yoktur, doğrudan JSON olarak gönderin:
body = { "jsonBody": [ { "attributes": { "FIRSTNAME": "Jane", "LASTNAME": "Doe", "COMPANY": "Acme", }, }, { "attributes": { "FIRSTNAME": "John", "LASTNAME": "Smith", "COMPANY": "Globex", }, }, ], "listIds": [42],}Aynı 10 MB sınırı. Aynı asenkron davranış.
Tamamlanmayı sorgulama
İçe aktarma asenkrondur: processId onu izlemek için tutamacınızdır. Süreç durumunu kontrol etmek için ayrı bir uç nokta vardır:
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}")Uzun süren işler için daha temiz desen notifyUrl kullanmaktır: içe aktarma bittiğinde Brevo’nun POST yapacağı bir HTTPS uç noktası iletin ve sorgulamayı atlayın.
body["notifyUrl"] = "https://your-app.example.com/webhooks/brevo-import"Yaygın hatalar ve nasıl düzeltilir
Belirgin nedeni olmayan 400 Bad Request: Neredeyse her zaman CSV’deki noktalı virgül ve virgül meselesidir. Brevo’nun içe aktarması ; bekler, , değil. Dönüştürme adımınızdan sonra fileBody’yi tekrar kontrol edin.
Özel öznitelik değerleri kayboluyor: Öznitelik hesabınızda yok. Kişiler → Ayarlar → Kişi öznitelikleri altında onu içe aktarmadan önce oluşturun veya komut dosyanızın bir parçası olarak oluşturmak için Attributes API kullanın.
401 Unauthorized: Yanlış üst bilgi adı. api-key (küçük harf, tire), Authorization veya X-API-Key değil.
İçe aktarma “başarılı” ama kişiler listede görünmüyor: listIds’in doğru listeyi içerdiğini kontrol edin. Ayrıca: updateExistingContacts false ise ve kişiler zaten varsa, Brevo onları listeye yeniden eklemek yerine sessizce atlar.
Bazı satırlar içe aktarıldı, bazıları aktarılmadı: Brevo, içe aktarma bittikten sonra size satır başına bir hata raporu e-postayla gönderir (eğer disableNotification: true ayarlamadıysanız). Rapor hangi satırların hatalı e-postalara, eksik zorunlu alanlara veya biçimlendirme sorunlarına sahip olduğunu söyler.
Ne zaman komut dosyası yazmalı, ne zaman UI kullanmalı
UI, bin satırın altındaki tek seferlik içe aktarmalar için iyidir. Şu durumlarda bir komut dosyası kazanır:
- Birden fazla kez içe aktarıyorsanız (örneğin CRM’inizden haftalık dışa aktarım)
- Kaynak veriler Brevo’ya inmeden önce temizlenmesi gerekiyorsa (yinelenen kayıtların kaldırılması, telefon numaralarının biçimlendirilmesi, tam adların ad/soyad olarak ayrılması)
- Kimsenin düğmelere tıklaması olmadan zamanlanmış olarak çalışmasını istiyorsanız
- Dosya UI’nın rahat alanından daha büyükse
Yukarıdaki komut dosyasını bir cron işine veya bir GitHub Action’a sarın ve otomatik kişi senkronizasyonuna sahip olursunuz. Bu serideki bir sonraki yazı, aynı şeyi doğrudan bir Google Sheet’ten Apps Script kullanarak nasıl yapacağınızı gösterir, sunucu gerekmez.