Nobody wants to own a cities table.
We do.

Cities, behind an HTTP GET.

Geomelon is a read-only REST API holding 2,000,000+ cities, 203 countries, every administrative region under them, and their names in 50 languages.

You've written this seed script before. It scrapes something, it half-works, the encoding is wrong for two countries, and eighteen months later nobody remembers where the data came from. This is that, maintained.

16 endpointsall GETstable UUIDsWikidata IDsCORS enabledOpenAPI 3.0
oneshot.geomelon.devAustria · Hungarian
$
    — t=—

    The names are the reason this is hard

    Lat/lon you can get anywhere. What you can't easily get is the fact that a Hungarian user searching for Vienna types Bécs. Send preferredLanguages=hu,de,en and the API walks the list until it finds a name that exists.

    // GET /cities/{id}/translations — Vienna, abridged
    [
    "de"Wien
    "en"Vienna
    "hu"Bécs
    "cs"Vídeň
    "pl"Wiedeń
    "ru"Вена
    "ja"ウィーン
    "zh"维也纳
    "ar"فيينا
    "tr"Viyana
    ]

    Countries and regions carry the same. Ask for one preferred name and you get one; ask for the translations endpoint and you get the whole set to index on.

    16 endpoints, all GET

    There is no write path. Nothing to secure, nothing to migrate, no data of yours living somewhere else.

    cities7 endpoints

    GET /cities/search

    Name, country, region, population range. Sort and paginate.

    GET /cities/{id}

    One city, country and region already joined on.

    GET /cities/{id}/translations

    Every recorded name for that city.

    GET /cities/{id}/settlement-types

    Administrative classification with descriptions.

    GET /cities/byCoordinates/closest

    Nearest cities to a point, distance in km included.

    GET /cities/byCoordinates/largest

    Most populous cities near a point.

    GET /cities/distance

    Kilometres between two cities.

    countries4 endpoints

    GET /countries

    Prefix search on name, or filter by telephone code.

    GET /countries/{id}

    ISO code, dialling code, trunk prefix, driving side, plate code, head of state and government, regions inline.

    GET /countries/{id}/translations

    Country name in every language it has one.

    GET /countries/{id}/regions

    Regions with ISO 3166-2 codes and population.

    regions3 endpoints

    GET /regions

    All regions, optionally scoped to a country.

    GET /regions/{id}

    Region with parent country inlined.

    GET /regions/{id}/translations

    Localised region names.

    languages2 endpoints

    GET /languages

    Every language held, with a count of cities covered.

    GET /languages/{id}

    One language by UUID.

    fields on a city record

    Country name, ISO code, flag emoji and dialling code are already there. Rendering a result row costs you one request, not three.

    idwikidataIdnamelocalizedNamenormalizedNametranslations[]populationlatitudelongitudeelevationareatimeZonepostalCodepostalCodes[]dialingCodedialingCodes[]officialWebsiteflagImagecountryIdcountryNamecountryCodecountryEmojicountryTelephoneCodecountryTelephoneCodes[]regionIdregionNameregionCode

    Pick a client and go

    Typed, zero dependencies, both of them. TypeScript ships CJS and ESM; Python is standard library only, 3.8 through 3.14, ships py.typed.

    $ npm install geomelon
    
    import { GeomelonClient } from "geomelon";
    
    const client = new GeomelonClient({ apiKey: process.env.RAPIDAPI_KEY });
    
    const cities = await client.cities.search({
      name: "barc",
      countryCode: "ES",
      minPopulation: 50000,
      preferredLanguages: "ca,es,en",
      sort: "population_desc",
    });
    
    const nearby = await client.cities.byCoordinatesClosest({
      lat: 41.3825, lon: 2.1769,
    });

    Language we don't ship? The OpenAPI 3.0 spec is at geomelon.dev/openapi.json — openapi-generator covers 60+ targets.

    Free where it matters

    City autocomplete is what most people actually came for, so it costs nothing and needs no account. Everything else is behind a key.

    oneshot

    free · no key · no signup

    • 203 countries, 50 languages, 2,497 country–language pairs
    • Pre-built static JSON — a CDN read, not a database query
    • Access-Control-Allow-Origin: *, call it from the browser
    • Top 10 matches by population, English name as subtitle
    • Dots and apostrophes normalised: st. finds St. John's
    curl https://oneshot.geomelon.dev/at/hu/bec
    Coverage table

    Official packages

    Five clients, plus the dataset itself if you'd rather hold the data than call it.

    TypeScriptgeomelon

    TypeScript client. Typed, zero deps, dual CJS/ESM, Node 18+.

    npm install geomelon
    Pythongeomelon

    Python client. Stdlib only, dataclasses, full type hints.

    pip install geomelon
    Reactgeomelon-react

    Headless autocomplete hook plus a finished ARIA combobox.

    npm install geomelon-react
    MCPgeomelon-mcp

    MCP server, stdio or HTTP, 21 tools. Runs keyless too.

    npx geomelon-mcp
    n8nn8n-nodes-geomelon

    n8n community node. Every resource as a workflow step.

    Settings → Community Nodes
    Hugging Facegeomelon/cities

    The city dataset on Hugging Face for analysis or training.

    load_dataset("geomelon/cities")

    The box at the top isn't a demo.

    It's the production endpoint, unauthenticated, and it stays free. Try to break it. When you need population filters, coordinates or region data, the key takes about a minute.