API de Brevo: guía práctica para desarrolladores

Guía de la API de Brevo: autenticación, actualizaciones de contactos, webhooks, límites de tasa y reintentos seguros.

Brevo API

¿Cuál es la respuesta corta?

Usa https://api.brevo.com/v3/ con una cabecera api-key. Para sincronizar contactos, elige un ID de origen estable, atributos declarados y una lista explícita, y conserva las bajas. Comprueba el resultado, lee las cabeceras de límite, acota los reintentos tras un 429 y trata los webhooks como señales autenticadas que requieren deduplicación y reconciliación.

Saber más
En esta página

Una importación de contactos puede devolver 201 y seguir estando mal: se perdió el ID de origen, se eligió la lista equivocada o se ignoró una baja anterior. Son decisiones sobre quién gobierna los datos, no detalles HTTP.

Esta guía muestra primero cómo hacer una solicitud a Brevo y luego las comprobaciones que vuelven repetible una pequeña sincronización.

Qué cubre la API de Brevo

Todo vive bajo un único host y una única ruta de versión. La documentación para desarrolladores agrupa la superficie en cuatro áreas de producto:

  • Mensajería: email transaccional, SMS y WhatsApp, incluidos los envíos por lotes, la programación y la actividad de los mensajes.
  • Plataforma de marketing: contactos, listas, segmentos y campañas de email.
  • eCommerce: productos, pedidos y seguimiento de eventos de cliente.
  • Conversaciones: el widget de chat y la gestión programática de conversaciones.

Esas áreas comparten una cuenta, una base de datos de contactos y una clave de API. Eso es cómodo y de vez en cuando peligroso: un script escrito pensando en datos de staging está hablando con los mismos contactos a los que envían tus campañas.

Transaccional frente a marketing

Las dos familias se comportan de forma lo bastante distinta como para que confundirlas sea el error de diseño más habitual.

TransaccionalMarketing
Endpoint principalPOST /v3/smtp/emailPOST /v3/emailCampaigns
DestinatariosExplícitos en la solicitudlistIds o segmentIds
DisparadorTu aplicación, en tiempo realProgramado o enviado a demanda
Forma habitual del volumenContinuo, un mensaje cada vezA ráfagas, un envío grande
Postura ante los límites de tasaMuy alta, 1.000 solicitudes por segundo en el nivel generalBaja, los endpoints de campaña caen bajo el límite general

Si todavía estás decidiendo si Brevo es la plataforma adecuada, la visión general de la plataforma cubre ese terreno.

Autenticación y gestión de claves

Para una clave de API estándar, Brevo usa la cabecera api-key; algunas operaciones también aceptan tokens OAuth bearer. La cabecera se llama api-key, no Authorization, y no lleva prefijo Bearer. Esto despista a casi todo el mundo que ha usado antes otra API de mensajería.

OAuth puede usar un token Bearer en operaciones compatibles; una clave API estándar se envía en api-key.

Terminal window
curl https://api.brevo.com/v3/account \
-H "api-key: $BREVO_API_KEY"

Las claves se generan en la aplicación de Brevo, dentro de los ajustes de la cuenta, en la sección SMTP y API, en la pestaña de claves de API. Dale a cada clave un nombre descriptivo ligado al sistema que la usa. El valor de la clave se muestra exactamente una vez cuando se genera, así que si la pierdes generas una nueva en lugar de recuperar la antigua.

Unas cuantas reglas prácticas:

  • Emite una clave separada por entorno de despliegue y por servicio. Revocar una clave comprometida nunca debería tumbar tres sistemas que no tienen nada que ver.
  • Las claves de API estándar son de ámbito de cuenta. Trata cualquier clave como acceso total a contactos, envíos y datos de CRM.
  • Brevo también admite OAuth 2.0 para aplicaciones que actúan en nombre de otras cuentas de Brevo, descrito junto al flujo de claves en authentication schemes.
  • El servidor MCP que usan los asistentes de AI toma un token aparte y ese sí usa una cabecera bearer. Ese token se genera en la misma pantalla de claves de API, pero no es intercambiable con una clave REST.

URL base, versionado y tu primera escritura

