API da Brevo: guia prático para desenvolvedores

Guia da API da Brevo para desenvolvedores: autenticação, URL base, contatos, e-mail transacional, campanhas, objetos de CRM, webhooks, limites de taxa e limites reais.

Brevo API
API da Brevo?

A Brevo expõe uma única API REST que abrange mensagens transacionais, campanhas de marketing, dados de contato e registros de CRM. Fazer a primeira requisição devolver 201 leva cerca de dois minutos. Chegar a uma integração de produção que não perca dados em silêncio leva bem mais tempo, porque várias das restrições que mais importam são pouco documentadas ou contradizem o que a própria API informa sobre si mesma.

Este guia cobre as duas metades: os endpoints, SDKs e a autenticação de que você precisa no primeiro dia, e os limites da plataforma que você precisa contornar no desenho antes de colocar em produção.

O que a API da Brevo cobre

Tudo vive sob um único host e um único caminho de versão. A documentação para desenvolvedores agrupa a superfície em quatro áreas de produto:

  • Mensagens: e-mail transacional, SMS e WhatsApp, incluindo envios em lote, agendamento e atividade das mensagens.
  • Plataforma de marketing: contatos, listas, segmentos e campanhas de e-mail.
  • E-commerce: produtos, pedidos e rastreamento de eventos do cliente.
  • Conversas: o widget de chat e a gestão programática de conversas.

Essas áreas compartilham uma conta, um banco de contatos e uma chave de API. Isso é conveniente e ocasionalmente perigoso: um script escrito com uma ideia de dados de homologação está falando com os mesmos contatos para os quais suas campanhas enviam.

Transacional versus marketing

As duas famílias se comportam de forma bastante diferente, e confundi-las é o erro de design mais comum.

TransacionalMarketing
Endpoint principalPOST /v3/smtp/emailPOST /v3/emailCampaigns
EndereçamentoDestinatários explícitos na requisiçãolistIds ou segmentIds
DisparoSua aplicação, em tempo realAgendado ou enviado sob demanda
Formato típico de volumeContínuo, uma mensagem por vezEm picos, um envio grande
Postura de limite de taxaMuito alta, 1.000 requisições por segundo nos planos padrãoBaixa, os endpoints de campanha caem no limite geral

Se você ainda está decidindo se a Brevo é a plataforma certa, a visão geral da plataforma cobre esse terreno.

Autenticação e gestão de chaves

A Brevo usa uma chave de API simples em um cabeçalho personalizado. O cabeçalho se chama api-key, não Authorization, e não existe prefixo Bearer. Isso derruba quase todo mundo que já usou outra API de mensagens antes.

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

As chaves são geradas no app da Brevo, nas configurações da conta, na seção SMTP e API, na aba de chaves de API. Dê a cada chave um nome descritivo ligado ao sistema que a usa. O valor da chave aparece exatamente uma vez, no momento em que é gerada, então, se você perdê-lo, gera uma nova chave em vez de recuperar a antiga.

Algumas regras práticas:

  • Emita uma chave separada por ambiente de deploy e por serviço. Revogar uma chave comprometida nunca deveria derrubar três sistemas sem relação entre si.
  • As chaves de API padrão valem para a conta inteira. Trate qualquer chave como acesso total a contatos, envios e dados de CRM.
  • A Brevo também é compatível com OAuth 2.0 para aplicações que agem em nome de outras contas Brevo, descrito junto ao fluxo de chaves em authentication schemes.
  • O servidor MCP usado por assistentes de AI usa um token separado e esse sim usa um cabeçalho bearer. Esse token é gerado na mesma tela de chaves de API, mas não é intercambiável com uma chave REST.

URL base, versionamento e sua primeira escrita

A URL base é https://api.brevo.com/v3/. A versão fica no caminho, não em um cabeçalho, e a v3 é a geração atual. Todo caminho neste guia é relativo a essa base.

Uma primeira escrita é mais informativa que uma primeira leitura, porque exercita as partes da conta que costumam estar mal configuradas (remetentes verificados, em especial):

Terminal window
curl -X POST https://api.brevo.com/v3/smtp/email \
-H "api-key: $BREVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender": { "name": "Ops", "email": "[email protected]" },
"to": [{ "email": "[email protected]", "name": "Dev" }],
"subject": "First transactional send",
"htmlContent": "<html><body><p>It works.</p></body></html>",
"tags": ["smoke-test"]
}'

Um envio bem-sucedido devolve 201 com um messageId. Um envio agendado devolve 202.

