Authentication
Secure your API requests with Bearer token authentication.
Creating API Keys
#You can create API keys in two ways:
- From the API Keys dashboard
- Programmatically via
POST /v1/api-keys
Bearer Token Format
#Include your API key in the Authorization header of every request:
Authorization: Bearer sf_live_your_api_keyAll keys start with sf_live_ for production or sf_test_ for test mode.
Example Request
#curl https://smtpfa.st/api/v1/emails \
-H "Authorization: Bearer sf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"from":"[email protected]","to":["[email protected]"],"subject":"Test","html":"<p>Hello</p>"}'const res = await fetch("https://smtpfa.st/api/v1/emails", {
method: "POST",
headers: {
Authorization: "Bearer sf_live_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "[email protected]",
to: ["[email protected]"],
subject: "Test",
html: "<p>Hello</p>",
}),
});import requests
res = requests.post(
"https://smtpfa.st/api/v1/emails",
headers={"Authorization": "Bearer sf_live_your_api_key"},
json={
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Test",
"html": "<p>Hello</p>",
},
)API Key Scopes
#Scopes control what each key can access. When creating a key, select only the permissions you need.
| Parameter | Type | Description |
|---|---|---|
| email:send | scope | Emails: POST /v1/emails and batch sends |
| email:read | scope | Emails: Look up sent emails and analytics |
| domain:read | scope | Domains: List domains and verification status |
| domain:write | scope | Domains: Add, verify and delete domains |
| contact:read | scope | Contacts: List and export contacts |
| contact:write | scope | Contacts: Create, update, import, suppress |
| form:read | scope | Signup forms: List forms and pending signups |
| form:write | scope | Signup forms: Create and edit forms |
| webhook:read | scope | Webhooks: List webhooks |
| webhook:write | scope | Webhooks: Create, edit, test and delete webhooks |
| logs:read | scope | Logs: GET /v1/logs with filters by type, time, recipient, domain and tag |
| inbound:read | scope | Inbound email: List messages, download raw mail and attachments; in inboxes, mark read, label, archive, assign, add notes and write drafts (sending them needs email:send) |
| inbound:delete | scope | Inbound email: Delete received messages and their files, and move inbox threads to trash |
| team:read | scope | Team: List members, and pending invitations for owners and admins |
| team:manage | scope | Team: Invite people, revoke invitations, change roles, remove members |
| billing:read | scope | Billing: The plan, usage against its limits, and invoices |
| billing:manage | scope | Billing: Checkout and billing portal links for a person to open; nothing is charged through the API |
| apikey:manage | scope | API keys: List, create and revoke API keys |
A key with no scopes chosen gets a default set, whether it is created in the dashboard or through the API. Pass scopes to narrow it.
Rate Limit Headers
#Every response includes rate limit headers so you can track your usage:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1712345678See the Rate Limits page for details on limits per tier and how to handle 429 responses.
Authentication Errors
#| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 403 | Key does not have the required scope |
Check what a key can do
#Before this endpoint the only way to discover a key's scopes was to call each one and see which returned 403. This returns them directly, along with the team it belongs to and where it stands against the rate limit. It needs no scope of its own, so any valid key can call it.
/v1/meThe scopes, team, account tier and rate limit position of the key making the request, and the user who created it. user_id matches user_id on GET /v1/team/members, so you can tell which member the key acts as. account.tier is the plan: free, starter, growth or scale.
Response
{
"api_key_id": "key_01j9x3p2q7",
"user_id": "user_01j9x3m4k2",
"scopes": ["email:send", "email:read", "domain:read"],
"team_id": "team_01j9x3n8r1",
"team_sending_status": "active",
"account": { "tier": "growth", "status": "active" },
"send_checks": { "display_name_enforced": false, "link_scan_enforced": false },
"rate_limit": { "per_second": 10, "remaining": 9, "reset": 1758374400000 }
}Check usage against your plan
#Emails in the last 24 hours and this month against your limit, prepaid credits, and contacts, segments, broadcasts and domains against their limits. Check it before a large send or an import instead of finding out from a refusal. The response has the same shape as Resend's GET /usage, so the Resend SDKs' usage call works against SMTPfast. A null limit means no cap. Emails count recipients, the unit the quota charges in, and received mail does not count against the limit. AI credits and automation runs are not offered: they are in the response only to match Resend's shape, and their limit is 0. On a team, the email quota and broadcast cap follow the team owner's plan and the domain limit the team's plan. Like /v1/me, it needs no scope.
/v1/usageUsage and limits for the account the key belongs to.
Response
{
"object": "usage",
"emails": {
"daily": { "used": 37, "limit": null, "sent": 37, "received": 3, "resets_at": null },
"monthly": { "used": 1200, "limit": 100000, "sent": 1200, "received": 40, "resets_at": "2026-11-01T00:00:00.000Z" },
"credits": { "balance": 500 },
"effective_remaining": 99300
},
"contacts": { "used": 205, "limit": 50000 },
"segments": { "used": 1, "limit": 50 },
"broadcasts": { "used": 4, "limit": 50 },
"ai_credits": { "used": 0, "limit": 0, "next_increase_at": null },
"automation_runs": { "used": 0, "limit": 0, "resets_at": "2026-11-01T00:00:00.000Z" },
"domains": { "used": 2, "limit": null },
"rate_limit": { "limit": 10, "duration": "1000ms" }
}