Uso e métricas
O consumo do plano e as métricas de entrega, abertura e clique.
Consulte quanto do plano o time já usou e as métricas de entrega dos seus e-mails.
#Uso do plano
GET/usage
Cotas de envio do dia e do mês (dias e meses em UTC), domínios, inboxes, pessoas no time, campanhas do mês (broadcasts: enviadas, enviando ou agendadas), execuções de automação iniciadas no mês, créditos de IA, se o envio está pausado e o limite de requisições por segundo. limit: null significa sem limite.
Quais chaves podem chamar: qualquer chave, de acesso total ou sending_access, e o painel. Assim, um app que só tem uma chave de envio lê o limite de requisições (rate_limit), as cotas diária e mensal e a pausa de envio do plano. Para as chaves sending_access, email_verifications não traz a lista de pacotes do Adicional de validação (grants), só os totais, e reputation não traz o history; o resto da resposta é igual.
Campos: plan (o plano de envio e o de marketing), emails.daily e emails.monthly (used, limit e resets_at; em emails.daily, também plan_limit e limited_by, veja abaixo; sent repete used e received é sempre 0), emails.credits (a reserva do Adicional de envios: veja abaixo), contacts (used, limit, plan_limit e packs dos contatos do plano de marketing: veja abaixo), audience (veja abaixo), overage dentro de emails.monthly e de contacts (se o excedente está ligado para o produto, enabled; se está valendo, active; e o que passou do limite no período, extra, em pacotes de 1.000, packs; em contatos, peak é o maior número do período), domains, team_members, inboxes, broadcasts, automation_runs, ai_credits (com remaining e resets_at), email_verifications (os créditos de validação de e-mails: used, limit, remaining, monthly_remaining, grant_credits, grants, resets_at e free_preview_remaining), features, paused, sending, reputation (veja abaixo) e rate_limit. A cota mensal de e-mails e o limite de contatos seguem a faixa de volume contratada (veja Planos).
emails.daily.limit é o limite diário que vale: o menor entre o do plano e o teto da reputação da conta (null: sem limite). emails.daily.plan_limit é só o do plano, e emails.daily.limited_by diz qual dos dois limita: plan, reputation ou null quando não há limite. Um limite diário definido pela equipe da Postal para o time substitui o teto da reputação, e aí limited_by vem plan. Ao atingir o limite, os envios recebem 429 daily_quota_exceeded; quando é a reputação que limita, a mensagem é 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 é a reputação da conta: score (de 0 a max_score, 100), level (de 1 a max_level, 5), daily_limit (o teto do nível; null no nível 5, sem teto da reputação), next (o próximo nível, com level, score e daily_limit; null no nível 5), levels (a tabela dos níveis, cada um com level, score mínimo, daily_limit e max_bounce_rate, a taxa de bounces permanentes que pausa os envios) e rules (os pontos por dia limpo conforme o volume, gain, cada degrau com o nível máximo até onde ele leva a conta, max_level; as taxas de um dia limpo, clean, e de um dia grave, severe; e as penalidades warning_penalty, severe_penalty e pause_penalty). As taxas são frações. history traz até 30 mudanças, da mais recente para a mais antiga, cada uma com day, kind, delta (os pontos somados ou tirados), score (a pontuação depois dela) e created_at. kind é day (a avaliação de um dia, com outcome: clean, quiet com menos de 10 e-mails e sem mudança, warning ou severe; e com sent, bounce_rate, complaint_rate, content_rejected_rate e all_bounce_rate; e com capped_at_level quando o ganho parou no topo do nível que o volume do dia permite), pause (uma pausa dos envios, com reason_code; outcome é null) ou staff (um ajuste da equipe da Postal; a nota da equipe não aparece). As chaves sending_access recebem o bloco sem history. Veja Reputação da conta.
sending mostra se os envios do time estão pausados por reputação: paused, paused_at, reason (o motivo em texto, no idioma da requisição: Accept-Language: pt-BR ou en, sem o cabeçalho em inglês), reason_code (bounce_rate, complaint_rate, content_rejected, all_bounce_rate ou manual; null sem pausa e nas pausas antigas), reason_details (os números do motivo, com as taxas como frações e a janela em window_days ou, na janela das últimas 24 horas, window_hours; null sem pausa, em manual e nas pausas antigas: veja a pausa de envios) e review_requested_at (quando a revisão foi pedida, ou null). paused, no nível de cima, lista os recursos que o plano atual pausou por estarem acima do limite, em [{"key", "label", "count"}] (vazia quando nada está pausado; o label segue o Accept-Language): veja Limites e recursos do plano.
audience é a Audiência de contatos em relação ao limite do plano de marketing, para avisar antes de a captura dos e-mails transacionais parar: used (contatos), limit (null: sem limite), percent (inteiro, de 0 a mais de 100; null sem limite), capture_enabled (o time guarda os destinatários dos e-mails transacionais na Audiência) e capture_paused (a captura está ligada, mas o limite foi atingido, então os novos destinatários não estão sendo guardados). O dono e os admins também recebem e-mails ao chegar a 80%, 90% e 100% do limite.
contacts.limit é o limite que vale: os contatos do plano mais os dos pacotes do Adicional de contatos, quando o time os tem; plan_limit é só o do plano. contacts.packs traz contacts (o que os pacotes somam; 0 sem pacotes), effective (igual a limit), scheduled (null, ou {"effective_at", "pack_contacts", "effective_limit"} quando uma redução espera a renovação: effective_limit é o limite depois dela) e, nas chaves de acesso total, items (os pacotes do time, com pack_id, name, contacts, quantity, scheduled_quantity, contacts_total, max_quantity, amount, currency e interval). As chaves sending_access veem os totais, mas não items. O bloco audience e os avisos de uso usam o mesmo limite.
marketing_recipients conta os contatos diferentes que receberam campanhas ou e-mails de automação no ciclo, que não podem passar do mesmo limite de contatos: used, limit (null: sem limite), remaining, percent (inteiro; null sem limite), billed_above_limit (true com o excedente de contatos valendo: os que passam do limite são cobrados, não recusados), cycle_start e resets_at (quando o ciclo renova e o contador volta a zero). Quem já recebeu campanha no ciclo continua contando até resets_at, mesmo se for apagado. Em contacts.overage, recipients repete used: o excedente de contatos é medido pelo maior entre peak e recipients.
emails.credits é a reserva do Adicional de envios, à parte da cota: emails.monthly.used e limit continuam sendo os do plano, e os créditos nunca são somados ao limite. Ela só é usada depois que a cota mensal acaba. Traz reserve (sempre true), balance (os créditos que dá para usar agora), used_this_month (os que os envios tiraram da reserva no mês), in_use (true quando a cota mensal acabou e há saldo, então os envios saem da reserva), next_expires_at (a data em que vence o primeiro pacote com validade, ou null) e, nas chaves de acesso total, grants: os pacotes e bônus com saldo, na ordem em que são usados (os que vencem antes primeiro, os sem validade por último), cada um com id, source (purchase ou staff), name, emails, remaining e expires_at (null: sem validade). As chaves sending_access veem os totais, mas não grants. Em email_verifications.grants, expires_at também é null num pacote sem validade.
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 e-mail
GET/emails/metrics
Contagens e taxas calculadas a partir dos eventos no período. Sem dimensions, só vêm os totais; com period, vem uma linha por período (inclusive os vazios). Com domain ou broadcast (campanha), cada linha traz também o nome (domain_name, broadcast_name). Os resultados ficam em cache por até 15 minutos.
Contagens: sent, delivered, delivery_delayed, bounced, bounced_permanent, bounced_transient, bounced_undetermined, complained, failed, suppressed, opened, unique_opened, clicked, unique_clicked, received, unsubscribed. Taxas (de 0 a 1): delivery_rate (entregues / enviados), open_rate e click_rate (únicos / entregues), bounce_rate (bounces / enviados), complaint_rate e unsubscribe_rate (sobre entregues).
Parâmetros de consulta
start_datestringInício, em ISO 8601. Uma data sem hora vale 00:00 notimezone. Padrão: 6 dias antes do fim, à meia-noite.end_datestringFim, em ISO 8601. Uma data sem hora vale 00:00 notimezone: para incluir o último dia inteiro, mande2026-10-22T23:59:59. Padrão (e máximo): agora. Se vier antes destart_date, a resposta é422.timezonestringFuso IANA para datas sem fuso e para os períodos, comoAmerica/Sao_Paulo. Padrão:UTC.granularitystringhourly,daily(padrão),weeklyoumonthly. No máximo 10.000 períodos.metricsstringMétricas separadas por vírgula (ou o parâmetro repetido). Padrão: todas.dimensionsstringAgrupamentos:period,domain,emaile/oubroadcast.domain_idstringAté 100 IDs de domínio, separados por vírgula.email_idstringAté 100 IDs de e-mail, separados por vírgula.broadcast_idstringAté 100 IDs de campanha, separados por vírgula.
Erros
- 422
validation_errorData, fuso, granularidade, métrica ou dimensão inválida, ou período longo demais para a granularidade.
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
}
]
}