---
title: "Cách nhập danh bạ CSV hoặc Excel vào Brevo bằng script (Python, Node.js, cURL)"
description: "Nhập hàng loạt danh bạ từ tệp CSV hoặc Excel vào Brevo bằng API import-contacts. Bao gồm script Python và Node.js sẵn sàng chạy, lệnh cURL một dòng, xử lý lỗi và mẹo cho tệp lớn hơn 10 MB."
url: https://tajo.io/vi/blog/import-csv-contacts-to-brevo-with-a-script/
markdown: https://tajo.io/vi/blog/import-csv-contacts-to-brevo-with-a-script.md
lang: vi-VN
author: "Set Noa"
author_url: https://tajo.io/vi/set-noa/
published: 2026-04-30
updated: 2026-05-01
alternates: {en-US: /blog/import-csv-contacts-to-brevo-with-a-script.md, bg-BG: /bg/blog/import-csv-contacts-to-brevo-with-a-script.md, cs-CZ: /cz/blog/import-csv-contacts-to-brevo-with-a-script.md, de-DE: /de/blog/import-csv-contacts-to-brevo-with-a-script.md, da-DK: /dk/blog/import-csv-contacts-to-brevo-with-a-script.md, es-ES: /es/blog/import-csv-contacts-to-brevo-with-a-script.md, fr-FR: /fr/blog/import-csv-contacts-to-brevo-with-a-script.md, el-GR: /gr/blog/import-csv-contacts-to-brevo-with-a-script.md, hi-IN: /hi/blog/import-csv-contacts-to-brevo-with-a-script.md, id-ID: /in/blog/import-csv-contacts-to-brevo-with-a-script.md, hr-HR: /hr/blog/import-csv-contacts-to-brevo-with-a-script.md, hu-HU: /hu/blog/import-csv-contacts-to-brevo-with-a-script.md, it-IT: /it/blog/import-csv-contacts-to-brevo-with-a-script.md, ja-JP: /jp/blog/import-csv-contacts-to-brevo-with-a-script.md, ko-KR: /kr/blog/import-csv-contacts-to-brevo-with-a-script.md, ar-SA: /ar/blog/import-csv-contacts-to-brevo-with-a-script.md, nl-NL: /nl/blog/import-csv-contacts-to-brevo-with-a-script.md, no-NO: /no/blog/import-csv-contacts-to-brevo-with-a-script.md, pl-PL: /pl/blog/import-csv-contacts-to-brevo-with-a-script.md, pt-BR: /pt/blog/import-csv-contacts-to-brevo-with-a-script.md, ro-RO: /ro/blog/import-csv-contacts-to-brevo-with-a-script.md, ru-RU: /ru/blog/import-csv-contacts-to-brevo-with-a-script.md, sv-SE: /se/blog/import-csv-contacts-to-brevo-with-a-script.md, zh-CN: /zh-cn/blog/import-csv-contacts-to-brevo-with-a-script.md, sl-SI: /sl/blog/import-csv-contacts-to-brevo-with-a-script.md, sr-Latn-RS: /sr/blog/import-csv-contacts-to-brevo-with-a-script.md, th-TH: /th/blog/import-csv-contacts-to-brevo-with-a-script.md, tr-TR: /tr/blog/import-csv-contacts-to-brevo-with-a-script.md, ur-PK: /ur/blog/import-csv-contacts-to-brevo-with-a-script.md, sk-SK: /sk/blog/import-csv-contacts-to-brevo-with-a-script.md}
---

