Uso y métricas
El consumo del plan y las métricas de entrega, apertura y clic.
Consulta cuánto del plan ha usado ya el equipo y las métricas de entrega de tus correos electrónicos.
#Uso del plan
GET/usage
Cuotas de envío del día y del mes (días y meses en UTC), dominios, buzones de entrada, personas en el equipo, campañas del mes (broadcasts: enviadas, enviándose o programadas), ejecuciones de automatización iniciadas en el mes, créditos de IA, si el envío está pausado y el límite de solicitudes por segundo. limit: null significa sin límite.
Qué claves pueden llamar: cualquier clave, de acceso total o sending_access, y el panel. Así, una app que solo tiene una clave de envío lee el límite de solicitudes (rate_limit), las cuotas diaria y mensual y la pausa de envío del plan. Para las claves sending_access, email_verifications no trae la lista de paquetes del Adicional de validación (grants), solo los totales, y reputation no trae el history; el resto de la respuesta es igual.
Campos: plan (el plan de envío y el de marketing), emails.daily y emails.monthly (used, limit y resets_at; en emails.daily, también plan_limit y limited_by, mira abajo; sent repite used y received es siempre 0), emails.credits (la reserva del Adicional de envíos: mira abajo), contacts (used, limit, plan_limit y packs de los contactos del plan de marketing: mira abajo), audience (mira abajo), overage dentro de emails.monthly y de contacts (si el excedente está activado para el producto, enabled; si está vigente, active; y lo que pasó del límite en el período, extra, en paquetes de 1.000, packs; en contactos, peak es el mayor número del período), domains, team_members, inboxes, broadcasts, automation_runs, ai_credits (con remaining y resets_at), email_verifications (los créditos de verificación de correos: used, limit, remaining, monthly_remaining, grant_credits, grants, resets_at y free_preview_remaining), features, paused, sending, reputation (mira abajo) y rate_limit. La cuota mensual de correos y el límite de contactos siguen el tramo de volumen contratado (consulta Planes).
emails.daily.limit es el límite diario vigente: el menor entre el del plan y el tope de la reputación de la cuenta (null: sin límite). emails.daily.plan_limit es solo el del plan, y emails.daily.limited_by indica cuál de los dos limita: plan, reputation o null cuando no hay límite. Un límite diario definido por el equipo de Postal para el equipo sustituye al tope de la reputación, y entonces limited_by es plan. Al alcanzar el límite, los envíos reciben 429 daily_quota_exceeded; cuando es la reputación la que limita, el mensaje es Your account is at reputation level 2 of 5, which allows 2000 emails a day while it builds its sending history. The limit grows with each day of sending without bounces or complaints. It resets at 00:00 UTC.
reputation es la reputación de la cuenta: score (de 0 a max_score, 100), level (de 1 a max_level, 5), daily_limit (el tope del nivel; null en el nivel 5, sin tope de la reputación), next (el siguiente nivel, con level, score y daily_limit; null en el nivel 5), levels (la tabla de niveles, cada uno con level, score mínimo, daily_limit y max_bounce_rate, la tasa de rebotes permanentes que pausa los envíos) y rules (los puntos por día limpio según el volumen, gain, cada escalón con el nivel máximo al que lleva la cuenta, max_level; las tasas de un día limpio, clean, y de un día grave, severe; y las penalizaciones warning_penalty, severe_penalty y pause_penalty). Las tasas son fracciones. history trae hasta 30 cambios, del más reciente al más antiguo, cada uno con day, kind, delta (los puntos sumados o restados), score (la puntuación después) y created_at. kind es day (la evaluación de un día, con outcome: clean, quiet con menos de 10 correos y sin cambios, warning o severe; y con sent, bounce_rate, complaint_rate, content_rejected_rate y all_bounce_rate; y con capped_at_level cuando la ganancia se detuvo en el tope del nivel que permite el volumen del día), pause (una pausa de los envíos, con reason_code; outcome es null) o staff (un ajuste del equipo de Postal; la nota del equipo no aparece). Las claves sending_access reciben el bloque sin history. Mira Reputación de la cuenta.
sending muestra si los envíos del equipo están pausados por reputación: paused, paused_at, reason (el motivo en texto, en el idioma de la solicitud: Accept-Language: pt-BR o en; sin el encabezado, en inglés), reason_code (bounce_rate, complaint_rate, content_rejected, all_bounce_rate o manual; null sin pausa y en las pausas antiguas), reason_details (los números del motivo, con las tasas como fracciones y la ventana en window_days o, en la ventana de las últimas 24 horas, window_hours; null sin pausa, en manual y en las pausas antiguas: mira la pausa de envíos) y review_requested_at (cuándo se pidió la revisión, o null). paused, en el nivel superior, lista los recursos que el plan actual pausó por estar por encima del límite, en [{"key", "label", "count"}] (vacía cuando nada está pausado; el label sigue el Accept-Language): mira Límites y recursos del plan.
audience es la Audiencia de contactos frente al límite del plan de marketing, para avisarte antes de que la captura de los correos transaccionales se detenga: used (contactos), limit (null: sin límite), percent (entero, de 0 a más de 100; null sin límite), capture_enabled (el equipo guarda los destinatarios de los correos transaccionales en la Audiencia) y capture_paused (la captura está activada, pero se alcanzó el límite, así que los nuevos destinatarios no se están guardando). El propietario y los administradores también reciben correos al llegar al 80%, 90% y 100% del límite.
contacts.limit es el límite vigente: los contactos del plan más los de los paquetes del Adicional de contactos, cuando el equipo los tiene; plan_limit es solo el del plan. contacts.packs trae contacts (lo que suman los paquetes; 0 sin paquetes), effective (igual a limit), scheduled (null, o {"effective_at", "pack_contacts", "effective_limit"} cuando una reducción espera la renovación: effective_limit es el límite después de ella) y, en las claves de acceso total, items (los paquetes del equipo, con pack_id, name, contacts, quantity, scheduled_quantity, contacts_total, max_quantity, amount, currency e interval). Las claves sending_access ven los totales, pero no items. El bloque audience y los avisos de uso usan el mismo límite.
marketing_recipients cuenta los contactos diferentes que recibieron campañas o correos de automatización en el ciclo, que no pueden pasar del mismo límite de contactos: used, limit (null: sin límite), remaining, percent (entero; null sin límite), billed_above_limit (true con el excedente de contactos vigente: los que pasan del límite se cobran, no se rechazan), cycle_start y resets_at (cuando el ciclo se renueva y el contador vuelve a cero). Quien ya recibió una campaña en el ciclo sigue contando hasta resets_at, aunque se elimine. En contacts.overage, recipients repite used: el excedente de contactos se mide por el mayor entre peak y recipients.
emails.credits es la reserva del Adicional de envíos, aparte de la cuota: emails.monthly.used y limit siguen siendo los del plan, y los créditos nunca se suman al límite. Solo se usa después de agotada la cuota mensual. Trae reserve (siempre true), balance (los créditos que se pueden usar ahora), used_this_month (los que los envíos sacaron de la reserva en el mes), in_use (true cuando la cuota mensual se agotó y hay saldo, así que los envíos salen de la reserva), next_expires_at (la fecha en que vence el primer paquete con vigencia, o null) y, en las claves de acceso total, grants: los paquetes y regalos con saldo, en el orden en que se usan (los que vencen antes primero, los que no vencen al final), cada uno con id, source (purchase o staff), name, emails, remaining y expires_at (null: sin vencimiento). Las claves sending_access ven los totales, pero no grants. En email_verifications.grants, expires_at también es null en un paquete sin vencimiento.
cURL
curl https://api.postal.so/usage \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/usage', {
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.postal.so/usage",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->get('usage', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);
$data = json_decode($response->getBody(), true);
{
"object": "usage",
"plan": {
"id": "pro",
"name": "Pro",
"marketing_id": "free",
"marketing_name": "Free"
},
"emails": {
"daily": {
"used": 1840,
"limit": 5000,
"plan_limit": null,
"limited_by": "reputation",
"sent": 1840,
"received": 0,
"resets_at": "2026-10-25T00:00:00Z"
},
"monthly": {
"used": 31207,
"limit": 50000,
"sent": 31207,
"received": 0,
"resets_at": "2026-11-01T00:00:00Z",
"overage": {
"enabled": false,
"active": false,
"unit": "emails",
"extra": 0,
"packs": 0
}
},
"credits": {
"reserve": true,
"balance": 20000,
"used_this_month": 0,
"in_use": false,
"next_expires_at": "2027-01-15T12:00:00Z",
"grants": [
{
"id": "019a3f20-6c7d-7e8f-9a01-2b3c4d5e6f70",
"source": "purchase",
"name": "Pacote A",
"emails": 20000,
"remaining": 20000,
"expires_at": "2027-01-15T12:00:00Z"
}
]
}
},
"contacts": {
"used": 3120,
"limit": 5000,
"plan_limit": 5000,
"packs": {
"contacts": 0,
"effective": 5000,
"scheduled": null,
"items": []
},
"overage": {
"enabled": false,
"active": false,
"unit": "contacts",
"extra": 0,
"packs": 0,
"peak": 3120,
"recipients": 2870
}
},
"marketing_recipients": {
"used": 2870,
"limit": 5000,
"remaining": 2130,
"percent": 57,
"billed_above_limit": false,
"cycle_start": "2026-10-25T14:00:00Z",
"resets_at": "2026-11-25T14:00:00Z"
},
"audience": {
"used": 3120,
"limit": 5000,
"percent": 62,
"capture_paused": false,
"capture_enabled": true
},
"domains": {
"used": 2,
"limit": 10
},
"team_members": {
"used": 4,
"limit": 10
},
"inboxes": {
"used": 1,
"limit": 10
},
"features": {
"custom_inbound_domains": true,
"postal_branding": false,
"ai_assistant": true,
"email_verification": true
},
"broadcasts": {
"used": 3,
"limit": null
},
"automation_runs": {
"used": 318,
"limit": 10000
},
"ai_credits": {
"used": 12,
"limit": 100,
"remaining": 88,
"resets_at": "2026-11-01T00:00:00Z"
},
"email_verifications": {
"used": 120,
"limit": 5000,
"remaining": 5880,
"monthly_remaining": 4880,
"grant_credits": 1000,
"grants": [
{
"id": "019a1d40-5b6c-7d7e-8f90-1a2b3c4d5e6f",
"source": "purchase",
"name": "Pacote de 1.000 créditos",
"credits": 1000,
"remaining": 1000,
"expires_at": "2027-10-20T00:00:00Z"
}
],
"resets_at": "2026-11-01T00:00:00Z",
"free_preview_remaining": 880
},
"paused": [],
"sending": {
"paused": false,
"paused_at": null,
"reason": null,
"reason_code": null,
"reason_details": null,
"review_requested_at": null
},
"reputation": {
"score": 23,
"max_score": 100,
"level": 3,
"max_level": 5,
"daily_limit": 5000,
"next": {
"level": 4,
"score": 30,
"daily_limit": 10000
},
"levels": [
{
"level": 1,
"score": 0,
"daily_limit": 500,
"max_bounce_rate": 0.03
},
{
"level": 2,
"score": 5,
"daily_limit": 2000,
"max_bounce_rate": 0.04
},
{
"level": 3,
"score": 15,
"daily_limit": 5000,
"max_bounce_rate": 0.05
},
{
"level": 4,
"score": 30,
"daily_limit": 10000,
"max_bounce_rate": 0.05
},
{
"level": 5,
"score": 60,
"daily_limit": null,
"max_bounce_rate": 0.05
}
],
"rules": {
"gain": [
{
"sent": 10,
"points": 3,
"max_level": 3
},
{
"sent": 100,
"points": 5,
"max_level": 4
},
{
"sent": 1000,
"points": 6,
"max_level": 5
}
],
"clean": {
"bounce_rate": 0.02,
"complaint_rate": 0.001,
"content_rejected_rate": 0.01,
"all_bounce_rate": 0.1
},
"severe": {
"bounce_rate": 0.05,
"complaint_rate": 0.003,
"content_rejected_rate": 0.05,
"all_bounce_rate": 0.2
},
"warning_penalty": 10,
"severe_penalty": 25,
"pause_penalty": 40
},
"history": [
{
"day": "2026-10-23",
"kind": "day",
"outcome": "clean",
"delta": 5,
"score": 23,
"sent": 340,
"bounce_rate": 0.0058,
"complaint_rate": 0,
"content_rejected_rate": 0,
"all_bounce_rate": 0.0117,
"created_at": "2026-10-24T01:00:12Z"
},
{
"day": "2026-10-22",
"kind": "day",
"outcome": "quiet",
"delta": 0,
"score": 18,
"sent": 6,
"bounce_rate": 0,
"complaint_rate": 0,
"content_rejected_rate": 0,
"all_bounce_rate": 0,
"created_at": "2026-10-23T01:00:09Z"
},
{
"day": "2026-10-21",
"kind": "staff",
"outcome": null,
"delta": 3,
"score": 18,
"created_at": "2026-10-21T14:32:40Z"
}
]
},
"rate_limit": {
"limit": 10,
"duration": "1s"
}
}
{
"sending": {
"paused": true,
"paused_at": "2026-10-24T15:20:02Z",
"reason": "Taxa de bounce de 6,40% nos últimos 7 dias (limite: 5,00%).",
"reason_code": "bounce_rate",
"reason_details": {
"bounce_rate": 0.064,
"limit": 0.05,
"window_days": 7
},
"review_requested_at": null
}
}
{
"sending": {
"paused": true,
"paused_at": "2026-10-24T15:20:02Z",
"reason": "Taxa de bounces de todos os tipos de 17,20% nas últimas 24 horas (limite: 15,00%).",
"reason_code": "all_bounce_rate",
"reason_details": {
"all_bounce_rate": 0.172,
"limit": 0.15,
"window_hours": 24
},
"review_requested_at": null
}
}
{
"emails": {
"monthly": {
"used": 50210,
"limit": 50000,
"sent": 50210,
"received": 0,
"resets_at": "2026-11-01T00:00:00Z",
"overage": {
"enabled": false,
"active": false,
"unit": "emails",
"extra": 0,
"packs": 0
}
},
"credits": {
"reserve": true,
"balance": 24790,
"used_this_month": 210,
"in_use": true,
"next_expires_at": "2027-01-15T12:00:00Z",
"grants": [
{
"id": "019a3f20-6c7d-7e8f-9a01-2b3c4d5e6f70",
"source": "purchase",
"name": "Pacote A",
"emails": 20000,
"remaining": 19790,
"expires_at": "2027-01-15T12:00:00Z"
},
{
"id": "019a3f21-7d8e-7f90-8b12-3c4d5e6f7081",
"source": "purchase",
"name": "Pacote B",
"emails": 5000,
"remaining": 5000,
"expires_at": null
}
]
}
}
}
{
"contacts": {
"used": 3120,
"limit": 25000,
"plan_limit": 5000,
"packs": {
"contacts": 20000,
"effective": 25000,
"scheduled": {
"effective_at": "2026-11-20T12:00:00+00:00",
"pack_contacts": 10000,
"effective_limit": 15000
},
"items": [
{
"pack_id": "019a2e10-4b5c-7d6e-8f70-1a2b3c4d5e6f",
"name": "Pacote A",
"contacts": 10000,
"quantity": 2,
"scheduled_quantity": 1,
"contacts_total": 20000,
"max_quantity": 10,
"amount": 4900,
"currency": "brl",
"interval": "month"
}
]
},
"overage": {
"enabled": false,
"active": false,
"unit": "contacts",
"extra": 0,
"packs": 0,
"peak": 3120
}
}
}
#Métricas de correo
GET/emails/metrics
Conteos y tasas calculados a partir de los eventos del período. Sin dimensions, solo vienen los totales; con period, viene una fila por período (incluidos los vacíos). Con domain o broadcast (campaña), cada fila incluye también el nombre (domain_name, broadcast_name). Los resultados se guardan en caché hasta 15 minutos.
Conteos: sent, delivered, delivery_delayed, bounced, bounced_permanent, bounced_transient, bounced_undetermined, complained, failed, suppressed, opened, unique_opened, clicked, unique_clicked, received, unsubscribed. Tasas (de 0 a 1): delivery_rate (entregados / enviados), open_rate y click_rate (únicos / entregados), bounce_rate (rebotes / enviados), complaint_rate y unsubscribe_rate (sobre entregados).
Parámetros de consulta
start_datestringInicio, en ISO 8601. Una fecha sin hora equivale a las 00:00 en eltimezone. Predeterminado: 6 días antes del fin, a medianoche.end_datestringFin, en ISO 8601. Una fecha sin hora equivale a las 00:00 en eltimezone: para incluir el último día completo, envía2026-10-22T23:59:59. Predeterminado (y máximo): ahora. Si es anterior astart_date, la respuesta es422.timezonestringZona horaria IANA para fechas sin zona y para los períodos, comoAmerica/Sao_Paulo. Predeterminado:UTC.granularitystringhourly,daily(predeterminado),weeklyomonthly. Máximo 10.000 períodos.metricsstringMétricas separadas por comas (o el parámetro repetido). Predeterminado: todas.dimensionsstringAgrupaciones:period,domain,emaily/obroadcast.domain_idstringHasta 100 IDs de dominio, separados por comas.email_idstringHasta 100 IDs de correo, separados por comas.broadcast_idstringHasta 100 IDs de campaña, separados por comas.
Errores
- 422
validation_errorFecha, zona horaria, granularidad, métrica o dimensión no válida, o período demasiado largo para la granularidad.
cURL
curl 'https://api.postal.so/emails/metrics?start_date=2026-10-20&end_date=2026-10-22T23:59:59&timezone=America/Sao_Paulo&metrics=sent,delivered,delivery_rate&dimensions=period' \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/emails/metrics?start_date=2026-10-20&end_date=2026-10-22T23:59:59&timezone=America/Sao_Paulo&metrics=sent,delivered,delivery_rate&dimensions=period', {
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
},
});
const data = await response.json();
Python
import os
import requests
response = requests.get(
"https://api.postal.so/emails/metrics",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
params={
"start_date": "2026-10-20",
"end_date": "2026-10-22T23:59:59",
"timezone": "America/Sao_Paulo",
"metrics": "sent,delivered,delivery_rate",
"dimensions": "period",
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->get('emails/metrics', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'query' => [
'start_date' => '2026-10-20',
'end_date' => '2026-10-22T23:59:59',
'timezone' => 'America/Sao_Paulo',
'metrics' => 'sent,delivered,delivery_rate',
'dimensions' => 'period',
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "metrics",
"start_date": "2026-10-20T03:00:00.000Z",
"end_date": "2026-10-23T02:59:59.000Z",
"metrics": ["sent", "delivered", "delivery_rate"],
"dimensions": ["period"],
"granularity": "daily",
"totals": {
"sent": 3120,
"delivered": 3087,
"delivery_rate": 0.9894
},
"data": [
{
"period": "2026-10-20T00:00:00-03:00",
"sent": 1004,
"delivered": 995,
"delivery_rate": 0.991
},
{
"period": "2026-10-21T00:00:00-03:00",
"sent": 1130,
"delivered": 1116,
"delivery_rate": 0.9876
},
{
"period": "2026-10-22T00:00:00-03:00",
"sent": 986,
"delivered": 976,
"delivery_rate": 0.9899
}
]
}