Os endpoints que você vai usar de verdade

Contatos

POST /v3/contacts cria um contato. O corpo aceita email, um mapa attributes para campos personalizados, listIds, ext_id para sua própria chave externa, e as duas flags que mais importam na prática: updateEnabled, que transforma a chamada em um upsert, e getId, que faz a resposta devolver o id do contato.

As leituras passam por GET /v3/contacts, que pagina com limit (padrão 50, máximo 1000) e offset, e é compatível com modifiedSince e createdSince em UTC. Sincronizações incrementais devem se apoiar em modifiedSince em vez de percorrer a lista inteira. Note que o parâmetro filter só permite um operador de igualdade, então qualquer coisa mais expressiva pertence a um segmento.

Para carga em massa, POST /v3/contacts/import aceita fileUrl, fileBody ou jsonBody, aponta para listIds e roda de forma assíncrona, devolvendo um processId. A Brevo documenta um máximo de 10 MB por corpo e recomenda ficar perto de 8 MB, porque o parsing infla o payload. Informe notifyUrl para você saber o resultado em vez de ficar consultando.

E-mail transacional

POST /v3/smtp/email é o cavalo de batalha. Além de sender, to, subject e htmlContent, os campos que vale conhecer são:

  • templateId com params, que substitui o conteúdo inline por um template da Brevo e suas substituições de variáveis. Os params de cada versão têm teto de 100 KB, e o acumulado, de 1000 KB.
  • messageVersions, que envia variantes personalizadas em uma única chamada, com até 99 destinatários por versão.
  • tags, que você deveria sempre definir. As tags voltam nos eventos de webhook e são a única forma barata de correlacionar um evento de entrega com o trecho de código que o produziu.
  • scheduledAt mais batchId, para envios futuros que você talvez queira cancelar em grupo.
  • headers, em Title-Case, para cabeçalhos SMTP personalizados.

Uma única requisição aceita no máximo 2.000 destinatários. Para a diferença entre esse endpoint e o envio de campanhas, o guia de e-mail transacional traz a visão de estratégia de mensagens.

Campanhas de e-mail

POST /v3/emailCampaigns exige name e sender, além de exatamente uma fonte de conteúdo: htmlContent (mínimo de 10 caracteres, abaixo de 1 MB), htmlUrl ou templateId. O público vai em recipients como listIds ou segmentIds, e scheduledAt usa o formato UTC YYYY-MM-DDTHH:mm:ss.SSSZ. Rotas complementares cobrem envio imediato, envio de teste, atualização de status e obtenção do relatório da campanha.

Empresas, negócios e objetos

O CRM da Brevo tem dois caminhos de escrita que se sobrepõem, e escolher certo faz diferença.

As rotas de CRM são POST /v3/companies, PATCH /v3/companies/{id}, DELETE /v3/companies/{id} e o conjunto equivalente para negócios. Elas são síncronas. Um PATCH devolve 204 assim que a mudança é aplicada.

A API de objetos é o caminho em massa: POST /v3/objects/{object_type}/batch/upsert aceita até 1000 registros e 1 MB por requisição, até 500 atributos por registro e até 10 registros de associação por tipo de objeto por registro. Ela devolve 202 com um processId, o que significa aceito, não 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" }
}
]
}'

O guia do Brevo CRM cobre o modelo de objetos do lado do operador.

SDKs oficiais

A Brevo mantém clientes na organização getbrevo do GitHub:

LinguagemRepositório
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

O cliente Node é instalado como @getbrevo/brevo:

Terminal window
npm install @getbrevo/brevo
import { BrevoClient } from "@getbrevo/brevo";
const brevo = new BrevoClient({ apiKey: process.env.BREVO_API_KEY });
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);

O cliente Python é instalado com pip install brevo-python. Se você preferir não carregar uma dependência de SDK por causa de dois endpoints, a superfície HTTP crua é pequena o bastante para ser chamada direto, o que também mantém você isolado da rotatividade de versões do SDK:

import os
import requests
BASE = "https://api.brevo.com/v3"
HEADERS = {
"api-key": os.environ["BREVO_API_KEY"],
"Content-Type": "application/json",
}
def upsert_contact(email, attributes, list_ids):
response = requests.post(
f"{BASE}/contacts",
headers=HEADERS,
json={
"email": email,
"attributes": attributes,
"listIds": list_ids,
"updateEnabled": True,
},
timeout=30,
)
response.raise_for_status()
return response