La URL base es https://api.brevo.com/v3/. La versión va en la ruta y no en una cabecera, y v3 es la generación actual. Todas las rutas de esta guía son relativas a esa base.

Para la primera escritura de contacto, usa una dirección de prueba bajo tu control, un ID de lista real y un ID de origen que no cambie. Crea antes el atributo FNAME en Brevo. Con una clave real, este ejemplo escribe en la cuenta:

Terminal window
curl -X POST https://api.brevo.com/v3/contacts \
-H "api-key: $BREVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"ext_id": "test-source-contact-1",
"attributes": { "FNAME": "Dev" },
"listIds": [42],
"updateEnabled": true,
"getId": true
}'

Sustituye 42 por el ID de tu lista de prueba. Vuelve a leer el contacto y comprueba ID, nombre, lista y bloqueos. Un 2xx por sí solo no prueba que la sincronización sea correcta.

Los endpoints que vas a usar de verdad

Contactos

POST /v3/contacts crea un contacto. El cuerpo admite email, attributes declarados, listIds y ext_id como clave de origen estable. updateEnabled: true actualiza un contacto existente y getId: true pide el ID resultante. Brevo documenta estos campos. Reutiliza el mismo ext_id en cada ejecución y conserva la correspondencia entre IDs de origen y de Brevo para reconciliar cambios.

Antes de escribir, decide qué sistema gobierna cada atributo y qué lista recibe a los contactos con consentimiento. Indica listIds de forma explícita. Nunca establezcas emailBlacklisted: false en una actualización rutinaria. Brevo advierte que cambiar el correo de un contacto bloqueado elimina el bloqueo. Trata ese cambio por separado. Comprueba bloqueos y bajas antes de añadirlo a una lista de marketing. Sigue existiendo una carrera entre lectura y escritura; si necesitas una garantía estricta, haz una última comprobación antes de enviar.

Las lecturas pasan por GET /v3/contacts, que pagina con limit (50 por defecto, 1000 como máximo) y offset, y admite modifiedSince y createdSince en UTC. Las sincronizaciones incrementales deberían apoyarse en modifiedSince en lugar de recorrer la lista completa. Ten en cuenta que el parámetro filter solo admite un operador de igualdad, así que cualquier cosa más expresiva pertenece a un segmento.

Para carga masiva, POST /v3/contacts/import acepta fileUrl, fileBody o jsonBody, apunta a listIds y se ejecuta de forma asíncrona devolviendo un processId. Brevo documenta un máximo de 10 MB por cuerpo y recomienda quedarse cerca de 8 MB porque el parseo infla la carga útil. Facilita notifyUrl para enterarte del resultado en lugar de hacer polling.

Email transaccional

POST /v3/smtp/email es el caballo de batalla. Más allá de sender, to, subject y htmlContent, los campos que merece la pena conocer son:

  • templateId con params, que sustituye el contenido en línea por una plantilla de Brevo y sus sustituciones de variables. Los params de cada versión están limitados a 100 KB y los acumulados a 1000 KB.
  • messageVersions, que envía variantes personalizadas en una sola llamada, con hasta 99 destinatarios por versión.
  • tags pueden ayudar a relacionar los eventos de entrega con el flujo de la aplicación que los produjo.
  • scheduledAt junto con batchId, para envíos futuros que quizá quieras cancelar en grupo.
  • headers, en Title-Case, para cabeceras SMTP personalizadas.

Una sola solicitud acepta como máximo 2.000 destinatarios. Para la diferencia entre este endpoint y el envío de campañas, la guía de email transaccional ofrece la visión de estrategia de mensajería.

Campañas de email

POST /v3/emailCampaigns requiere name y sender, además de exactamente una fuente de contenido: htmlContent (mínimo 10 caracteres, menos de 1 MB), htmlUrl o templateId. La audiencia va en recipients como listIds o segmentIds, y scheduledAt usa el formato UTC YYYY-MM-DDTHH:mm:ss.SSSZ. Las rutas complementarias cubren el envío inmediato, el envío de una prueba, la actualización del estado y la descarga del informe de campaña.

Empresas, negocios y objetos

El CRM de Brevo tiene dos rutas de escritura solapadas, y elegir bien importa.

