Appearance
API reference
The SendGrail API is a small HTTP/JSON API for sending email and reading back what happened to it. This page covers the conventions every endpoint shares.
Base URL
https://api.sendgrail.com/v1The version is part of the path. When a breaking change becomes necessary it ships as /v2 alongside /v1, so an integration keeps working until you choose to move it.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/emails | Send an email |
POST | /v1/emails/batch | Send up to 100 emails |
GET | /v1/emails | List emails |
GET | /v1/emails/{id} | Retrieve an email |
PATCH | /v1/emails/{id} | Reschedule an email |
POST | /v1/emails/{id}/cancel | Cancel a scheduled email |
GET | /v1/emails/{id}/attachments | List attachments |
Authentication
Every request carries an API key as a bearer token:
http
Authorization: Bearer sg_xxxxxxxx
Content-Type: application/jsonKeys have one of two access levels, and the difference is deliberate:
| Access | Can | Cannot |
|---|---|---|
| Sending | send, reschedule, cancel | read your mail back |
| Full | all of the above, plus list / retrieve / attachments | — |
A key that lives inside an application should be a Sending key. If it leaks, someone can send from your domain — bad — but they cannot download your message history, which is usually worse. Reading with a Sending key returns 403 restricted_api_key.
Every response carries a request id
http
X-Request-Id: 8f2c1e64-1b17-4f8c-9f0e-3b6c1e0f5c31That id is the id of this call's entry in your dashboard's Logs page — which holds the exact request body, the response we sent, the status and the latency. Errors repeat it in the body:
json
{
"message": "The to field is required.",
"code": "validation_error",
"errors": { "to": ["The to field is required."] },
"request_id": "8f2c1e64-1b17-4f8c-9f0e-3b6c1e0f5c31"
}So a misbehaving call never has to be reconstructed from a timestamp: open Logs → <request_id> and it's there, exactly as we received it. Quote the id to support and we can look at the same thing you are.
Content type
Requests are JSON. Responses are always JSON — including errors, and regardless of the Accept header you send. The API never redirects.
Idempotency
Pass an Idempotency-Key on a send and a retry with the same key returns the original result instead of sending a second copy:
http
Idempotency-Key: order-1234-receiptUse it anywhere a retry is possible — a queue worker, a webhook handler, a mobile client on a flaky connection.
Conventions
- Errors — one shape for every failure. See Errors.
- Rate limits — per team, with headers on every response. See Rate limits.
- Lists — cursor-paginated. See Pagination.
- Ids — every object is addressed by a UUID. Internal database ids are never exposed, so an id you hold is always safe to store and to log.
- Timestamps — ISO 8601, UTC (
2026-07-14T09:41:55+00:00). - Unknown fields in a request body are ignored.