Getting started
Spaw verifies email addresses over a plain REST API and a dashboard, with one credit balance across everything. This guide takes you from a fresh account to your first verified address in a few minutes.
The first request does not wait for any of it. A published sandbox key answers the sample requests in these docs without an account, so you can see a real response before you decide to sign up — skip to your first request and paste it.
Create an API key
Past the sandbox key, every API request authenticates with a key created from the dashboard.
- 1
Create an account
Sign up with an email and password, then confirm the address from the email that arrives: the API refuses every request from an unconfirmed account. Confirming also credits a one-off signup bonus, on top of the free credits every account is granted at the start of each month. No card required.
- 2
Open the API keys page
Once signed in, choose API keys in the header. Name the key after where it will live — "Production", "Staging" — so your usage history stays readable.
- 3
Copy the key
The secret key is shown once and stored hashed; copy it before leaving the page. Revoking a key stops its requests immediately.
There is a second kind of key: publishable pk_ keys, made for the browser form widget. They can run the four browser lookups — email, phone, IP and postal address — only from the domains you allow, and stay visible in the dashboard. Their answers carry no meta block, so a page visitor never sees the account's balance. Keep secret keys on servers; ship publishable keys to pages.
Key controls. A secret key can be limited when it is created: scopes (email, IP, phone, address, entity, consistency, account) confine it to the product families an integration needs, a daily credit cap stops a runaway script (single and batch calls count what they bill; bulk runs are not counted but cannot be started once the cap is reached), and an allowlist of addresses or CIDR ranges makes a leaked key useless elsewhere. Refused calls answer KEY_SCOPE_DENIED, KEY_SPEND_CAP_REACHED or KEY_IP_NOT_ALLOWED and bill nothing. A batch that reaches the cap part-way is not refused: it stops there, keeps the items already paid for, and answers 200 with meta.stopped_reason: "key_spend_cap" — so a single large batch can never spend past the cap. All three controls apply to tool calls over the MCP server exactly as they apply here, so a key given to an agent is limited by the same three things; a scope or cap refusal reaches the agent as a tool error naming the same code, because an HTTP status is something an MCP client reports as a broken connection. The controls are fixed at creation: create a new key to change them. To replace a key without downtime — a leak, or a scheduled rotation — use Rotate on the API keys page: the replacement carries the same scopes, cap, address allowlist and default country, and the old key keeps working for 24 hours so you can move each integration over, or you can end it immediately with Revoke now.
Your first request
Send the key as a bearer token, JSON in and out. Verifying an address is one POST — and this one runs as printed, because sk_sandbox_spaw is the published sandbox key rather than a placeholder:
$ curl https://spaw.co/api/v1/email \ -H "Authorization: Bearer sk_sandbox_spaw" \ -H "Content-Type: application/json" \ -d '{"email": "[email protected]"}'
{
"success": true,
"data": {
"email": "[email protected]",
"deliverable": "deliverable",
"reason": null,
"risk_score": 0,
"mx_provider": "other",
"disposable": false,
… 21 more fields, see the API reference
},
"meta": { "credits_used": 0, "credits_remaining": null, "sandbox": true }
}What the sandbox key does. It answers five lookups — POST /api/v1/email, /phone, /ip, GET /api/v1/ip/{ip} and POST /api/v1/address — and only for the sample values these docs print: the six test addresses on spaw.test, the seven test numbers +1 202 555 0100 to 0106, the sample IP 8.8.8.8, the sample postal address in the reference and the five test postal addresses at 1 to 5 Spaw Test Street, Springfield, IL 99999 (each answers ok_to_ship and blocked_by from the published default shipping policy, so the decision fields can be exercised without an account). The request body has to carry those fields and nothing else, so a body with an extra option is refused rather than quietly ignored. Everything else answers SANDBOX_ENDPOINT_NOT_ALLOWED or SANDBOX_VALUE_NOT_ALLOWED, naming what it would have taken. It is limited to 30 requests a minute from one address.
What it does not do. The key is printed here, so everybody has it: it is attached to no account, spends no credits, writes nothing that lasts, and cannot read a balance, a bulk run, a monitored list, a suppression list or a request log. That is why meta.credits_remaining answers null and meta.sandbox is true. Swap in your own sk_live_ key and the same call answers for any address, bills a credit for a fresh usable answer, and reports the balance it left.
Every endpoint answers the same envelope — { success, data, meta } on success, a typed error.code on failure — and no verification ever sends mail. The dashboard playground runs the exact same lookups if you want to try addresses before writing code, and the six fixed test email addresses ([email protected], [email protected], …), the seven test phone numbers and the five test postal addresses the sandbox answers for are free on your own key too, so integration tests never spend credits; see the API reference.
Credits & billing
You pay for answers, not attempts, and anything you paid to check once is free to check again for a week.
| outcome | credits |
|---|---|
| Fresh lookup with a usable answer | 1 credit |
| Invalid input or undeliverable verdict | 0 |
| Repeat of a recent lookup, within 7 days | 0 |
| Rejected or failed request | 0 |
Two credits arrive without being bought: a free grant at the start of every calendar month, and a one-off signup bonus the first time the account confirms its email address. Both land on the same balance, never expire, and are listed on the Usage page like any other movement. The size of the monthly grant is on the pricing page; the bonus is named in the welcome email.
The dashboard's Usage page lists every credit movement with the key or channel that spent it, and GET /api/v1/account returns the live balance for pre-flight checks.
Need more than the free grant? Buy a credit pack from the Billing page in the dashboard, or start a monthly plan that deposits the same credits every month for less. Packs start at $9 for 1,000 credits, plans at $7 a month; credits from either never expire, and a plan is cancelled at any time.
Next: the email verification guide for verdicts, lists, bulk runs, and the form widget — or the API reference for every endpoint and field.