Também existe um servidor MCP em https://mcp.brevo.com/v1/brevo/mcp para assistentes de AI, autenticado com um token bearer gerado na mesma tela de configurações. Ele é útil para exploração e perguntas sobre a conta, não para caminhos de dados em produção.

Webhooks

Os webhooks são como você fica sabendo o que aconteceu depois de um envio. POST /v3/webhooks cria um, com url, events, type e, opcionalmente, channel (email ou sms), batched, headers personalizados e um objeto auth.

Existem três tipos de webhook, com vocabulários de evento distintos:

  • Transacional: sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened, unsubscribed.
  • Marketing: spam, opened, click, hardBounce, softBounce, unsubscribed, listAddition, delivered, contactUpdated, contactDeleted.
  • Entrada: inboundEmailProcessed e reply, que exigem adicionalmente um 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"
}'

Três coisas para acertar. Primeira: uma conta pode ter no máximo 40 webhooks somando todos os tipos, então roteie por evento dentro do seu handler em vez de registrar um endpoint por evento. Segunda: use a flag batched quando esperar volume, já que uma requisição carregando muitos eventos é bem mais barata de processar que muitas requisições. Terceira: proteja o receptor. A Brevo publica suas faixas de IP de envio, e restringir seu endpoint a essas faixas é a abordagem documentada. Adicione seu próprio segredo compartilhado pelo campo headers como segunda camada.

Os handlers precisam ser idempotentes. Trate o id da mensagem mais o tipo de evento mais o timestamp como chave de deduplicação.

Limites de taxa e tratamento de erros

Os limites de taxa da Brevo são por endpoint e por nível de plano, e a distância entre endpoints é enorme.

EndpointPadrãoProfessional e 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 RPHmais alto no Enterprise
GET /v3/smtp/emails2 RPS, 7.200 RPH3 RPS, 10.800 RPH
Todo o resto100 RPH200 RPH

Essa última linha é a que dói. O envio é praticamente sem medição, enquanto a gestão de campanhas, as leituras de CRM e a maioria das chamadas administrativas dividem um orçamento de 100 requisições por hora nos planos padrão. Um backfill ingênuo que lê o registro de uma empresa antes de cada escrita esgota uma hora de cota em menos de dois minutos.

Toda resposta traz x-sib-ratelimit-limit, x-sib-ratelimit-remaining e x-sib-ratelimit-reset. Leia esses cabeçalhos no sucesso, não só na falha. Ultrapassar um limite devolve 429, e a resposta correta é esperar o intervalo indicado no cabeçalho de reset e depois aplicar backoff exponencial com jitter.

async function callBrevo(path, init, attempt = 0) {
const response = await fetch(`https://api.brevo.com/v3${path}`, {
...init,
headers: { "api-key": process.env.BREVO_API_KEY, "Content-Type": "application/json", ...init.headers },
});
if (response.status === 429 && attempt < 5) {
const reset = Number(response.headers.get("x-sib-ratelimit-reset") || 1);
const backoff = Math.pow(2, attempt) * 250 + Math.random() * 250;
await new Promise((r) => setTimeout(r, reset * 1000 + backoff));
return callBrevo(path, init, attempt + 1);
}
return response;
}

Repita em 429 e 5xx. Nunca repita 400 ou 409 às cegas, porque os dois normalmente significam que a requisição está errada, não adiantada, e um 409 em particular pede uma ação diferente, não uma repetição.

Testar sem enviar e-mail

Adicione o cabeçalho X-Sib-Sandbox com o valor drop a um envio transacional. A Brevo valida a requisição, devolve 201 com um messageId, não entrega nada e não grava nenhum log de e-mail.

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

Entenda o que isso prova e o que não prova. O modo sandbox valida apenas o formato da requisição. Ele não diz nada sobre autenticação do remetente, renderização de template ou entregabilidade. Mantenha uma conta Brevo separada para testes de integração de qualquer coisa que toque contatos ou dados de CRM, porque o modo sandbox cobre o envio e não o resto da API.

Limites que moldam o desenho da sua integração

Estas são as restrições que só aparecem quando uma integração roda contra uma conta real em volume. Várias contradizem o que a API diz sobre si mesma. Nenhuma delas é negociável, então a única resposta sensata é desenhar em torno delas.

Empresas exigem domínio, e só uma empresa por domínio

GET /v3/crm/attributes/companies informa todos os atributos como não obrigatórios, e a referência de criação de empresa lista apenas name como mandatório. Na prática, POST /v3/companies sem um atributo domain não vazio devolve 400 com uma mensagem sobre atributos padrão obrigatórios ausentes. Uma string vazia falha do mesmo jeito que omitir o campo.

