API reference/history

List the account's verification history

GET/api/v1/email/history

authentication
Secret API key as a bearer token
billing
Free.

Every email lookup the account ran through any channel — the API, the dashboard, the form widget, batch and bulk runs, monitors, the MCP server — newest first, 100 per page, for the retention window (meta.retention_days, 30 days by default). Each row records the verdict, the reason, the risk score, whether the mailbox handshake answered, what the lookup cost, whether it was a 7-day repeat, and the channel that ran it as via.

Rows are written after billing, so a refused lookup never appears here, and test addresses are never logged. Filters combine. An unknown verdict or an unparseable date answers 422.

Parameters

name in type description
pagequeryintegerThe page to return. Default: 1.
verdictquerystringOnly lookups with this verdict. One of: deliverable, risky, undeliverable.
emailquerystringOnly lookups of this exact address, compared case-insensitively against the address as it was checked. At most 254 characters.
sincequerystringOnly lookups on or after this date or timestamp (ISO 8601).
untilquerystringOnly lookups on or before this timestamp; a bare date covers the whole day.
viaquerystringOnly lookups from this channel, matched exactly: `api · {key name}`, `dashboard`, `widget · {key name}`, `bulk run #{id}`, `monitor · {name}`, or `mcp · {key name}`. At most 120 characters.

Example request

curl https://spaw.co/api/v1/email/history \
  -H "Authorization: Bearer sk_live_…"
const response = await fetch('https://spaw.co/api/v1/email/history', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer sk_live_…',
  },
});
const result = await response.json();
import requests

response = requests.get(
    'https://spaw.co/api/v1/email/history',
    headers={'Authorization': 'Bearer sk_live_…'},
)
result = response.json()
$ch = curl_init('https://spaw.co/api/v1/email/history');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…'],
]);
$result = json_decode(curl_exec($ch), true);

Responses

200One page of history rows.

{
    "success": true,
    "data": {
        "lookups": [
            {
                "id": 9310,
                "email": "[email protected]",
                "deliverable": "deliverable",
                "reason": null,
                "risk_score": 0,
                "mx_provider": "google",
                "smtp_checked": true,
                "credits_used": 1,
                "cache_hit": false,
                "via": "api · Production key",
                "created_at": "2026-09-03T10:12:44+00:00"
            },
            {
                "id": 9309,
                "email": "[email protected]",
                "deliverable": "risky",
                "reason": "role",
                "risk_score": 30,
                "mx_provider": "other",
                "smtp_checked": true,
                "credits_used": 0,
                "cache_hit": true,
                "via": "dashboard",
                "created_at": "2026-09-03T09:58:02+00:00"
            }
        ]
    },
    "meta": {
        "page": 1,
        "per_page": 100,
        "total": 2,
        "retention_days": 30
    }
}

401The key is missing, malformed, or revoked.

{
    "success": false,
    "error": {
        "code": "UNAUTHENTICATED",
        "message": "Provide a valid API key as a bearer token.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

422The request body could not be validated; `error.errors` lists the fields.

{
    "success": false,
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "The email field is required.",
        "errors": {
            "email": [
                "The email field is required."
            ]
        },
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

429Over 5 requests per second for the key. Retry after the limit resets.

{
    "success": false,
    "error": {
        "code": "RATE_LIMITED",
        "message": "Too many requests. Retry after the limit resets.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

Error codes

Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.

markdown version·openapi.json