The /v1/places/autocomplete endpoint powers the address field of a checkout, a signup form or a shipping calculator. You send whatever the user has typed so far and get back ranked suggestions, each one already carrying its full hierarchy so you can render Madrid, Comunidad de Madrid, Spain without a second call.
It covers the country, state and city levels. It does not cover street addresses or points of interest such as businesses, opening hours or reviews.
GET https://api.countrydataapi.com/v1/places/autocomplete
| Parameter | Type | Required | Description |
|---|---|---|---|
apikey |
string | Yes | Your API authentication key |
q |
string | Yes | What the user typed. Minimum 2 characters |
country |
string | No | Restrict results to one country: internal id, ISO-2, ISO-3 or name |
types |
string | No | Comma-separated levels to search: country, state, city. Defaults to all three |
limit |
number | No | Number of suggestions, 1-20. Defaults to 5 |
lang |
string | No | Language of the returned names. Defaults to en |
sessiontoken |
string | No | Groups every keystroke of one form field into a single billable session |
The query is normalised before searching, so users do not have to fight the keyboard:
avila finds Ávila, munchen finds München.hospitalet finds L'Hospitalet de Llobregat.york finds New York, not just names that start with York.Results are ranked by how well they match (exact name, then start of the name, then start of an inner word), then from broader to narrower administrative level, then by name length. The match field on each suggestion tells you which rule fired, which is handy for highlighting.
curl "https://api.countrydataapi.com/v1/places/autocomplete?apikey=your-api-key&q=mad&country=ES&limit=5"
const API_KEY = 'your-api-key';
async function search(query, sessionToken) {
const params = new URLSearchParams({
apikey: API_KEY,
q: query,
limit: '5',
sessiontoken: sessionToken,
});
const response = await fetch(
`https://api.countrydataapi.com/v1/places/autocomplete?${params}`
);
return response.json();
}
import requests
response = requests.get(
'https://api.countrydataapi.com/v1/places/autocomplete',
params={
'apikey': 'your-api-key',
'q': 'mad',
'country': 'ES',
'limit': 5,
},
)
data = response.json()
import { CountryDataApi, Places } from '@countrydataapi/sdk';
const api = new CountryDataApi({ apiKey: 'your-api-key' });
const session = Places.createSession();
const { suggestions } = await api.places.autocomplete({
q: 'mad',
country: 'ES',
sessiontoken: session,
});
{
"success": true,
"query": "mad",
"suggestions": [
{
"id": "66c7a6c9e4bda21f4ab1a0f1",
"type": "city",
"name": "Madrid",
"description": "Madrid, Comunidad de Madrid, Spain",
"match": "prefix",
"components": {
"city": { "id": "66c7a6c9e4bda21f4ab1a0f1", "name": "Madrid" },
"state": { "id": "66c7a6c9e4bda21f4ab10a22", "name": "Comunidad de Madrid" },
"country": {
"id": "66c7a6c9e4bda21f4ab10ef2",
"name": "Spain",
"iso2": "ES",
"iso3": "ESP",
"phone_code": "+34",
"flag": "🇪🇸"
}
}
}
],
"count": 1,
"session": { "token": "6f9e...", "billed": true, "ttl_seconds": 180 },
"tokens_used": 1,
"remaining_tokens": 4871
}
| Field | Type | Description |
|---|---|---|
suggestions[].id |
string | Pass this to /v1/places/details |
suggestions[].type |
string | country, state or city |
suggestions[].name |
string | Name in the requested language |
suggestions[].description |
string | Ready to render: "Madrid, Comunidad de Madrid, Spain" |
suggestions[].match |
string | exact, prefix or word |
suggestions[].components |
object | Resolved hierarchy: city, state, country |
session |
object | Present only when you sent a sessiontoken. billed says whether this call was charged |
A typeahead fires a request on every key. Charging per request would make a single address field cost 8-10 tokens and turn your bill into a function of how fast your users type.
Send a sessiontoken — any UUID, generated when the field gains focus — and the whole session costs 1 token no matter how many requests it took:
/autocomplete call. Only the first one is charged./details call for the suggestion the user picked. That call is free and closes the session.Sessions expire after 3 minutes. Without a sessiontoken, each request costs 1 token.
// One session per address field
let session = crypto.randomUUID();
input.addEventListener('focus', () => { session = crypto.randomUUID(); });
// ...every keystroke reuses `session`, so the whole field costs 1 token
{
"success": false,
"error": {
"code": "INVALID_PARAMETER",
"message": "Parameter \"q\" is required and must be at least 2 characters long.",
"status": 400
},
"message": "Parameter \"q\" is required and must be at least 2 characters long."
}
Unlike the older endpoints, /v1/places/* returns a real HTTP status code (400, 401, 402, 404) alongside a stable error.code. See the Error Codes documentation.
/v1/* sends Access-Control-Allow-Origin: *, so you can call the autocomplete directly from your frontend without a proxy. Bear in mind that the API key is then visible to anyone who opens devtools — use a key with a budget you are comfortable exposing, or proxy through your own backend if that matters to you.
country when you already know it. Fewer, better suggestions.types=city if the field is specifically a city field.