Iniciar sesión Crear cuenta
Referencia de la API

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

Acceso total o solo envío

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.

GET /usage

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);
200Respuesta
{
  "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"
  }
}
200Solo el campo "sending", con los envíos pausados
{
  "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
  }
}
200Solo el campo "sending", en una pausa por la ventana de las últimas 24 horas (niveles 1 a 3; texto de "reason" ilustrativo)
{
  "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
  }
}
200Solo el campo "emails", con la cuota mensual agotada y la reserva del Adicional de envíos en uso (valores ilustrativos)
{
  "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
        }
      ]
    }
  }
}
200Solo el campo "contacts", en un equipo con paquetes del Adicional de contactos y una reducción programada (valores ilustrativos)
{
  "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

Clave de acceso total

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_datestring
    Inicio, en ISO 8601. Una fecha sin hora equivale a las 00:00 en el timezone. Predeterminado: 6 días antes del fin, a medianoche.
  • end_datestring
    Fin, en ISO 8601. Una fecha sin hora equivale a las 00:00 en el timezone: para incluir el último día completo, envía 2026-10-22T23:59:59. Predeterminado (y máximo): ahora. Si es anterior a start_date, la respuesta es 422.
  • timezonestring
    Zona horaria IANA para fechas sin zona y para los períodos, como America/Sao_Paulo. Predeterminado: UTC.
  • granularitystring
    hourly, daily (predeterminado), weekly o monthly. Máximo 10.000 períodos.
  • metricsstring
    Métricas separadas por comas (o el parámetro repetido). Predeterminado: todas.
  • dimensionsstring
    Agrupaciones: period, domain, email y/o broadcast.
  • domain_idstring
    Hasta 100 IDs de dominio, separados por comas.
  • email_idstring
    Hasta 100 IDs de correo, separados por comas.
  • broadcast_idstring
    Hasta 100 IDs de campaña, separados por comas.

Errores

  • 422validation_error
    Fecha, zona horaria, granularidad, métrica o dimensión no válida, o período demasiado largo para la granularidad.
GET /emails/metrics

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);
200Respuesta
{
  "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
    }
  ]
}