Paginação e filtros
As listas usam paginação por cursor com limit, after e before, e aceitam filtros na query string.
#Como funciona
Os endpoints que listam recursos (GET /emails, GET /domains, GET /logs…) devolvem os itens do mais recente para o mais antigo, numa página por vez:
{
"object": "list",
"has_more": true,
"data": [
{
"object": "email",
"id": "019a1b2c-51ab-7d30-8e11-2a3b4c5d6e7f",
"…": "…"
},
{
"object": "email",
"id": "019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
"…": "…"
}
]
}
| Parâmetro | Descrição |
|---|---|
limit | Itens por página, de 1 a 100. Padrão: 20. |
after | ID de um item: devolve os itens mais antigos que ele. Use o ID do último item da página para ir à próxima. |
before | ID de um item: devolve os itens mais recentes que ele. Use o ID do primeiro item para voltar uma página. |
has_more diz se há mais itens na direção pedida. Não dá para usar after e before juntos (422). Como o cursor é o ID, itens criados enquanto você pagina não fazem a lista pular nem repetir itens.
#Percorrer todas as páginas
Para ler uma lista inteira, peça páginas de 100 e siga o after até has_more ser false:
cURL
# Primeira página
curl "https://api.postal.so/emails?limit=100" \
-H "Authorization: Bearer $POSTAL_API_KEY"
# Próxima página: o ID do último item recebido
curl "https://api.postal.so/emails?limit=100&after=019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b" \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
async function listAll(path) {
const items = [];
let after;
do {
const url = new URL(path, 'https://api.postal.so');
url.searchParams.set('limit', '100');
if (after) url.searchParams.set('after', after);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.POSTAL_API_KEY}` },
});
const page = await response.json();
items.push(...page.data);
after = page.has_more ? page.data.at(-1).id : undefined;
} while (after);
return items;
}
const bounces = await listAll('/emails?status=bounced');
Python
import os
import requests
def list_all(path, **filters):
items, after = [], None
while True:
params = {"limit": 100, **filters}
if after:
params["after"] = after
page = requests.get(
f"https://api.postal.so{path}",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
params=params,
timeout=10,
).json()
items.extend(page["data"])
if not page["has_more"]:
return items
after = page["data"][-1]["id"]
bounces = list_all("/emails", status="bounced")
PHP
function listAll(string $path, array $filters = []): array
{
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$items = [];
$after = null;
do {
$page = json_decode($client->get($path, [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'query' => [...$filters, 'limit' => 100, ...($after ? ['after' => $after] : [])],
])->getBody(), true);
array_push($items, ...$page['data']);
$after = $page['has_more'] ? end($page['data'])['id'] : null;
} while ($after !== null);
return $items;
}
$bounces = listAll('emails', ['status' => 'bounced']);
Para grandes volumes, prefira as exportações em CSV: elas usam os mesmos filtros e não gastam o seu limite de requisições.
#Filtros
Várias listas aceitam filtros na query string, combinados com a paginação. Filtros com vários valores aceitam uma lista separada por vírgula, como status=bounced,complained.
| Endpoint | Filtros |
|---|---|
GET /emails | search, status, domain_id, api_key_id, start_date, end_date |
GET /emails/receiving | inbox_id, unread, search |
GET /domains | search, status, region |
GET /api-keys | search, permission |
GET /templates | search, status |
GET /contacts | search, segment_id, topic_id, unsubscribed |
GET /segments, GET /forms | search |
GET /broadcasts | search, status |
GET /broadcasts/{id}/recipients | status, search |
GET /events | name, contact |
GET /automations | search, status |
GET /automations/{id}/runs | status, contact |
GET /logs | status, method, source, user_agent, api_key_id, search, start_date, end_date |