Las rutas de CRM son POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} y el conjunto equivalente para los negocios. Son síncronas. Un PATCH devuelve 204 en cuanto se aplica el cambio.

La API de objetos es la vía masiva: POST /v3/objects/{object_type}/batch/upsert acepta hasta 1000 registros y 1 MB por solicitud, hasta 500 atributos por registro y hasta 10 registros de asociación por tipo de objeto y registro. Devuelve 202 con un processId, lo que significa aceptado y no aplicado.

Terminal window
curl -X POST https://api.brevo.com/v3/objects/company/batch/upsert \
-H "api-key: $BREVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"identifiers": { "id": 12345 },
"attributes": { "domain": "acme.example", "industry": "retail" }
}
]
}'

La guía de Brevo CRM cubre el modelo de objetos desde el lado del operador.

SDK oficiales

Brevo mantiene clientes en la organización de GitHub getbrevo:

LenguajeRepositorio
Node.jsgithub.com/getbrevo/brevo-node
Pythongithub.com/getbrevo/brevo-python
PHPgithub.com/getbrevo/brevo-php
Javagithub.com/getbrevo/brevo-java
C#github.com/getbrevo/brevo-csharp
Gogithub.com/getbrevo/brevo-go
Rubygithub.com/getbrevo/brevo-ruby

El cliente de Node se instala como @getbrevo/brevo:

Terminal window
npm install @getbrevo/brevo
import { BrevoClient } from "@getbrevo/brevo";
// Reconcile uncertain send outcomes before retrying.
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY, maxRetries: 0 });
const result = await brevo.transactionalEmails.sendTransacEmail({
subject: "Order confirmed",
htmlContent: "<html><body><p>Thanks for your order.</p></body></html>",
sender: { name: "Acme", email: "[email protected]" },
to: [{ email: "[email protected]", name: "Customer" }],
tags: ["order-confirmation"],
});
console.log("Message ID:", result.messageId);

El cliente de Python se instala con pip install brevo-python. Si prefieres no arrastrar una dependencia de SDK para dos endpoints, la superficie HTTP en crudo es lo bastante pequeña como para llamarla directamente, lo que además te aísla de los cambios de versión del SDK:

El cliente de Python se instala con pip install brevo-python. Para unos pocos endpoints también puedes llamar a HTTP directamente:

import os
import requests
BASE = "https://api.brevo.com/v3"
HEADERS = {"api-key": os.environ["BREVO_API_KEY"]}
def upsert_consented_contact(source_id, email, first_name, list_id, consent):
if not consent:
raise ValueError("No marketing consent in the source")
if not source_id or not isinstance(list_id, int):
raise ValueError("Stable source ID and numeric list ID required")
# Configure FNAME in Brevo before using this explicit field mapping.
response = requests.post(
f"{BASE}/contacts",
headers=HEADERS,
json={
"email": email,
"ext_id": source_id,
"attributes": {"FNAME": first_name},
"listIds": [list_id],
"updateEnabled": True,
"getId": True,
},
timeout=30,
)
response.raise_for_status()
return response.json().get("id") if response.content else None

Una actualización correcta puede devolver 204 sin cuerpo. En ese caso, None indica que debes volver a leer el contacto para obtener su ID.

Este ejemplo supone que el código que llama ya ha comprobado el consentimiento y los bloqueos en Brevo. No solicita quitar ningún bloqueo. Con una clave real, el código escribe en la cuenta; pruébalo antes con respuestas simuladas. Los conflictos de identidad pueden devolver distintos códigos 4xx según el endpoint. Lee la respuesta y reconcilia los IDs en vez de suponer siempre un 409.

Webhooks

Los webhooks son la forma de enterarte de lo que pasó después de un envío. POST /v3/webhooks crea uno, con url, events, type y, de forma opcional, channel (email o sms), batched, headers personalizadas y un objeto auth.

Hay tres tipos de webhook con vocabularios de evento distintos:

  • Transaccional: sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened, unsubscribed.
  • Marketing: spam, opened, click, hardBounce, softBounce, unsubscribed, listAddition, delivered, contactUpdated, contactDeleted.
  • Entrante: inboundEmailProcessed y reply, que además requieren un domain.