Pior: a unicidade de domínio é imposta. Uma segunda empresa em um domínio já em uso devolve 409. Para comércio B2B isso é estrutural: subsidiárias que compartilham um mesmo domínio de e-mail de comprador não podem existir todas como empresas separadas na Brevo. Sincronizar um contato também é suficiente para fazer aparecer uma empresa no domínio de e-mail desse contato, então uma criação pode colidir com uma empresa que ninguém criou explicitamente. O handler correto adota a empresa existente no 409 em vez de falhar ou repetir.

Atributos não declarados são descartados em silêncio

Este é o comportamento mais perigoso da plataforma, e a Brevo documenta isso com todas as letras: se um atributo aparece em uma requisição mas não foi previamente definido no schema do objeto, nada acontece. Sem erro, sem criação de atributo, sem aviso.

Uma resposta 2xx, portanto, não é prova de que seus dados chegaram. Leia o schema antes de escrever, descarte qualquer coisa não declarada no seu próprio cliente e recuse rodar uma sincronização cujos atributos não existem, em vez de escrever meio registro por um mês até alguém perceber.

Filtros por atributo são aceitos e ignorados

GET /v3/companies?filters[attributes.domain]=... devolve 200 e ignora o filtro. Dois filtros completamente diferentes devolvem os mesmos registros. Não existe jeito funcional de localizar uma empresa por atributo por essa rota.

Somando ao fato de que a lista sem filtro dá timeout com 504 em contas grandes com qualquer tamanho de página, uma empresa existente pode ser genuinamente impossível de encontrar pelo caminho documentado. A saída é varrer GET /v3/objects/company/records com sort=desc, que é rápido, paginado e devolve atributos, limitado a um número sensato de páginas. Uma empresa que acabou de disparar um 409 quase sempre foi criada momentos antes, então a varredura do mais novo para o mais antigo a encontra rápido.

Um milhão de registros por tipo de objeto, e nada de exclusão em massa

POST /v3/objects/{type}/batch/upsert devolve 400 assim que um tipo de objeto atinge um milhão de registros. Isso bloqueia atualizações tanto quanto criações: endereçar um registro existente pelo próprio id numérico falha igual. O caminho inteiro de escrita de objetos fecha de uma vez.

Voltar a ficar abaixo do teto é lento, porque POST /v3/objects/{type}/batch/delete devolve 403 para os tipos de objeto padrão da Brevo, como company. A única rota é DELETE /v3/companies/{id}, um registro por chamada, a cerca de 156 ms. Limpar 124.000 registros assim levou horas com 20 workers em paralelo. Monitore a contagem de registros em uma rotina agendada em vez de descobrir o teto por meio de uma sincronização quebrada, e roteie atualizações de alto volume por PATCH /v3/companies/{id}, que não tem esse limite.

ext_id é o id da Brevo, não o seu

Em registros de objeto, identifiers.ext_id guarda o próprio id de empresa do CRM da Brevo, uma string no estilo Mongo. Não é uma chave externa livre. Usar ext_id com o identificador da sua plataforma como chave de upsert cria duplicatas em vez de casar registros. Seu id externo pertence a um atributo declarado próprio.

Upserts de objeto são assíncronos, escritas de CRM não

batch/upsert devolve 202 e um processId, e aplica depois. Um id inexistente falha de forma assíncrona e mesmo assim devolve 202 para quem chamou. PATCH /v3/companies/{id} devolve 204 e é aplicado de forma síncrona. Se sua sincronização reporta sucesso, só o caminho síncrono merece essa palavra sem uma leitura de conferência.

Um checklist curto de integração

  • Chaves de API separadas por serviço e por ambiente, rotacionadas em mudanças de equipe.
  • Todas as escritas passam por um cliente que lê os cabeçalhos de limite de taxa e faz backoff em 429.
  • O schema de atributos é verificado na inicialização, e a sincronização se recusa a rodar se seus atributos estiverem faltando.
  • 409 na criação de empresa significa adotar, não repetir.
  • Caminhos em massa usam a API de objetos para vazão e as rotas de CRM para qualquer coisa que precise ser confirmada.
  • Os webhooks são idempotentes, agrupados, restritos por IP e carregam um cabeçalho de segredo compartilhado.
  • Sincronizações incrementais de contatos usam modifiedSince, não varreduras da lista inteira.

