Referencia
Límites de velocidad
Cómo limita Emailit el envío por espacio de trabajo, las cabeceras ratelimit de cada envío, qué aspecto tiene un 429, los límites de otros endpoints y cómo reintentar con espera exponencial.
Emailit limita la velocidad y el volumen de envío de un espacio de trabajo, no el número de llamadas a la API que haces. Esta página explica los límites de envío, las cabeceras que los indican, los pocos endpoints con límites propios y cómo esperar antes de reintentar cuando recibes un 429.
Límites de envío
Cada espacio de trabajo tiene dos límites de envío. Los espacios de trabajo nuevos empiezan con estos valores por defecto en todos los planes:
| Límite | Por defecto | Ventana |
|---|---|---|
| Por segundo | 2 emails | Ventana deslizante de un segundo |
| Al día | 5000 emails | Día natural en UTC; se restablece a las 00:00 UTC |
Cómo se aplican los límites:
- Por espacio de trabajo. Todas las claves de API y el SMTP relay comparten los mismos contadores. Enviar por SMTP consume la misma cuota que la API.
- Se cuentan por destinatarios. Cada dirección única entre
to,ccybcces un email. Una petición con tres destinatarios cuenta como tres. - Solo cuentan los envíos. Los límites se aplican a Enviar un email y Reenviar un email. Leer datos y gestionar recursos no cuenta para ellos.
- Se comprueban antes y se cuentan después. Un envío se acepta siempre que el espacio de trabajo no haya alcanzado aún el límite, y sus destinatarios se suman a los contadores después. Una sola petición con muchos destinatarios puede hacer que superes el límite, y las peticiones siguientes reciben
429hasta que se libera la ventana.
Puedes ver los límites actuales y el uso de hoy en la tarjeta Sending Limits de la página de inicio de Dashboard.
Cabeceras de límite de velocidad
Las respuestas de los endpoints de envío y de reenvío incluyen estas cabeceras:
| Cabecera | Descripción |
|---|---|
ratelimit-limit |
Emails que el espacio de trabajo puede enviar por segundo. |
ratelimit-remaining |
Emails que quedan en la ventana actual de un segundo. |
ratelimit-reset |
Segundos que faltan para que se restablezca la ventana por segundo. |
ratelimit-daily-limit |
Emails que el espacio de trabajo puede enviar al día. |
ratelimit-daily-remaining |
Emails que quedan hoy. |
ratelimit-daily-reset |
Segundos que faltan para que el límite diario se restablezca a las 00:00 UTC. |
retry-after |
Segundos que hay que esperar antes de reintentar. Solo se envía con las respuestas 429. |
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
ratelimit-limit: 2
ratelimit-remaining: 1
ratelimit-reset: 1
ratelimit-daily-limit: 5000
ratelimit-daily-remaining: 4812
ratelimit-daily-reset: 52113Cuando alcanzas un límite
Un envío que supera el límite devuelve 429 Too Many Requests con una cabecera retry-after. El cuerpo indica qué límite has alcanzado:
{
"error": "Rate limit exceeded",
"message": "Too many requests. Maximum 2 messages per second allowed.",
"limit": 2,
"current": 2,
"retry_after": 1
}{
"error": "Daily limit exceeded",
"message": "Daily sending limit of 5000 messages has been reached.",
"limit": 5000,
"current": 5000,
"retry_after": 41760
}| Campo | Descripción |
|---|---|
error |
Rate limit exceeded para el límite por segundo, Daily limit exceeded para el límite diario. |
limit |
El límite que se ha alcanzado. |
current |
Cuántos emails se habían contado ya en la ventana. |
retry_after |
Segundos que hay que esperar. 1 para el límite por segundo, y los segundos que faltan hasta las 00:00 UTC para el límite diario. |
Cuando se rechaza una petición, no se envía nada y no se gastan créditos. Reintenta los rechazos por segundo tras una breve espera. En los rechazos diarios, pon el email en cola y envíalo después del restablecimiento, o solicita un límite más alto.
Otros límites
Algunos endpoints tienen límites propios:
| Endpoint | Límite | Se cuenta por |
|---|---|---|
POST /emails/{id}/forward |
3 por hora | Espacio de trabajo |
POST /webhooks/{id}/test |
5 por minuto | Dirección IP |
Enlaces alojados de suscripción y de baja (/subscribe/{token}, /unsubscribe/{token}) |
30 por minuto | Dirección IP |
Endpoints públicos de formularios: cargar un formulario (GET /forms/{token}) |
60 por minuto | Dirección IP |
Endpoints públicos de formularios: enviar un formulario (POST /forms/{token}/submit) |
30 por minuto | Dirección IP |
Los reenvíos también cuentan para los límites de envío anteriores. Si superas el límite de reenvíos, la API devuelve 429 con una cabecera retry-after:
{
"error": "too_many_requests",
"message": "Forwarding is limited to 3 emails per hour for this workspace. Try again later."
}Estos límites son fijos y no cambian con tu plan ni con tus límites de envío.
Todos los demás endpoints
Los endpoints que leen datos o gestionan recursos no tienen un límite fijo por endpoint. Haz un uso razonable: usa un pool pequeño de peticiones simultáneas en lugar de miles en paralelo, guarda en caché los datos que cambian poco y usa webhooks en lugar de consultar periódicamente el estado de los emails.
Aumentar tus límites
- Pro y Business. Los límites suben automáticamente a medida que envías, según tu salud de envío. Emailit los aumenta como máximo una vez cada siete días, y solo mientras tu salud de envío sea buena y ninguno de tus dominios esté pausado.
- Todos los planes. Solicita un aumento en la página de inicio de Dashboard: selecciona Request Increase en la tarjeta Sending Limits e indica los límites por segundo y diarios que necesitas, de dónde procede tu lista y por qué. El equipo de soporte suele revisar las solicitudes en 24 horas.
Para ver todos los límites de cada plan, consulta Límites.
Reintentar con espera exponencial
Reintenta las respuestas 429, 409 (clave de idempotencia en curso), 500 y 503. Espera los segundos que indique retry-after cuando la cabecera esté presente y, si no, usa una espera exponencial con variación aleatoria (jitter). Envía una Idempotency-Key para que un envío reintentado nunca se entregue dos veces, y no esperes a que pase un límite diario dentro de un bucle de peticiones.
# curl retries 429 and 5xx responses and honors retry-after.
curl https://api.emailit.com/v2/emails \
--retry 5 --retry-max-time 60 \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-confirmation" \
-d '{
"from": "Acme <orders@acme.com>",
"to": "ada@example.com",
"subject": "Your order #1042",
"text": "Thanks for your order."
}'import { randomUUID } from 'node:crypto';
const RETRYABLE = new Set([409, 429, 500, 503]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function sendEmail(payload, maxAttempts = 5) {
const idempotencyKey = randomUUID();
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const response = await fetch('https://api.emailit.com/v2/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.EMAILIT_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify(payload),
});
const body = await response.json();
if (response.ok) return body;
const retryAfter = Number(response.headers.get('retry-after')) || 0;
// Daily limit: retry_after is hours away, so give up and queue it.
if (!RETRYABLE.has(response.status) || retryAfter > 60 || attempt === maxAttempts) {
throw new Error(`Emailit ${response.status}: ${body.message ?? body.error}`);
}
const backoff = Math.min(1000 * 2 ** (attempt - 1), 30_000);
await sleep(retryAfter ? retryAfter * 1000 : backoff + Math.random() * 250);
}
}import os
import random
import time
import uuid
import requests
RETRYABLE = {409, 429, 500, 503}
def send_email(payload, max_attempts=5):
idempotency_key = str(uuid.uuid4())
for attempt in range(1, max_attempts + 1):
response = requests.post(
"https://api.emailit.com/v2/emails",
headers={
"Authorization": f"Bearer {os.environ['EMAILIT_API_KEY']}",
"Idempotency-Key": idempotency_key,
},
json=payload,
timeout=30,
)
if response.ok:
return response.json()
retry_after = int(response.headers.get("retry-after", 0))
# Daily limit: retry_after is hours away, so give up and queue it.
if response.status_code not in RETRYABLE or retry_after > 60 or attempt == max_attempts:
response.raise_for_status()
backoff = min(2 ** (attempt - 1), 30) + random.random() / 4
time.sleep(retry_after or backoff)Consejos para grandes volúmenes
- Envía desde una cola con un número fijo de workers y ajusta el tamaño del pool a tu límite por segundo.
- Reparte los trabajos por lotes grandes a lo largo del día en lugar de lanzarlos todos a la vez, para que un solo trabajo no consuma toda la cuota diaria.
- Vigila
ratelimit-daily-remainingy reduce el ritmo antes de que llegue a cero. - Para enviar newsletters a tus listas de contactos, usa campañas en lugar de llamar al endpoint de envío en un bucle.