Appearance
List logs
Every call made to this API with one of your keys — including the one that fetched this list.
http
GET /v1/logsRequires a Full access API key.
Query parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | How many to return. Default 20, min 1, max 100. |
after | string | Return logs after this id. Cannot be combined with before. |
before | string | Return logs before this id. Cannot be combined with after. |
method | string | Only calls with this method — GET, POST, PATCH, DELETE. Case-insensitive. |
response_status | number | Only calls we answered with this status, e.g. 422. |
endpoint | string | Only calls to paths starting with this. /v1/emails also matches /v1/emails/batch. |
Newest first. Cursor-paginated — there is no total, because a keyset paginator never counts the table, and this is the largest table you have.
Example
bash
# every call that failed validation
curl "https://api.sendgrail.com/v1/logs?response_status=422" \
-H "Authorization: Bearer $SENDGRAIL_API_KEY"js
const res = await fetch(
'https://api.sendgrail.com/v1/logs?response_status=422',
{ headers: { Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}` } },
);
const { data, has_more } = await res.json();Response
json
{
"object": "list",
"has_more": true,
"data": [
{
"object": "log",
"id": "a816c847-153d-4be8-a43e-f05391ff01e6",
"created_at": "2026-07-14T20:20:00+00:00",
"endpoint": "/v1/contacts",
"method": "POST",
"response_status": 422,
"latency_ms": 13,
"ip": "127.0.0.1",
"user_agent": "curl/8.7.1",
"api_key": { "id": "c7e0321e-95f2-478f-8af1-bbe1f705e5ce", "name": "production" }
}
]
}The request and response bodies are not in the list — fetch one log with Retrieve a log to see them. A page of a hundred bodies would be megabytes you didn't ask for.
Everything is logged, including reads
A GET is logged the same as a POST, so this endpoint appears in its own output. That is deliberate: "who read my contacts, and when" is a question worth being able to answer, and a log that only recorded writes could not answer it.
Finding the call that went wrong
You rarely want to page through logs. You usually have a failure and want that call — and an error already tells you where it is. Its request_id is the log id:
bash
curl "https://api.sendgrail.com/v1/logs/$REQUEST_ID" \
-H "Authorization: Bearer $SENDGRAIL_API_KEY"The filters are for the other case: a sweep. "Show me everything that 422'd this week", or "everything that touched /v1/emails".
Retention
Logs are kept for 30 days, and their request/response bodies for 7. After 7 days a log still tells you what was called, when, from where, and what status it returned — it just no longer carries the bodies, which are the bulk of the weight. After 30 days the row goes too.
This is what keeps the table, and the queries over it, from growing without bound — every call you make adds a row to it, forever, otherwise.
If you need a call's body kept longer than a week, copy it out when you make it.