Construir e manter essa camada é trabalho de engenharia de verdade: verificação de schema, backoff, lógica de adoção, reconciliação. A Tajo existe para absorver isso, mantendo dados de Shopify e de comércio sincronizados com contatos, empresas e eventos da Brevo sem ninguém escrever à mão a lógica de retentativa e deduplicação. Se em vez disso você vai montar tudo por conta própria, o guia de integração da Brevo percorre as escolhas de modelo de dados que vêm antes do código.

Principais conclusões

  • A API é uma única superfície REST em https://api.brevo.com/v3/, autenticada com um cabeçalho api-key em vez de um token bearer.
  • Os limites de taxa são muito desiguais: o envio é praticamente sem medição, enquanto a maioria dos outros endpoints divide 100 requisições por hora nos planos padrão.
  • Existem SDKs oficiais para sete linguagens, mas a superfície HTTP é simples o bastante para ser chamada direto quando você só precisa de alguns endpoints.
  • O modo sandbox valida apenas o formato da requisição, então mantenha uma conta separada para testar qualquer coisa além de envios.
  • Uma resposta 2xx não prova que a escrita foi aplicada. Atributos não declarados são descartados em silêncio, e os upserts de objeto são assíncronos.
  • Desenhe em torno dos limites fixos: uma empresa por domínio, um milhão de registros por tipo de objeto, nenhuma exclusão em massa para objetos padrão e filtros por atributo que silenciosamente não fazem nada.

Perguntas frequentes

Qual é a URL base da API da Brevo?
Todas as chamadas REST da Brevo vão para https://api.brevo.com/v3/. A versão faz parte do caminho, e a v3 é a geração atual, então um endpoint como o de envio transacional tem o caminho completo https://api.brevo.com/v3/smtp/email.
Como você se autentica na API da Brevo?
Envie sua chave em um cabeçalho HTTP chamado api-key. A Brevo não usa um cabeçalho Authorization nem Bearer para chaves de API padrão. As chaves são geradas nas configurações da conta, em SMTP e API, chaves de API, e o valor aparece uma única vez.
Quais são os limites de taxa da API da Brevo?
Os limites são por endpoint e por nível de plano. Em contas padrão, o endpoint de envio transacional permite 1.000 requisições por segundo, os endpoints de contatos permitem 10 por segundo, e todos os outros endpoints ficam limitados a 100 requisições por hora. Os planos Professional e Enterprise recebem níveis mais altos. Ultrapassar um limite devolve 429.
A Brevo tem SDKs oficiais?
Sim. A Brevo publica clientes para Node.js, Python, PHP, Java, C#, Go e Ruby na organização getbrevo do GitHub. O pacote Node é @getbrevo/brevo no npm e o pacote Python é brevo-python no PyPI.
Como testo a API da Brevo sem enviar e-mail de verdade?
Adicione o cabeçalho X-Sib-Sandbox com o valor drop a um envio transacional. A Brevo valida a requisição, devolve 201 com um messageId, não envia nada e não grava nenhum log de e-mail. Ela só verifica o formato da requisição, não a entregabilidade.
Quais eventos de webhook a Brevo oferece?
Os webhooks transacionais cobrem sent, request, delivered, hardBounce, softBounce, blocked, spam, invalid, deferred, click, opened, uniqueOpened e unsubscribed. Os webhooks de marketing acrescentam listAddition, contactUpdated e contactDeleted. Os webhooks de entrada cobrem inboundEmailProcessed e reply. Uma conta pode ter no máximo 40 webhooks no total.
Posso criar duas empresas com o mesmo domínio na Brevo?
Não. A Brevo impõe unicidade de domínio nos registros de empresa e devolve 409 com um erro de unicidade de domínio se você tentar. O comportamento correto da integração é adotar a empresa existente em vez de repetir a criação.
Por que minha escrita na API da Brevo retornou sucesso mas não mudou nada?
As escritas em objetos e no CRM descartam silenciosamente atributos que não estão declarados no schema do objeto. A Brevo documenta esse comportamento de forma explícita: nenhum erro é gerado e nenhum atributo é criado. Leia o schema antes e verifique a escrita em vez de confiar em um status 2xx.
Existe exclusão em massa na API da Brevo?
Não para os tipos de objeto padrão da Brevo, como company. A rota de exclusão em lote devolve 403 para eles, então a limpeza roda um registro por chamada com DELETE /v3/companies/{id}. Planeje limpezas em massa em horas, não em minutos.

Solicite acesso antecipado

Informe seu nome e um e-mail ou número de telefone. Entraremos em contato com os detalhes de acesso à Tajo.

detecção automática
Obter Brevo