[Đồng bộ dữ liệu](https://tajo.io/vi/blog/?category=data-sync)

# Cách nhập danh bạ CSV hoặc Excel vào Brevo bằng script (Python, Node.js, cURL)

Nhập hàng loạt danh bạ từ tệp CSV hoặc Excel vào Brevo bằng API import-contacts. Bao gồm script Python và Node.js sẵn sàng chạy, lệnh cURL một dòng, xử lý lỗi và mẹo cho tệp lớn hơn 10 MB.

import csv contacts to brevo

Endpoint API của Brevo để nhập danh bạ hàng loạt là gì?

Sử dụng POST /v3/contacts/import. Truyền CSV dưới dạng fileBody (≤10 MB), JSON dưới dạng jsonBody, hoặc URL qua fileUrl cho tệp lớn. Endpoint này bất đồng bộ và trả về processId. Ví dụ Python, Node.js và cURL bên dưới.

[Tìm hiểu thêm](https://tajo.io/vi/blog/import-csv-contacts-to-brevo-with-a-script/#t%E1%BB%95ng-quan-endpoint)

| Trường nguồn | Trường đích | Giá trị mẫu |
| --- | --- | --- |
| `EMAIL` | `EMAIL` | `alex@example.com` |
| `FIRSTNAME` | `FIRSTNAME` | `Alex` |
| `LASTNAME` | `LASTNAME` | `Example` |

Nếu bạn từng cần đẩy vài nghìn danh bạ từ một bảng tính vào Brevo, lộ trình UI thủ công nhanh chóng trở nên mệt mỏi: chọn tệp, ánh xạ cột, chọn danh sách, chờ, lặp lại. Một script làm cùng công việc đó trong vài giây và quan trọng hơn, bạn có thể chạy lại nó theo lịch, sau khi xuất từ cơ sở dữ liệu hoặc bất cứ khi nào CRM của bạn xuất ra một CSV mới.

Hướng dẫn này bao gồm endpoint API mà Brevo cung cấp đúng cho mục đích đó, với các script đầy đủ chạy được bằng Python, Node.js và một lệnh cURL một dòng. Mọi thứ trong hướng dẫn này đều gửi đến **`POST /v3/contacts/import`**.

## Tổng quan endpoint

```http
POST https://api.brevo.com/v3/contacts/import
Content-Type: application/json
api-key: YOUR_API_KEY

{
  "fileBody": "EMAIL;FIRSTNAME;LASTNAME\njane@example.com;Jane;Doe",
  "listIds": [42],
  "updateExistingContacts": true,
  "emailBlacklist": false,
  "smsBlacklist": false
}
```

Phản hồi trả về nhanh chóng:

```json
{ "processId": 78 }
```

Đó là 202 Accepted, Brevo đã chấp nhận import và đang xử lý trong nền. `processId` là handle theo dõi của bạn nếu bạn muốn polling để kiểm tra hoàn thành hoặc cài đặt webhook `notifyUrl`.

Một vài điều cần lưu ý trước khi viết code:

- **Body có thể là CSV (`fileBody`), JSON (`jsonBody`) hoặc URL từ xa (`fileUrl`).** Chọn một. Định dạng CSV sử dụng dấu chấm phẩy (`;`) để phân tách cột, không phải dấu phẩy. Đó là một cái bẫy phổ biến.
- **`fileBody` và `jsonBody` giới hạn 10 MB.** Brevo khuyến nghị giữ ở mức khoảng 8 MB để an toàn. Với bất cứ tệp nào lớn hơn, hãy tải tệp lên S3, GCS hoặc bất kỳ host HTTPS nào và truyền `fileUrl` thay thế.
- **Custom attributes không tồn tại trong tài khoản của bạn sẽ bị bỏ qua mà không báo lỗi.** Hãy tạo chúng trong UI của Brevo (hoặc qua Attributes API) trước khi import các hàng dùng chúng, nếu không dữ liệu sẽ biến mất.
- **`updateExistingContacts` mặc định là `true`.** Nếu bạn đặt thành `false`, Brevo bỏ qua các danh bạ có email đã tồn tại. Hữu ích cho các tác vụ “chỉ thêm mới”.
- **`emptyContactsAttributes`** kiểm soát việc các ô trống trong CSV có xóa giá trị hiện có hay không. Mặc định `false` (các ô trống bị bỏ qua). Đặt thành `true` nếu CSV của bạn là nguồn sự thật và bạn muốn các ô trống xóa dữ liệu cũ.

## Lấy API key

Đăng nhập vào Brevo → **Settings → SMTP & API → API Keys** → tạo key mới. Nó trông như `xkeysib-...`. Bạn sẽ truyền nó dưới dạng header HTTP `api-key` trong mọi request. Hãy coi nó như mật khẩu, nó có toàn quyền đọc/ghi trên tài khoản của bạn.

Một mẫu sạch sẽ: đặt nó vào biến môi trường để nó không bao giờ rơi vào source tree của bạn.

```bash
export BREVO_API_KEY="xkeysib-..."
```

## Python: đọc CSV, đẩy lên Brevo

Đây là script đơn giản nhất có thể: đọc CSV cục bộ với thư viện chuẩn, gửi body thẳng đến Brevo. Không cần SDK bên thứ ba ngoài `requests`.

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

Chạy nó:

```bash
python import_csv_to_brevo.py contacts.csv
```

Hàng đầu tiên của CSV là header. Tên cột ánh xạ tới các attribute của Brevo theo **chữ hoa, khớp chính xác**: `EMAIL`, `FIRSTNAME`, `LASTNAME`, cộng với bất kỳ custom attribute nào bạn đã định nghĩa. `EMAIL` là bắt buộc.

```csv
EMAIL,FIRSTNAME,LASTNAME,COMPANY,CITY
jane@example.com,Jane,Doe,Acme,Berlin
john@example.com,John,Smith,Globex,Paris
```

Đó là toàn bộ luồng. Script trả về trong vòng một giây; quá trình import thực tế chạy ở phía server và mất từ vài giây đến vài phút tùy vào khối lượng.

## Tệp Excel: ba lộ trình khả thi

Endpoint import của Brevo không đọc trực tiếp `.xlsx`, nên bạn có ba lựa chọn thực sự tùy vào nơi công việc cần diễn ra:

1. **Chạy macro VBA bên trong workbook**: đồng bộ một-cú-click từ chính Excel, không cần script bên ngoài. Đây là câu trả lời đúng khi tệp nằm trên desktop và nhóm bạn muốn một nút trên sheet. Code đầy đủ trong [hướng dẫn macro VBA Excel sang Brevo](https://tajo.io/vi/blog/import-excel-contacts-to-brevo-with-vba-macro).
2. **Office Scripts + Power Automate**: nếu workbook nằm trên OneDrive/SharePoint và bạn muốn đồng bộ theo lịch không cần giám sát. Cũng được nói tới trong [hướng dẫn Excel](https://tajo.io/vi/blog/import-excel-contacts-to-brevo-with-vba-macro).
3. **Chuyển đổi `.xlsx` sang CSV bằng script**: nội dung mà phần còn lại của mục này đề cập. Tốt nhất nếu bạn đã chạy Python hoặc Node trên server và chỉ cần kéo workbook về một lần mỗi ngày.

Với lựa chọn 3, pandas khiến nó thành một dòng duy nhất:

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

Nếu bạn không muốn pandas làm dependency, riêng `openpyxl` cũng đủ:

```python
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: cùng một việc, SDK chính thức

Brevo phát hành `@getbrevo/brevo` cho Node. Nó xử lý xác thực, retry và shape request có kiểu:

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

Hoặc, nếu bạn không muốn SDK, `fetch` thuần cũng hoạt động theo cùng cách:

```javascript
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: lệnh một dòng cho import tức thời

Khi bạn chỉ muốn test endpoint hoặc đẩy thủ công một tệp nhỏ vào:

```bash
# 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 .` nuốt toàn bộ tệp và escape JSON. Giúp bạn không phải escape thủ công các xuống dòng và dấu nháy.

## Tệp lớn hơn 10 MB: dùng `fileUrl`

`fileBody` giới hạn 10 MB. Nếu dump danh bạ của bạn lớn hơn, hãy lưu trữ tệp tại một URL mà Brevo có thể fetch và truyền nó:

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

Bất cứ thứ gì có thể truy cập qua URL HTTPS công khai đều hoạt động: URL S3 đã ký trước, GCS, host tĩnh của riêng bạn, thậm chí URL raw của GitHub cho import một lần. Brevo fetch tệp từ URL của bạn, sau đó chạy import. Định dạng được chấp nhận: `.csv`, `.txt`, `.json`.

## Gửi JSON thay vì CSV

Nếu bạn đã kéo danh bạ từ cơ sở dữ liệu, không cần đi vòng qua CSV, hãy gửi chúng dưới dạng JSON trực tiếp:

```python
body = {
    "jsonBody": [
        {
            "email": "jane@example.com",
            "attributes": {
                "FIRSTNAME": "Jane",
                "LASTNAME": "Doe",
                "COMPANY": "Acme",
            },
        },
        {
            "email": "john@example.com",
            "attributes": {
                "FIRSTNAME": "John",
                "LASTNAME": "Smith",
                "COMPANY": "Globex",
            },
        },
    ],
    "listIds": [42],
}
```

Cùng giới hạn 10 MB. Cùng hành vi bất đồng bộ.

## Polling để kiểm tra hoàn thành

Quá trình import bất đồng bộ, `processId` là handle để theo dõi nó. Có một endpoint riêng để kiểm tra trạng thái process:

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

Với các tác vụ chạy lâu, mẫu sạch hơn là `notifyUrl`: truyền một endpoint HTTPS mà Brevo sẽ POST đến khi import kết thúc và bỏ qua polling.

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

## Lỗi thường gặp và cách khắc phục

**`400 Bad Request` không có nguyên nhân rõ ràng**: hầu như luôn là vấn đề dấu chấm phẩy so với dấu phẩy trong CSV. Quá trình import của Brevo mong đợi `;`, không phải `,`. Kiểm tra lại `fileBody` sau bước chuyển đổi của bạn.

**Giá trị custom attribute biến mất**: Attribute không tồn tại trong tài khoản của bạn. Tạo nó trong **Contacts → Settings → Contact attributes** trước khi import, hoặc dùng [Attributes API](https://developers.brevo.com/reference/create-attribute) để tạo nó như một phần của script.

**`401 Unauthorized`**: Sai tên header. Đó là `api-key` (chữ thường, có dấu gạch nối), không phải `Authorization` hoặc `X-API-Key`.

**Import “thành công” nhưng danh bạ không xuất hiện trong list**: Kiểm tra xem `listIds` có chứa list đúng không. Ngoài ra: nếu `updateExistingContacts` là `false` và danh bạ đã tồn tại, Brevo âm thầm bỏ qua chúng thay vì thêm lại vào list.

**Một số hàng được import, một số không**: Brevo gửi email báo cáo lỗi từng hàng cho bạn sau khi import hoàn tất (trừ khi bạn đặt `disableNotification: true`). Báo cáo cho bạn biết hàng nào có email sai, thiếu trường bắt buộc hoặc các vấn đề định dạng.

## Khi nào nên dùng script và khi nào dùng UI

UI ổn cho các import một lần dưới một nghìn hàng. Một script thắng ngay khi:

- Bạn import nhiều hơn một lần (ví dụ xuất hàng tuần từ CRM của bạn)
- Dữ liệu nguồn cần làm sạch trước khi đưa vào Brevo (loại trùng lặp, định dạng số điện thoại, tách họ tên thành tên/họ)
- Bạn muốn nó chạy theo lịch mà không có ai bấm nút
- Tệp lớn hơn vùng thoải mái của UI

Bọc script ở trên trong một cron job hoặc một GitHub Action và bạn có đồng bộ danh bạ tự động. Bài tiếp theo trong loạt này hướng dẫn cách làm điều tương tự trực tiếp từ Google Sheet bằng Apps Script, không cần server.

## Đọc thêm

- [Brevo API: tham khảo Import contacts](https://developers.brevo.com/reference/import-contacts)
- [Brevo API: Contact attributes](https://developers.brevo.com/reference/create-attribute)
- [Xây dựng danh sách danh bạ cho các chiến dịch email của bạn](https://help.brevo.com/hc/en-us/articles/209499265)

## Câu hỏi Thường gặp

**Endpoint API của Brevo để nhập danh bạ hàng loạt là gì?**

POST https://api.brevo.com/v3/contacts/import. Endpoint chấp nhận nội dung CSV (fileBody), URL tệp từ xa (fileUrl) hoặc mảng JSON (jsonBody). Endpoint này bất đồng bộ, trả về processId ngay lập tức và hoàn tất việc nhập trong nền.

**Kích thước tệp tối đa là bao nhiêu?**

10 MB cho CSV inline (fileBody) hoặc JSON (jsonBody). Với tệp lớn hơn, hãy lưu trữ tệp ở nơi có thể truy cập được và truyền URL qua fileUrl. Cách này không có giới hạn kích thước nào được công bố.

**Làm thế nào để nhập tệp .xlsx?**

API import của Brevo chỉ chấp nhận .csv, .txt hoặc .json. Hãy chuyển .xlsx sang .csv trước. pandas, openpyxl hoặc LibreOffice headless có thể làm việc đó trong một dòng. Script trong hướng dẫn này bao gồm bước chuyển đổi xlsx sang csv.

**Có ghi đè danh bạ hiện có không?**

Mặc định là có. updateExistingContacts mặc định là true và đối chiếu theo email. Đặt thành false để bỏ qua các danh bạ đã tồn tại. Đặt emptyContactsAttributes thành true nếu bạn muốn các ô CSV trống xóa giá trị hiện có (nếu không, các ô trống sẽ bị bỏ qua).

## Bài viết liên quan

### [Nhập liên hệ Google Sheets vào Brevo bằng Apps Script](https://tajo.io/vi/blog/import-google-sheet-contacts-to-brevo-apps-script/)

30 thg 4, 2026 · 3 phút đọc

Google Sheets → Brevo

### [Cách đẩy danh bạ Excel vào Brevo bằng macro VBA (và lựa chọn Office Scripts)](https://tajo.io/vi/blog/import-excel-contacts-to-brevo-with-vba-macro/)

30 thg 4, 2026 · 7 phút đọc

Microsoft Excel → Brevo

### [Xuất dữ liệu và di chuyển Brevo: cách đưa dữ liệu vào hoặc ra](https://tajo.io/vi/blog/brevo-data-export-migration/)

19 thg 8, 2026 · 13 phút đọc

8 phần
