Envío por lotes
POST /v1/email/batches acepta hasta 100 payloads de envío completos en una sola solicitud. Cada elemento es un mensaje independiente con su propio remitente, destinatarios y contenido. Usa un lote para enviar recibos, alertas u otros mensajes por destinatario con menos solicitudes API. Para un solo mensaje, consulta Envío de correo electrónico.
Cuándo usar qué
- Mensajes independientes que ya tienes listos. Usa un lote y entrégalos en una sola solicitud.
- Un flujo constante de alto volumen. Llamar al endpoint de envío individual en un bucle es una arquitectura válida, y un lote no hace que ningún mensaje individual sea más barato ni más rápido de entregar. Lo que cambia es el throughput, porque las solicitudes por lotes usan el grupo de limitación de solicitudes email_batch en lugar del grupo email_send, y cada una admite hasta 100 mensajes.
- Un correo a una audiencia almacenada. Eso es un broadcast, que resuelve la audiencia en destinatarios y personaliza por contacto.
Envíos por lotes
El cuerpo de la solicitud es un objeto JSON cuyo array messages contiene de 1 a 100 objetos de mensaje. Cada elemento es una solicitud de envío completa e independiente con su propio from, to, subject, contenido, y opcionalmente su propio category, ip_pool_id, tags y metadata. El esquema del elemento es exactamente el payload de envío individual, así que todo lo descrito en envío de correo electrónico aplica por elemento, incluido el valor por defecto category de marketing y el envío por plantilla.
Eso incluye scheduled_at, así que un lote puede mezclar mensajes que salen ahora con mensajes que salen después, cada uno a su propia hora. Las reglas y el margen en envío programado aplican por elemento, y un elemento programado se cancela por su propio ID como cualquier otro mensaje programado.
const batch = await bird.email.sendBatch({
messages: [
{
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["alice@example.com"],
subject: "Your receipt",
html: "<p>Thanks, Alice.</p>",
},
{
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["bob@example.com"],
subject: "Your receipt",
html: "<p>Thanks, Bob.</p>",
},
],
});
for (const item of batch.data) console.log(item.id, item.status);batch = client.email.send_batch(
messages=[
{
"from_": {"email": "onboarding@messagebird.dev", "name": "Bird"},
"to": ["delivered@messagebird.dev"],
"subject": "Hello from Bird",
"html": "<p>My first Bird email.</p>",
},
{
"from_": {"email": "onboarding@messagebird.dev", "name": "Bird"},
"to": ["someone-else@messagebird.dev"],
"subject": "Hello again from Bird",
"text": "My second Bird email.",
},
],
)
for item in batch.data:
print(item.id, item.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
batch, err := client.Email.SendBatch(context.Background(), bird.EmailSendBatchParams{
Messages: []bird.EmailSendParams{
{
From: "onboarding@messagebird.dev",
To: []string{"alice@example.com"},
Subject: "Hello, Alice",
HTML: "<p>Welcome!</p>",
},
{
From: "onboarding@messagebird.dev",
To: []string{"bob@example.com"},
Subject: "Hello, Bob",
HTML: "<p>Welcome!</p>",
},
},
})
if err != nil {
log.Fatal(err)
}
for _, item := range batch.Data {
fmt.Println(item.Id)
}
}$batch = $bird->email->sendBatch(messages: [
(new EmailMessageSendRequest())
->setFrom((new EmailAddress())->setEmail('onboarding@messagebird.dev')->setName('Bird'))
->setTo([(new EmailAddress())->setEmail('delivered@messagebird.dev')])
->setSubject('Hello from Bird')
->setHtml('<p>My first Bird email.</p>'),
(new EmailMessageSendRequest())
->setFrom((new EmailAddress())->setEmail('onboarding@messagebird.dev')->setName('Bird'))
->setTo([(new EmailAddress())->setEmail('someone-else@messagebird.dev')])
->setSubject('Hello again from Bird')
->setText('My second Bird email.'),
]);
foreach ($batch->getData() ?? [] as $item) {
echo $item->getId(), ' ', $item->getStatus(), "\n";
}bird email send-batch --body-file - <<'JSON'
{
"messages": [
{
"from": {
"email": "noreply@acme.com",
"name": "Acme Support"
},
"to": [
{
"email": "delivered@messagebird.dev",
"name": "Jane Doe"
}
],
"subject": "Your receipt for order #1234",
"text": "Thanks for your purchase! Your receipt is attached."
},
{
"from": {
"email": "noreply@acme.com",
"name": "Acme Support"
},
"to": [
{
"email": "delivered@messagebird.dev",
"name": "John Roe"
}
],
"subject": "Your receipt for order #1235",
"text": "Thanks for your purchase! Your receipt is attached."
}
]
}
JSON{
"name": "email_send_batch",
"arguments": {
"messages": [
{
"from": {
"email": "noreply@acme.com",
"name": "Acme Support"
},
"subject": "Your receipt for order #1234",
"text": "Thanks for your purchase! Your receipt is attached.",
"to": [
{
"email": "delivered@messagebird.dev",
"name": "Jane Doe"
}
]
},
{
"from": {
"email": "noreply@acme.com",
"name": "Acme Support"
},
"subject": "Your receipt for order #1235",
"text": "Thanks for your purchase! Your receipt is attached.",
"to": [
{
"email": "delivered@messagebird.dev",
"name": "John Roe"
}
]
}
]
}
}curl -X POST https://us1.platform.bird.com/v1/email/batches \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: batch-2026-07-23-001" \
-d '{
"messages": [
{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Your receipt",
"html": "<p>Thanks for your order.</p>",
"category": "transactional"
},
{
"from": "newsletter@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "June product news",
"html": "<p>What shipped this month.</p>",
"category": "marketing"
},
{
"from": "alerts@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Usage threshold reached",
"text": "You have used 80% of your quota.",
"category": "transactional"
}
]
}'Validación todo o nada
Cada elemento se valida antes de que se encole ninguno. Si un mensaje falla, ya sea un error de validación a nivel de campo o un dominio de remitente no verificado, el lote entero se rechaza con un 422 y no se envía nada: corrige ese elemento y reenvía el lote.
La supresión no forma parte de esa comprobación. Un elemento cuyos destinatarios están todos suprimidos se acepta igualmente y obtiene su propio ID em_, y esos destinatarios aparecen como status: rejected una vez que el mensaje se procesa (consulta supresiones).
Maneja la respuesta 202
Un lote exitoso devuelve 202 Accepted con una entrada por mensaje, en orden de envío:
Ejemplo de código
{
"data": [
{ "id": "em_01ky7q1vmkerwa7fxyycfe5ks1", "status": "accepted", "category": "transactional" },
{ "id": "em_01ky7q1vmkesr9h1y52tkqng9f", "status": "accepted", "category": "marketing" },
{ "id": "em_01ky7q1vmkesyr1z1h4s48jjxm", "status": "accepted", "category": "transactional" }
]
}Cada hijo es un mensaje ordinario: rastréalo por su ID em_ a través de GET /v1/email/messages/{message_id}, sus endpoints de destinatario y evento, y webhooks, exactamente como si lo hubieras enviado por separado. El mismo modelo asíncrono aplica, así que 202 significa aceptado de forma duradera y los resultados por destinatario llegan después.
Reintentos idempotentes
Envía un encabezado Idempotency-Key con el lote, como hace la solicitud de ejemplo. Si la solicitud tuvo éxito pero nunca viste la respuesta, repetirla con la misma clave devuelve el resultado original, los mismos IDs de mensaje hijo y un encabezado Idempotency-Replay, en lugar de enviar todos los mensajes otra vez. Un duplicado accidental aquí te cuesta hasta 100 correos, así que trata la clave como obligatoria en producción. Consulta idempotencia.
Adjuntos y el límite del cuerpo
Cada elemento del lote puede tener su propio attachments, con el mismo contrato de campo y presupuesto de tamaño por mensaje que un envío individual (consulta adjuntos). Un límite adicional aplica al lote en su conjunto: el cuerpo serializado de la solicitud JSON tiene un tope de 20 MB, y un cuerpo mayor se rechaza con un 413. Los adjuntos codificados en Base64 cuentan contra ese límite, así que los lotes con muchos adjuntos lo alcanzan rápidamente. Divídelos en varios lotes, o envíalos uno a la vez.
Broadcasts
Un broadcast envía un correo electrónico a una audiencia almacenada. Resolvemos los miembros actuales de la audiencia, menos las supresiones, en la lista de destinatarios cuando comienza el envío, y las propiedades de contacto de cada destinatario rellenan las variables de la plantilla. Lanza uno desde el dashboard, desde /v1/email/broadcasts, o con los comandos bird email broadcasts. Broadcasts tiene la guía paso a paso.
Próximos pasos
- Envío de correo electrónico: el payload por elemento completo, incluidos campos, límites y tags vs metadata
- Broadcasts: un correo a una audiencia almacenada, personalizado por contacto
- Categorías: marketing y transactional, y qué hace cada una con la política de supresión
- Idempotencia: formato de clave, retención y semántica de repetición
- Referencia de API: los esquemas completos de solicitud y respuesta del lote
- Envía 100 emails en una sola llamada API: un video que muestra un lote saliendo y cómo cada mensaje reporta su estado
Recursos relacionados
Continúa con la documentación, guías y ejemplos sobre este tema. Los recursos están en inglés.
Explorar la funcionalidadEmail batch sendingSeguir la ruta de aprendizajeBuild your first integration
Obtener un resumen de implementación