Envío por lotes
Envíe correos electrónicos a múltiples destinatarios en una sola llamada API
El envío por lotes solo está disponible con credenciales de flujo Marketing. Las credenciales transaccionales devuelven 400 BATCH_NOT_ALLOWED_FOR_TRANSACTIONAL. Esto previene el uso indebido de flujos transaccionales para correo masivo.
Enviar un lote
POST https://api.mail.land/v1/transactional/send-batchcurl -X POST https://api.mail.land/v1/transactional/send-batch \
-H "X-API-Key: ml_pem_Ab3Kx9mN2pQ7rT4wL8yD..." \
-H "Content-Type: application/json" \
-d '{
"from": {
"email": "[email protected]",
"name": "Your Company"
},
"recipients": [
{
"email": "[email protected]",
"name": "Alice",
"variables": { "firstName": "Alice", "plan": "Pro" }
},
{
"email": "[email protected]",
"name": "Bob",
"variables": { "firstName": "Bob", "plan": "Starter" }
}
],
"subject": "Hi {{firstName}}, check out what'\''s new",
"htmlContent": "<h1>Hey {{firstName}}!</h1><p>Your {{plan}} plan just got an upgrade.</p>",
"variables": {
"companyName": "Acme Inc",
"year": "2026"
},
"tags": ["product-update", "february-2026"],
"batchName": "February Product Update"
}'Campos de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
from | object | Sí | Dirección del remitente (email y name opcional) |
recipients | array | Sí | Lista de destinatarios (nota: recipients, no to) |
recipients[].email | string | Sí | Correo del destinatario |
recipients[].name | string | No | Nombre para mostrar del destinatario |
recipients[].variables | object | No | Variables de plantilla por destinatario |
subject | string | Condicional | Requerido a menos que se use templateId |
templateId | string | No | Usar una plantilla almacenada |
htmlContent | string | Condicional | Cuerpo HTML (requerido si no hay templateId) |
textContent | string | No | Texto plano alternativo |
variables | object | No | Variables globales. Se aplican tanto si se define templateId como si no; ante una clave superpuesta, gana el valor propio del destinatario en recipients[].variables |
replyTo | string | No | Dirección de respuesta |
batchName | string | No | Nombre legible para este lote |
tags | array | No | Etiquetas para analíticas y filtrado de webhooks |
customHeaders | object | No | Encabezados de correo personalizados |
tracking | object | No | Sobrescritura de seguimiento de aperturas/clics |
ipPoolId | string | No | Reservado para pools de IP dedicadas. Aún no disponible: enviarlo devuelve 400 IP_POOL_NOT_FOUND. Omítalo. |
scheduledAt | datetime | No | Programar para entrega futura, hasta 7 días de antelación |
externalBatchId | string | No | Identificador de lote de su sistema: si se envía, se devuelve tal cual como batchId en la respuesta |
priority | enum | No | Low, Normal (por defecto), High, Critical |
idempotencyKey | string | No | Evita envíos duplicados con la misma clave. Se prefiere el encabezado X-Idempotency-Key |
Note la diferencia en el nombre del campo: el envío por lotes usa recipients mientras que el envío individual usa to.
Respuesta
Estado: 202 Accepted
{
"success": true,
"batchId": "batch_f47ac10b58cc43729876fedcba098765",
"messageIds": ["m_a1b2c3...", "m_d4e5f6..."],
"totalRecipients": 2,
"acceptedCount": 2,
"rejectedCount": 0,
"rejectedRecipients": [],
"queuedAt": "2026-02-17T10:30:00Z",
"estimatedCompletion": "2026-02-17T10:35:00Z",
"isSandboxed": false
}| Campo | Tipo | Descripción |
|---|---|---|
batchId | string | Identificador único del lote |
messageIds | array | IDs de mensaje por destinatario (con prefijo m_ para marketing) |
totalRecipients | int | Total de destinatarios en la solicitud |
acceptedCount | int | Destinatarios aceptados para entrega |
rejectedCount | int | Destinatarios rechazados |
rejectedRecipients | array | Detalles de los destinatarios rechazados con motivos |
estimatedCompletion | datetime | Tiempo estimado para completar el procesamiento del lote |
Combinación de variables
Las variables por destinatario sobrescriben las variables globales cuando las claves coinciden:
{
"variables": {
"companyName": "Acme Inc",
"greeting": "Hello"
},
"recipients": [
{
"email": "[email protected]",
"variables": { "greeting": "Dear valued customer" }
},
{
"email": "[email protected]"
}
]
}[email protected]recibe:greeting = "Dear valued customer",companyName = "Acme Inc"[email protected]recibe:greeting = "Hello",companyName = "Acme Inc"
Manejo de errores
Errores específicos del envío por lotes:
| Estado | Código | Descripción |
|---|---|---|
400 | BATCH_NOT_ALLOWED_FOR_TRANSACTIONAL | La credencial transaccional no puede enviar lotes |
400 | VALIDATION_FAILED | Se excedió el máximo de destinatarios por lote (10.000) |
400 | INVALID_REQUEST | scheduledAt con más de 7 días de antelación ("Cannot schedule email more than 7 days in advance") |
400 | TEMPLATE_NOT_FOUND | templateId no nombra ninguna plantilla activa de esta cuenta (también se devuelve para una plantilla eliminada) |
400 | IP_POOL_NOT_FOUND | ipPoolId no nombra ningún pool asignado a esta cuenta |
422 | TEMPLATE_VARIABLE_MISSING | Todos los destinatarios carecen de valor para un {{placeholder}} (si solo faltan en algunos, se omiten y se listan en rejectedRecipients con el motivo template_variable_missing: name). No se encola nada |
400 | BATCH_SEND_FAILED | Todos los destinatarios están suprimidos |
429 | RATE_LIMIT_EXCEEDED | Límite de velocidad excedido (a nivel de workspace/plan, no por credencial) |
429 | QUOTA_EXCEEDED | Cuota mensual agotada |
Los códigos de verificación de cuenta y de política documentados para POST /v1/transactional/send (SENDING_BLOCKED, ACTION_REQUIRED, DAILY_LIMIT_EXCEEDED, MARKETING_PAUSED y el resto) también se aplican aquí.
Es posible un éxito parcial: algunos destinatarios pueden ser aceptados mientras otros son rechazados (por ejemplo, direcciones suprimidas). Verifique rejectedRecipients en la respuesta.
Próximos pasos
- REST API: referencia de envío de correo individual
- Plantillas: cree plantillas para correos por lotes
- Correos programados: programe lotes para más tarde