API Documentation - Endpoints & Examples

Address Validation

Check an address exists, and get it back in canonical form

The /v1/places/validate endpoint takes the country, state, city and postal code a user typed and tells you whether that combination actually exists — component by component — returning the canonical spelling and, when something is off, the closest correction.

It is the last step before you store a shipping address: the autocomplete guides the user, this confirms what they ended up with, including addresses typed by hand or imported from elsewhere.

Endpoint

GET  https://api.countrydataapi.com/v1/places/validate
POST https://api.countrydataapi.com/v1/places/validate

Both accept the same fields. GET is a CORS-simple request, so it works from the browser without a preflight; POST takes them in a JSON body.

Parameters

Parameter Type Required Description
apikey string Yes Your API authentication key
country string Yes Internal id, ISO-2, ISO-3 or name
state string No State or province name
city string No City name
zipcode string No Postal code
lang string No Language of the returned names. Defaults to en

Components you do not send come back with the verdict not_provided — nothing you omit can make the address invalid.

Request Example

curl "https://api.countrydataapi.com/v1/places/validate?apikey=your-api-key&country=ES&state=Madrid&city=Madrid&zipcode=28001"

POST with a JSON body

const response = await fetch(
  'https://api.countrydataapi.com/v1/places/validate',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      apikey: 'your-api-key',
      country: 'ES',
      state: 'Madrid',
      city: 'Madrid',
      zipcode: '28001',
    }),
  }
);
const { result } = await response.json();

TypeScript SDK

const { result } = await api.places.validate({
  country: 'ES',
  zipcode: '28001',
});

result.normalized.state?.name; // "Comunidad de Madrid"

Response Format

{
  "success": true,
  "result": {
    "valid": true,
    "has_corrections": false,
    "verdict": {
      "country": "confirmed",
      "state": "confirmed",
      "city": "confirmed",
      "zipcode": "confirmed"
    },
    "normalized": {
      "country": {
        "id": "66c7a6c9e4bda21f4ab10ef2",
        "name": "Spain",
        "iso2": "ES",
        "iso3": "ESP",
        "phone_code": "+34",
        "flag": "🇪🇸"
      },
      "state": { "id": "66c7a6c9e4bda21f4ab10a22", "name": "Comunidad de Madrid" },
      "city": { "id": "66c7a6c9e4bda21f4ab1a0f1", "name": "Madrid" },
      "zipcode": "28001"
    },
    "corrections": [],
    "postal": {
      "format": "#####",
      "regex": "^\\d{5}$",
      "example": "12345",
      "matches_format": true
    }
  },
  "tokens_used": 1,
  "remaining_tokens": 4870
}

Verdicts

Verdict Meaning
confirmed The value exists and matches exactly what was sent
corrected The component was resolved, but not from what was sent — see corrections
unconfirmed The value could not be verified
not_provided You did not send this component

valid is true only when every component you sent came back confirmed. Use has_corrections to decide whether to show the user a "did you mean" prompt rather than an error.

A postcode on its own resolves the state

The common checkout case: you only asked for a postcode and a city.

curl "https://api.countrydataapi.com/v1/places/validate?apikey=your-api-key&country=ES&zipcode=28001"

The response fills in normalized.state from the postcode, so you can populate the province field for the user instead of asking for it. The same works the other way around: send a city and the state comes back resolved.

When the postcode is not in our list

Postal code coverage is not complete for every country. If a code is not listed but does match the country's official format, the verdict is corrected rather than unconfirmed, and postal.matches_format is true. Treat that as "plausible, accept it" unless your use case demands certainty.

matches_format is null when the country has no official pattern on record.

Correction example

{
  "valid": false,
  "has_corrections": true,
  "verdict": { "country": "confirmed", "state": "unconfirmed", "city": "confirmed", "zipcode": "not_provided" },
  "corrections": [
    { "component": "state", "input": "Cataluna", "suggestion": "Cataluña" }
  ]
}

Token Usage

This endpoint consumes 1 token per request, regardless of how many components you send.

Related Endpoints