API reference/address
Validate an address from the browser with a publishable key
POST/api/v1/address/public
- authentication
- Publishable key in the body, checked against the browser Origin header
- billing
- Bills the key owner like a single lookup; capped by the key's daily credit cap when one is set.
The browser counterpart of POST /api/v1/address, for checking an address field in a checkout or signup form on the client without exposing a secret key. It is authenticated by a publishable pk_ key in the body plus the browser's Origin header, which must match one of the domains the key is locked to. A missing Origin is rejected on purpose: servers use a secret key.
The body takes the same address fields as the secret-key endpoint, including the required country. The lookup bills the key's owner under the normal rules (a fresh address that stands as written costs one credit; addresses that cannot, and 7-day repeats, are free) and answers { "success", "data" } with no meta block, so page visitors never see the owner's balance. The licensed deliverability check is never offered here: a deliverability field in the body is ignored.
Because the key sits in page source, give it a daily credit cap in the dashboard: once the cap is spent the endpoint answers 429 KEY_SPEND_CAP_REACHED until the next day. Free answers never count toward the cap. A key that carries a Cloudflare Turnstile pair requires a confirmed turnstile_token and answers 403 TURNSTILE_FAILED without one, before any credit is spent.
Throttled at 20 requests per minute per IP.
Parameters
| name | in | type | description |
|---|---|---|---|
| Originrequired | header | string | Sent by browsers automatically. Its host must be on the key's allowed-domain list. |
Request body
| field | type | description |
|---|---|---|
| keyrequired | string | A publishable key, which starts with `pk_`. At most 64 characters. |
| address | string | null | The whole address written as it would be on an envelope. Required unless `address_line1` is sent. At most 500 characters. |
| address_line1 | string | null | The street line. Required unless `address` is sent. At most 255 characters. |
| address_line2 | string | null | At most 255 characters. |
| organization | string | null | At most 200 characters. |
| dependent_locality | string | null | At most 100 characters. |
| locality | string | null | At most 100 characters. |
| administrative_area | string | null | At most 100 characters. |
| postal_code | string | null | At most 32 characters. |
| po_box | string | null | At most 64 characters. |
| countryrequired | string | An ISO 3166-1 alpha-2 country code, usually from the country select on the same form. At most 2 characters. |
| turnstile_token | string | null | Required when the key carries a Turnstile pair. A single-use token from the Turnstile widget on the page. At most 2,048 characters. |
Example request
curl -X POST https://spaw.co/api/v1/address/public \
-H "Origin: https://www.example.com" \
-H "Content-Type: application/json" \
-d '{
"key": "pk_live_…",
"address_line1": "221B Baker Street",
"locality": "London",
"postal_code": "NW1 6XE",
"country": "GB"
}'Responses
200The same fields as `POST /api/v1/address`, without a meta block.
{
"success": true,
"data": {
"valid": true,
"reason": null,
"country": "GB",
"street": "Baker Street",
"house_number": "221B",
"locality": "London",
"postal_code": "NW1 6XE",
"postal_code_valid": true,
"postal_code_type": "postal",
"address_type": "street",
"is_po_box": false,
"deliverability_checked": false,
"risk_score": 0,
"risk_level": "low"
}
}401The publishable key does not exist or was revoked.
{
"success": false,
"error": {
"code": "INVALID_PUBLISHABLE_KEY",
"message": "That publishable key does not exist or was revoked.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}402The balance is empty. The lookup did not run.
{
"success": false,
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Your credit balance is empty.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}403The page's origin is not on the key's allowed-domain list (`ORIGIN_NOT_ALLOWED`), or the key requires a Turnstile token that was missing or not confirmed (`TURNSTILE_FAILED`).
{
"success": false,
"error": {
"code": "ORIGIN_NOT_ALLOWED",
"message": "This publishable key cannot be used from this origin.",
"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"
}
}429The key has spent its owner's daily credit cap (`KEY_SPEND_CAP_REACHED`), or the IP exceeded 20 requests per minute (`RATE_LIMITED`).
{
"success": false,
"error": {
"code": "KEY_SPEND_CAP_REACHED",
"message": "This publishable key has reached its daily credit cap. The counter resets each day.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}Error codes
Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.