---
title: "Cómo importar contactos CSV o Excel a Brevo con un script (Python, Node.js, cURL)"
description: "Importa contactos en bloque desde un archivo CSV o Excel a Brevo usando la API import-contacts. Incluye scripts listos para usar en Python y Node.js, un one-liner de cURL, manejo de errores y consejos para archivos de más de 10 MB."
url: https://tajo.io/es/blog/import-csv-contacts-to-brevo-with-a-script/
markdown: https://tajo.io/es/blog/import-csv-contacts-to-brevo-with-a-script.md
lang: es-ES
author: "Set Noa"
author_url: https://tajo.io/es/set-noa/
published: 2026-04-30
updated: 2026-05-12
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, 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, vi-VN: /vi/blog/import-csv-contacts-to-brevo-with-a-script.md, sk-SK: /sk/blog/import-csv-contacts-to-brevo-with-a-script.md}
---

[Sincronización de datos](https://tajo.io/es/blog/?category=data-sync)

# Cómo importar contactos CSV o Excel a Brevo con un script (Python, Node.js, cURL)

Importa contactos en bloque desde un archivo CSV o Excel a Brevo usando la API import-contacts. Incluye scripts listos para usar en Python y Node.js, un one-liner de cURL, manejo de errores y consejos para archivos de más de 10 MB.

import csv contacts to brevo

¿Qué endpoint de Brevo importa contactos en bloque?

Usa POST /v3/contacts/import. Envía CSV como fileBody (≤10 MB), JSON como jsonBody, o una URL vía fileUrl para archivos grandes. El endpoint es asíncrono y devuelve un processId. Más abajo tienes ejemplos en Python, Node.js y cURL.

[Saber más](https://tajo.io/es/blog/import-csv-contacts-to-brevo-with-a-script/#el-endpoint-de-un-vistazo)

| Campo de origen | Campo de destino | Valor de ejemplo |
| --- | --- | --- |
| `EMAIL` | `EMAIL` | `alex@example.com` |
| `FIRSTNAME` | `FIRSTNAME` | `Alex` |
| `LASTNAME` | `LASTNAME` | `Example` |

Si alguna vez has tenido que mover unos miles de contactos desde una hoja de cálculo a Brevo, ya sabes que la ruta manual de la interfaz se vuelve pesada rápido: elegir el archivo, mapear columnas, elegir una lista, esperar, repetir. Un script hace lo mismo en segundos y, lo que es más importante, puedes volver a ejecutarlo de forma programada, después de un export de la base de datos o cada vez que tu CRM escupa un CSV nuevo.

Esta guía cubre el endpoint de la API que Brevo te da exactamente para esto, con scripts completos y funcionales en Python, Node.js y un one-liner de cURL. Todo en esta guía pega contra **`POST /v3/contacts/import`**.

## El endpoint de un vistazo

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

La respuesta vuelve rápido:

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

Eso es un 202 Accepted: Brevo aceptó la importación y la está procesando en segundo plano. El `processId` es tu manija para hacer seguimiento si quieres consultar el estado o configurar un webhook con `notifyUrl`.

Algunos puntos que vale la pena conocer antes de escribir código:

- **El cuerpo puede ser CSV (`fileBody`), JSON (`jsonBody`) o una URL remota (`fileUrl`).** Elige uno. La forma CSV usa punto y coma (`;`) para separar columnas, no comas, esa es una trampa habitual.
- **`fileBody` y `jsonBody` están limitados a 10 MB.** Brevo recomienda quedarse alrededor de 8 MB para ir sobre seguro. Para algo más grande, sube el archivo a S3, GCS o cualquier host HTTPS y pasa `fileUrl` en su lugar.
- **Los atributos personalizados que no existen en tu cuenta se ignoran en silencio.** Créalos en la interfaz de Brevo (o vía la API de Atributos) antes de importar filas que los usen, de lo contrario los datos simplemente desaparecen.
- **`updateExistingContacts` por defecto es `true`.** Si lo pones en `false`, Brevo se salta los contactos cuyo email ya existe, útil para tareas de “solo añadir nuevos”.
- **`emptyContactsAttributes`** controla si las celdas vacías del CSV borran los valores existentes. Por defecto `false` (las celdas vacías se ignoran). Ponlo en `true` si tu CSV es la fuente de verdad y quieres que los blancos borren los datos obsoletos.

## Conseguir una API key

Inicia sesión en Brevo → **Configuración → SMTP & API → API Keys** → crea una nueva clave. Tendrá la forma `xkeysib-...`. La pasarás como header HTTP `api-key` en cada petición. Trátala como una contraseña, tiene acceso de lectura/escritura completo a tu cuenta.

Un patrón limpio: ponla en una variable de entorno para que nunca acabe en tu árbol de fuentes.

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

## Python: leer un CSV y enviarlo a Brevo

Este es el script más simple posible: leer un CSV local con la biblioteca estándar y enviar el cuerpo directamente a Brevo. No necesitas ningún SDK de terceros aparte de `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}")
```

Ejecútalo:

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

La primera fila de tu CSV es la cabecera. Los nombres de columna se mapean a los atributos de Brevo por **mayúsculas, coincidencia exacta**: `EMAIL`, `FIRSTNAME`, `LASTNAME`, además de cualquier atributo personalizado que hayas definido. `EMAIL` es obligatorio.

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

Ese es todo el flujo. El script vuelve en menos de un segundo; la importación real corre en el servidor y tarda entre unos pocos segundos y unos minutos según el volumen.

## Archivos Excel: tres caminos viables

El endpoint de importación de Brevo no lee `.xlsx` directamente, así que tienes tres opciones reales según dónde necesites que viva el trabajo:

1. **Ejecutar una macro VBA dentro del libro**, sincronización de un clic desde el propio Excel, sin script externo. Es la respuesta correcta cuando el archivo vive en un escritorio y tu equipo quiere un botón en la hoja. Código completo en la [guía de macro VBA de Excel a Brevo](https://tajo.io/es/blog/import-excel-contacts-to-brevo-with-vba-macro).
2. **Office Scripts + Power Automate**, si el libro vive en OneDrive/SharePoint y quieres sincronización programada y desatendida. También se cubre en la [guía de Excel](https://tajo.io/es/blog/import-excel-contacts-to-brevo-with-vba-macro).
3. **Convertir `.xlsx` a CSV desde un script**, lo que cubre el resto de esta sección. Es la mejor opción si ya tienes Python o Node corriendo en un servidor y solo necesitas tirar de un libro una vez al día.

Para la opción 3, pandas lo hace en una línea:

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

Si no quieres pandas como dependencia, `openpyxl` solo también funciona:

```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: el mismo trabajo, SDK oficial

Brevo publica `@getbrevo/brevo` para Node. Maneja la autenticación, los reintentos y la forma tipada de la petición:

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

O, si no quieres el SDK, `fetch` plano funciona igual:

```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: un one-liner para importaciones puntuales

Cuando solo quieres probar el endpoint o meter un archivo pequeño a mano:

```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 .` se traga el archivo entero y lo escapa como JSON, te ahorra escapar manualmente saltos de línea y comillas.

## Archivos de más de 10 MB: usa `fileUrl`

`fileBody` está limitado a 10 MB. Si tu volcado de contactos es mayor, aloja el archivo en una URL que Brevo pueda descargar y pásala:

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

Cualquier cosa accesible vía una URL HTTPS pública funciona: URLs prefirmadas de S3, GCS, tu propio host estático, incluso una URL raw de GitHub para importaciones puntuales. Brevo descarga el archivo desde tu URL y luego ejecuta la importación. Formatos aceptados: `.csv`, `.txt`, `.json`.

## Enviar JSON en lugar de CSV

Si ya estás sacando contactos de una base de datos, no necesitas dar la vuelta por CSV: envíalos directamente como JSON:

```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],
}
```

Mismo límite de 10 MB. Mismo comportamiento asíncrono.

## Sondeo del estado

La importación es asíncrona, `processId` es tu manija para seguirla. Hay un endpoint separado para consultar el estado del proceso:

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

Para trabajos largos, el patrón más limpio es `notifyUrl`: pasa un endpoint HTTPS al que Brevo hará POST cuando termine la importación, y ahórrate el sondeo.

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

## Errores comunes y cómo solucionarlos

**`400 Bad Request` sin causa obvia.** Casi siempre es punto y coma frente a comas en el CSV. La importación de Brevo espera `;`, no `,`. Vuelve a comprobar `fileBody` después de tu paso de conversión.

**Los valores de atributos personalizados desaparecen.** El atributo no existe en tu cuenta. Créalo en **Contactos → Configuración → Atributos de contacto** antes de importar, o usa la [API de Atributos](https://developers.brevo.com/reference/create-attribute) para crearlo como parte de tu script.

**`401 Unauthorized`.** Nombre de header equivocado. Es `api-key` (en minúsculas, con guion), no `Authorization` ni `X-API-Key`.

**La importación dice “succeeded” pero los contactos no aparecen en la lista.** Comprueba que `listIds` contiene la lista correcta. Además: si `updateExistingContacts` es `false` y los contactos ya existen, Brevo los salta en silencio en lugar de volver a añadirlos a la lista.

**Algunas filas se importaron, otras no.** Brevo te envía por email un informe de errores fila a fila después de que termine la importación (a no ser que pongas `disableNotification: true`). El informe te dice qué filas tenían emails malos, faltaban campos obligatorios o tenían problemas de formato.

## Cuándo hacer un script y cuándo usar la interfaz

La interfaz está bien para importaciones puntuales por debajo de mil filas. Un script gana en cuanto:

- Vas a importar más de una vez (por ejemplo, un export semanal de tu CRM)
- Los datos de origen necesitan limpieza antes de aterrizar en Brevo (deduplicación, formateo de números de teléfono, separar nombre y apellido)
- Quieres que corra de forma programada sin que nadie haga clic
- El archivo es más grande de lo que la interfaz maneja con comodidad

Envuelve el script de arriba en un cron job o una GitHub Action y tienes sincronización automática de contactos. El siguiente post de esta serie muestra cómo hacer lo mismo directamente desde un Google Sheet usando Apps Script, sin servidor.

## Lecturas adicionales

- [Brevo API: referencia de importar contactos](https://developers.brevo.com/reference/import-contacts)
- [Brevo API: atributos de contacto](https://developers.brevo.com/reference/create-attribute)
- [Construir listas de contactos para tus campañas de email](https://help.brevo.com/hc/en-us/articles/209499265)

## Preguntas frecuentes

**¿Cuál es el endpoint de la API de Brevo para importar contactos en bloque?**

POST https://api.brevo.com/v3/contacts/import. Acepta contenido CSV (fileBody), una URL de archivo remoto (fileUrl) o un array JSON (jsonBody). El endpoint es asíncrono: devuelve un processId al instante y termina la importación en segundo plano.

**¿Cuál es el tamaño máximo del archivo?**

10 MB para CSV en línea (fileBody) o JSON (jsonBody). Para archivos más grandes, alójalo en un sitio accesible y pasa su URL mediante fileUrl, esa vía no tiene un límite de tamaño documentado.

**¿Cómo importo un archivo .xlsx?**

La API de importación de Brevo solo acepta .csv, .txt o .json. Convierte primero el .xlsx a .csv: pandas, openpyxl o LibreOffice headless lo hacen en una línea. El script de esta guía incluye una conversión xlsx a csv.

**¿Sobrescribirá los contactos existentes?**

Por defecto, sí: updateExistingContacts está en true y hace match por email. Ponlo en false para saltarte los contactos que ya existen. Pon emptyContactsAttributes en true si quieres que las celdas vacías del CSV borren los valores existentes (de lo contrario, las celdas vacías se ignoran).

## Artículos relacionados

### [Importar contactos de Google Sheets a Brevo con Apps Script](https://tajo.io/es/blog/import-google-sheet-contacts-to-brevo-apps-script/)

30 abr 2026 · 3 min de lectura

Google Sheets → Brevo

### [Cómo enviar contactos de Excel a Brevo con una macro VBA (y la alternativa con Office Scripts)](https://tajo.io/es/blog/import-excel-contacts-to-brevo-with-vba-macro/)

30 abr 2026 · 7 min de lectura

Microsoft Excel → Brevo

### [Exportación y migración de datos en Brevo: cómo mover tus datos hacia dentro o hacia fuera](https://tajo.io/es/blog/brevo-data-export-migration/)

19 ago 2026 · 11 min de lectura

8 secciones