Terminal window
curl -X POST https://api.brevo.com/v3/webhooks \
-H "api-key: $BREVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.example/hooks/brevo",
"type": "transactional",
"events": ["delivered", "hardBounce", "spam", "unsubscribed"],
"description": "Deliverability signals"
}'

Para cambios de contactos, registra un webhook de marketing con contactUpdated. La carga útil documentada llama al evento contact_updated. Autentica el receptor con el token bearer de auth o las cabeceras personalizadas del manual de seguridad de Brevo. Valida el secreto antes de procesar el evento y no lo registres.

Guarda una clave de deduplicación formada por tipo de evento, identificador de contacto y marca temporal durante un periodo suficiente para cubrir reintentos. La marca temporal sola no es única. Las entregas pueden repetirse o llegar fuera de orden. Tras cada evento, consulta el contacto actual y reconcilia el estado con tu fuente autorizada. Define una ventana de reproducción, rechaza eventos antiguos al recuperarte y ejecuta una revisión incremental periódica. Un webhook señala un cambio; por sí solo no crea una sincronización bidireccional. La guía de conectores explica las opciones.

Límites de tasa y gestión de errores

Los límites de tasa de Brevo son por endpoint y por nivel de plan, y la diferencia entre endpoints es enorme.

EndpointGeneral (todos los planes)Advanced (Professional y Enterprise)
POST /v3/smtp/email1.000 RPS2.000 RPS
POST /v3/transactionalSMS/send150 RPS200 RPS
/v3/contacts/...10 RPS, 36.000 RPH20 RPS, 72.000 RPH
POST /v3/events10 RPS, 36.000 RPH20 RPS, 72.000 RPH
GET /v3/smtp/emails2 RPS, 7.200 RPH3 RPS, 10.800 RPH
Endpoints sin límite propio100 RPH200 RPH

Brevo documenta x-sib-ratelimit-limit, x-sib-ratelimit-remaining y x-sib-ratelimit-reset para los endpoints limitados. Léelas también tras un éxito. Ante un 429, espera al reinicio y reintenta dentro de un presupuesto fijo de intentos y demora. El valor de reset es una duración en la unidad de la ventana, normalmente segundos, no una marca de tiempo Unix.

