Request logs API
The record of every request made with an API key and of every message sent over SMTP.
Every request made with an API key, and every message received over SMTP, is recorded with the body that was sent and the response. Attachment contents and texts longer than 5,000 characters are truncated. Requests made from the dashboard are not included in the logs. Logs are kept for 30 days. An SMTP send shows up with method SMTP and endpoint /smtp: the body carries the sender, the envelope recipients, the subject, the size and the number of attachments, and the response carries the email id (or the reason for the refusal).
#List logs
GET/logs
Query parameters
limitintegerFrom 1 to 100. Default: 20.afterstringCursor for the next page.beforestringCursor for the previous page.statusstringHTTP statuses or ranges separated by commas, such as4xx,500.methodstringMethods separated by commas, such asPOST,PATCH. SMTP sends use theSMTPmethod.sourcestringapiorsmtp.user_agentstringPart of the User-Agent.api_key_idstringOnly requests made with one key.searchstringSearches the path, such as/emails/batch.start_datestringFrom this date (ISO 8601, inclusive). With no time zone, UTC applies.end_datestringUp to this date (ISO 8601, inclusive). With only the date, it covers the whole day. If it comes beforestart_date, the response is422. See the date filters.
Errors
- 422
validation_errorA field is invalid. The message says which one.
cURL
curl 'https://api.postal.so/logs?status=4xx&source=api' \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/logs?status=4xx&source=api', {
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/logs",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
params={
"status": "4xx",
"source": "api",
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->get('logs', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'query' => [
'status' => '4xx',
'source' => 'api',
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "list",
"has_more": false,
"data": [
{
"id": "019a1b2d-0000-7b11-a2c3-d4e5f6a7b8c9",
"created_at": "2026-10-24T13:02:11.000000Z",
"endpoint": "/emails",
"method": "POST",
"response_status": 422,
"user_agent": "node-fetch/1.0",
"api_key_id": "0199f0e2-7b8c-7d9e-8f0a-1b2c3d4e5f6a"
}
]
}
#Get a log
GET/logs/{id}
Returns the log with request_body (the JSON that was sent, or the query string on GET requests) and response_body.
Path parameters
idstringrequiredLog ID.
Errors
- 404
not_foundThe resource does not exist or belongs to another team.
cURL
curl https://api.postal.so/logs/019a1b2c-4d60-7a11-b2c3-d4e5f6a7b8c9 \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/logs/019a1b2c-4d60-7a11-b2c3-d4e5f6a7b8c9', {
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/logs/019a1b2c-4d60-7a11-b2c3-d4e5f6a7b8c9",
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('logs/019a1b2c-4d60-7a11-b2c3-d4e5f6a7b8c9', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);
$data = json_decode($response->getBody(), true);
{
"object": "log",
"id": "019a1b2c-4d60-7a11-b2c3-d4e5f6a7b8c9",
"created_at": "2026-10-24T13:02:11.000000Z",
"endpoint": "/emails",
"method": "POST",
"response_status": 200,
"user_agent": "node-fetch/1.0",
"api_key_id": "0199f0e2-7b8c-7d9e-8f0a-1b2c3d4e5f6a",
"request_body": {
"from": "Acme Store <[email protected]>",
"to": ["[email protected]"],
"subject": "Your order #10482 is out for delivery",
"html": "<p>Order <b>#10482</b> is on its way.</p>"
},
"response_body": {
"id": "019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b"
}
}