API Documentation - Endpoints & Examples

Places Autocomplete

Search countries, states and cities as the user types

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.

Endpoint

GET https://api.countrydataapi.com/v1/places/autocomplete

Query Parameters

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

How matching works

The query is normalised before searching, so users do not have to fight the keyboard:

  • Case and accent insensitiveavila finds Ávila, munchen finds München.
  • Punctuation insensitivehospitalet finds L'Hospitalet de Llobregat.
  • Any word matchesyork 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.

Request Example

curl "https://api.countrydataapi.com/v1/places/autocomplete?apikey=your-api-key&q=mad&country=ES&limit=5"

JavaScript

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();
}

Python

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()

TypeScript SDK

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,
});

Response Format

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

Response Fields

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

Token Usage: pay per session, not per keystroke

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:

  1. Generate a token when the address input gains focus.
  2. Send it on every /autocomplete call. Only the first one is charged.
  3. Send it on the /details call for the suggestion the user picked. That call is free and closes the session.
  4. Generate a new token for the next address field.

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

Error Response

{
  "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.

Calling from the browser

/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.

Practical tips

  1. Debounce at around 150 ms. Fewer requests, same feel.
  2. Require 2-3 characters before firing. Shorter queries are not useful.
  3. Pass country when you already know it. Fewer, better suggestions.
  4. Pass types=city if the field is specifically a city field.
  5. Cache nothing per user — but you are free to store and cache the returned data for as long as you want. There is no restriction on retaining it.

Related Endpoints

Complete Integration Guide