async function callBrevo(path, init) {
for (let attempt = 0; attempt < 3; attempt++) {
const response = await fetch(`https://api.brevo.com/v3${path}`, {
...init,
headers: { "api-key": process.env.BREVO_API_KEY, ...init.headers },
});
if (response.status !== 429 || attempt === 2) return response;
const raw = Number(response.headers.get("x-sib-ratelimit-reset"));
const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt;
const delayMs = (seconds + 2 ** attempt) * 1000;
if (delayMs > 60_000) return response;
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}

Si la espera necesaria supera 60 segundos, la función devuelve 429. Programa otro intento para más tarde.

Este auxiliar solo sirve para solicitudes cuya repetición hayas comprobado que es segura. Tras un fallo de envío ambiguo, reconcilia primero tu identificador de mensaje antes de volver a enviar. Registra el estado y las cabeceras de límite, nunca la clave API ni los datos del contacto.

No repitas un 4xx a ciegas. La referencia de creación de contactos enumera 400 y 425; un conflicto de identidad no tiene por qué devolver 409. Tras un 5xx en una escritura, comprueba si el cambio ya se aplicó.

¿Qué estado devolvió?

  1. 429

    Espera con un límite de reintentos

    Usa la duración de reset y detente tras unos pocos intentos.

  2. 5xx después de escribir

    Comprueba si se aplicó

    El servidor puede haber aplicado el cambio antes del error.

  3. 400

    Corrige la solicitud

    Normalmente la solicitud es incorrecta.

  4. Conflicto de identidad

    Lee y reconcilia los IDs

    La API no promete un código de conflicto concreto.

Probar sin enviar correo

Añade la cabecera X-Sib-Sandbox con el valor drop a un envío transaccional. Brevo valida la solicitud, devuelve 201 con un messageId, no entrega nada y no escribe ningún registro de email.

Terminal window
curl -X POST https://api.brevo.com/v3/smtp/email \
-H "api-key: $BREVO_API_KEY" \
-H "X-Sib-Sandbox: drop" \
-H "Content-Type: application/json" \
-d '{ "sender": { "email": "[email protected]" }, "to": [{ "email": "[email protected]" }], "subject": "Sandbox", "htmlContent": "<p>hi</p>" }'

Entiende qué demuestra esto y qué no. El modo sandbox valida solo el formato de la solicitud. No dice nada sobre la autenticación del remitente, el renderizado de plantillas ni la entregabilidad. Mantén una cuenta de Brevo aparte para las pruebas de integración de todo lo que toque contactos o datos de CRM, porque el modo sandbox cubre el envío y no el resto de la API.

Antes de sincronizar contactos

  • Elige un ID de origen estable para ext_id y conserva el ID de Brevo devuelto.
  • Verifica los atributos antes de escribir. Brevo puede ignorar campos desconocidos o con tipos erróneos; vuelve a leer un contacto de prueba.
  • Usa un ID de lista explícito y un registro de consentimiento autorizado. Omite bloqueos y bajas existentes.
  • Cuenta por separado registros aceptados, aplicados, omitidos y fallidos. Un 202 en lotes u objetos solo confirma su aceptación inicial.
  • Usa los webhooks de marketing como señales y reconcilia luego el estado actual. Autentica, deduplica y limita la reproducción.
  • Observa las cabeceras de límite y detente tras pocos reintentos.

Tajo se centra en sincronizar los datos comerciales con Brevo. Si construyes esta capa, la guía de conectores de Brevo compara los caminos y sus fallos.

Preguntas frecuentes

¿Cuál es la URL base de la API de Brevo?

Todas las llamadas REST de Brevo van a https://api.brevo.com/v3/. La versión forma parte de la ruta y v3 es la generación actual, así que un endpoint como el envío transaccional es la ruta completa https://api.brevo.com/v3/smtp/email.

¿Cómo te autenticas en la API de Brevo?

Envía tu clave en una cabecera HTTP llamada api-key. Brevo no usa una cabecera Authorization ni un prefijo Bearer para las claves de API estándar. Las claves se generan en los ajustes de la cuenta, en la sección SMTP y API, y el valor se muestra una sola vez.

¿Cuáles son los límites de tasa de la API de Brevo?

Los límites son por endpoint y por nivel de límite de tasa. En el nivel general, que tienen todos los planes, el endpoint de envío transaccional permite 1.000 solicitudes por segundo, los endpoints de contactos permiten 10 por segundo y los endpoints sin límite propio están limitados a 100 solicitudes por hora. Los planes Professional y Enterprise pueden usar el nivel Advanced, y Enterprise el nivel Extended. Superar un límite devuelve 429.

¿Brevo tiene SDK oficiales?

Sí. Brevo publica clientes para Node.js, Python, PHP, Java, C#, Go y Ruby en la organización de GitHub getbrevo. El paquete de Node es @getbrevo/brevo en npm y el de Python es brevo-python en PyPI.

¿Cómo pruebo la API de Brevo sin enviar correos reales?

Añade la cabecera X-Sib-Sandbox con el valor drop a un envío transaccional. Brevo valida la solicitud, devuelve 201 con un messageId, no envía nada y no escribe ningún registro de email. Solo comprueba el formato de la solicitud, no la entregabilidad.

¿Qué eventos de webhook admite Brevo?

Los webhooks transaccionales cubren, entre otros, entregas, rebotes y bajas. Los de marketing incluyen contactUpdated y contactDeleted. Para eventos entrantes, Brevo documenta inboundEmailProcessed.

¿Cómo protege una actualización de contacto una baja previa?

Usa un ID de origen estable y atributos declarados. No establezcas emailBlacklisted en false en actualizaciones normales. Comprueba el contacto existente y tu fuente de consentimiento antes de añadirlo a una lista de marketing.

¿Basta un webhook para una sincronización bidireccional?

No. Autentica el receptor, elimina entregas duplicadas y reconcilia los eventos perdidos o desordenados con una fuente autorizada. Un webhook señala un cambio, pero no prueba que ambos sistemas coincidan.

Solicita acceso anticipado

Escribe tu nombre y dirección de correo de trabajo. Te enviaremos un enlace para confirmar tu solicitud de acceso a Tajo.

Obtener Brevo