API reference/address

Validate and enrich a postal address

POST/api/v1/address

authentication
Secret API key as a bearer token
billing
1 credit for a fresh lookup that answers `valid` true; addresses that cannot stand as written and 7-day repeats are free. Two spellings of one address share the repeat marker, so they are charged once. Asking for the deliverability check adds 3 credits only when the partner actually answered.

Reads a postal address against the country it is addressed to: that operator's own format (which parts it requires, how the lines are ordered, what shape the postcode takes), its naming for boxes and other non-street delivery points, the national address registers and postcode directories, the open company registers, and the branch lists mailbox operators publish themselves.

Send the address either as one written address, the way it would appear on an envelope, or as the separated fields (address_line1, address_line2, organization, dependent_locality, locality, administrative_area, postal_code, po_box). Both spellings may be combined: a field sent beside a written address wins over what the parser read out of it. country (ISO 3166-1 alpha-2) is always required, because an address can only be measured against a country's own format; a code the format table does not cover answers 422.

The answer gives the address broken into its parts, formatted as that country writes it, whether the postcode matches the country's pattern (postal_code_valid) and, where the whole directory is installed, whether it exists (postcode_status) and whether the town written on the address is the town that postcode belongs to (locality_matches_postcode). address_type says what kind of delivery point it is (street, po_box, private_mailbox, general_delivery, parcel_locker, rural_route, military, bulk_mail) and address_type_source says what settled it. An address that cannot stand as written answers valid: false with a reason: empty_address, missing_country, unknown_country, missing_street, missing_locality, missing_administrative_area, missing_postal_code, invalid_postal_code or postcode_not_found.

Where a national address register is installed, exists answers whether the building itself is in it. It is true only on a premise match, false only when the register that answered claims to list every address in its country (register_coverage: "complete") and does not carry this one, and null otherwise — including a register that covers only part of its country (register_coverage: "partial", which is what the United States National Address Database is, because states share their data voluntarily). match_level says how close the register got (premise, street, postcode, none).

The risk_score sums the weights of the signals in risk_signals (undeliverable 80, private_mailbox 60, mail_drop 50, vacant 40, general_delivery 40, po_box 30, terminated_postcode 30, locality_mismatch 30, not_in_register 20, bulk_mail 20; 60 and above is high, 30 and above medium), each with the dataset that produced it. Being a post-office box is not scored where a box is how that country receives mail, and not_in_register fires only when exists is false, so a register covering part of a country never contributes to the score.

Every signal whose dataset is not installed answers null, which means "not evaluated" and never "no". deliverability: true asks a licensed partner whether post actually arrives at the address and whether the delivery point is a mail-receiving agency, vacant or residential; while no partner is enabled, deliverability_checked answers false and nothing extra is charged.

The product is about places, not people: no field says who lives or works at an address, and none is ever added.

Request body

field type description
addressstring | nullThe whole address written as it would be on an envelope, with the lines separated by commas or newlines. Required unless `address_line1` is sent. At most 500 characters.
address_line1string | nullThe street line — house number and street name, in the order that country writes them. Required unless `address` is sent. At most 255 characters.
address_line2string | nullA second line — a flat, suite or unit, or a dependent locality. A value that reads as a unit is moved into `unit`. At most 255 characters.
organizationstring | nullThe company or institution the address belongs to. At most 200 characters.
dependent_localitystring | nullA neighbourhood, village or district written below the town. At most 100 characters.
localitystring | nullThe town or city. At most 100 characters.
administrative_areastring | nullThe state, province or region, as a code or a name; it is folded to the code that country uses where one exists. At most 100 characters.
postal_codestring | nullThe postcode or ZIP code. At most 32 characters.
po_boxstring | nullA post-office box written on its own, when it is not part of the address lines. At most 64 characters.
countryrequiredstringAn ISO 3166-1 alpha-2 country code the postal format table covers (`US`, `GB`, `DE`). Case-insensitive. At most 2 characters.
deliverabilitybooleanAsk the licensed partner whether post actually arrives at the address. Reserves the premium credits up front and charges them only when the partner answered. Default: .

Example request

curl -X POST https://spaw.co/api/v1/address \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "address": "1600 Amphitheatre Parkway, Mountain View, CA 94043",
  "country": "US"
}'
const response = await fetch('https://spaw.co/api/v1/address', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_…',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "address": "1600 Amphitheatre Parkway, Mountain View, CA 94043",
    "country": "US"
  }),
});
const result = await response.json();
import requests

response = requests.post(
    'https://spaw.co/api/v1/address',
    headers={'Authorization': 'Bearer sk_live_…'},
    json={
        'address': '1600 Amphitheatre Parkway, Mountain View, CA 94043',
        'country': 'US'
    },
)
result = response.json()
$ch = curl_init('https://spaw.co/api/v1/address');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'address' => '1600 Amphitheatre Parkway, Mountain View, CA 94043',
        'country' => 'US'
    ]),
]);
$result = json_decode(curl_exec($ch), true);

Responses

200The address as the pipeline reads it; `valid: false` with a `reason` for an address that cannot stand as written.

{
    "success": true,
    "data": {
        "valid": true,
        "reason": null,
        "country": "US",
        "country_name": "United States",
        "continent": "NA",
        "is_eu": false,
        "is_eea": false,
        "privacy_regime": "ccpa",
        "formatted": "1600 Amphitheatre Parkway\nMOUNTAIN VIEW, CA 94043",
        "organization": null,
        "street": "Amphitheatre Parkway",
        "house_number": "1600",
        "unit": null,
        "po_box": null,
        "dependent_locality": null,
        "locality": "Mountain View",
        "administrative_area": "CA",
        "postal_code": "94043",
        "postal_code_valid": true,
        "postal_code_type": "zip",
        "postcode_status": "live",
        "locality_matches_postcode": true,
        "address_type": "street",
        "address_type_source": "address_rules",
        "is_po_box": false,
        "is_private_mailbox": null,
        "exists": null,
        "match_level": "street",
        "register": "us-national-address-database",
        "register_coverage": "partial",
        "latitude": 37.4224,
        "longitude": -122.0841,
        "location_source": "postcode_centroid",
        "companies_registered": null,
        "is_mail_drop": false,
        "mail_drop_provider": null,
        "mail_drop_source": null,
        "deliverability_checked": false,
        "is_deliverable": null,
        "is_cmra": null,
        "is_vacant": null,
        "is_residential": null,
        "risk_score": 0,
        "risk_level": "low",
        "risk_signals": [],
        "sources": [
            {
                "dataset": "address-formats",
                "version": "2026-09-05"
            },
            {
                "dataset": "address-rules",
                "version": "2026-09-05"
            },
            {
                "dataset": "us-national-address-database",
                "version": "2026-08-14"
            }
        ]
    },
    "meta": {
        "credits_used": 1,
        "credits_remaining": 9,
        "cache_hit": false,
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

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"
    }
}

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"
    }
}

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