country-state-city
by tansuasici
README.md
# Country State City
<p align="center">
<img src="public/logo.png" alt="Country State City Logo" width="128" height="128">
</p>
<p align="center">
<strong>Complete World Location Data — NPM Package, MCP Server & Web App</strong>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/@tansuasici/country-state-city"><img src="https://img.shields.io/npm/v/@tansuasici/country-state-city?color=blue" alt="npm version"></a>
<a href="https://www.npmjs.com/package/@tansuasici/country-state-city"><img src="https://img.shields.io/npm/dm/@tansuasici/country-state-city" alt="npm downloads"></a>
<img src="https://img.shields.io/badge/Code-MIT-green.svg" alt="Code license: MIT">
<img src="https://img.shields.io/badge/Data-ODbL--1.0-blue.svg" alt="Data license: ODbL 1.0">
</p>
---
## About
Country State City provides a versioned dataset of 250 countries and territories, 4,963 administrative areas, 147,739 populated-place records, and 922 Türkiye districts as an NPM package, an MCP server for AI assistants, and an interactive web app.
## Features
- **Versioned location data** — 250 countries and territories, 4,963 administrative areas, and 147,739 places
- **Immutable public identity** — Namespaced `csc:country|state|city|district:{id}` values, source-ID collision gates, and version-pair migration downloads
- **Normalized display labels** — CLDR-style country names and separated administrative categories across subdivision and place records without changing canonical source names
- **Reproducible artifacts** — one canonical country/state/city source generates optimized data, browser shards, and package copies with byte-level CI drift checks
- **Multiple Formats** — JSON, CSV, XML, YAML output
- **MCP Server** — Connect to Claude Desktop and AI assistants
- **TypeScript** — Full type definitions included
- **Dual Entry Points** — Separate browser (ESM) and Node.js (CJS/ESM) builds
- **Search** — Filter countries, states, and cities by name
- **Ranked location search** — Canonical names, aliases, transliterations, typo tolerance, match reasons, and stable IDs
- **Nearest-centre lookup** — Lazy spatial indexes, batch queries, antimeridian handling, distance, and confidence metadata
- **Timezone utilities** — Observe IANA timezone offsets for a specific instant instead of relying on stale stored offsets
- **Explicit Türkiye districts** — 922 current districts with province parents, legacy aliases, open source IDs, and centre-coordinate review status
- **Optional boundary layers** — Versioned Türkiye admin-1/admin-2 GeoJSON downloads and a lazy overview TopoJSON export
- **Auditable quality** — Machine-readable coverage, provenance, normalization, package-budget, and migration reports
- **Interactive Playground** — Test the API at the live demo
## Quick Start
```bash
npm install @tansuasici/country-state-city
```
```typescript
import { CountryStateCity, toPublicId } from '@tansuasici/country-state-city';
const countries = CountryStateCity.getAllCountries();
const turkey = CountryStateCity.getCountryByIso2('TR');
const states = CountryStateCity.getStatesByCountryId(225);
const cities = CountryStateCity.getCitiesByStateId(2170);
const districts = CountryStateCity.getDistrictsByCountryCode('TR');
const currentAdmin1 = CountryStateCity.getAdministrativeAreas({ level: 1 });
const settlements = CountryStateCity.getSettlements({ countryCode: 'TR' });
const stableCityId = toPublicId('city', cities[0].id);
const stableDistrictId = toPublicId('district', districts[0].id);
// Search
const results = CountryStateCity.searchLocations('İstanbull', {
countryCode: 'TR',
entityTypes: ['state'],
});
// Optional Türkiye boundary asset — not loaded with the core API
const { default: turkeyOverview } = await import(
'@tansuasici/country-state-city/data/boundaries/tr/overview.json',
{ with: { type: 'json' } }
);
// Different formats
const csv = CountryStateCity.getAllCountries('csv');
const xml = CountryStateCity.getStatesByCountryId(231, 'xml');
const yaml = CountryStateCity.getCitiesByStateId(1416, 'yaml');
```
### Country flags in browser UIs
`Country.emoji` is a Unicode regional-indicator sequence, so rendering depends on the operating
system and emoji font. Windows may display `TR`, `AF`, and similar letter pairs instead of color
flags. Use `Country.iso2` to resolve a locally hosted SVG or PNG when the interface needs consistent
cross-platform flags. The website vendors its own SVG set; those website assets are not an npm
package export and should not be hotlinked.
## Hosted REST and GraphQL API
The optional hosted API exposes the same canonical data snapshot under `/api/v1` and includes the immutable data version and source attribution in every response.
```bash
curl 'https://countrystatecity.tansuasici.com/api/v1/countries?limit=3&fields=id,name,iso2' \
-H 'x-api-key: YOUR_API_KEY'
```
REST supports search, pagination, field selection, ETag caching, and per-key rate limits. Read-only GraphQL is available at `/api/graphql`; the OpenAPI contract and operating policy live at `/api/openapi.json` and `/api/policy.json`. See the [hosted API documentation](https://countrystatecity.tansuasici.com/docs/hosted-api) and [interactive playground](https://countrystatecity.tansuasici.com/docs/api-playground).
Offline nearest-centre lookup uses a lazy 3D KD-tree, handles the antimeridian, and returns distance, confidence, and the immutable data version:
```typescript
const nearest = CountryStateCity.nearestCenters(
{ latitude: 40.9811, longitude: 29.0651 },
{ entityTypes: ['country', 'state', 'city'], countryCode: 'TR' }
);
// Ranked multilingual/alias search with canonical identity and match reason
const matches = CountryStateCity.searchLocations('München', {
countryCode: 'DE',
entityTypes: ['city'],
});
```
For polygon containment, import a compatible GeoJSON boundary and use `PolygonLookupIndex`; current versioned coverage is Türkiye admin-1/admin-2. Nearest centres are not treated as proof of administrative containment.
## MCP Integration
Use location data directly in Claude Desktop and other MCP-compatible AI assistants.
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"country-state-city": {
"command": "npx",
"args": ["-y", "@tansuasici/country-state-city"]
}
}
}
```
**11 Tools** — `search_countries`, `get_country`, `get_countries_by_region`, `get_states`, `search_states`, `get_cities`, `search_cities`, `get_stats`, `get_regions`, `get_timezones`, `get_currencies`
**6 Resources** — `://countries`, `://countries/{iso2}`, `://countries/{iso2}/states`, `://states/{id}/cities`, `://stats`, `://snapshot`
The exact production identity policy and version-pair migration index are published as package exports:
```javascript
import identityPolicy from '@tansuasici/country-state-city/data/identity-policy.json';
import migrationIndex from '@tansuasici/country-state-city/data/migrations/index.json';
import coveragePolicy from '@tansuasici/country-state-city/data/coverage-policy.json';
import coverageReport from '@tansuasici/country-state-city/data/coverage-report.json';
import stateCoordinatePolicy from '@tansuasici/country-state-city/data/geography/state-coordinate-policy.json';
import schemaNormalizationPolicy from '@tansuasici/country-state-city/data/schema-normalization-policy.json';
```
## API Reference
### Country Methods
```typescript
CountryStateCity.getAllCountries(format?: 'json' | 'csv' | 'xml' | 'yaml');
CountryStateCity.getCountryById(id: number);
CountryStateCity.getCountryByIso2(iso2: string);
CountryStateCity.getCountryByIso3(iso3: string);
CountryStateCity.searchCountries(query: string);
CountryStateCity.getCountriesByRegion(region: string);
CountryStateCity.getCountriesBySubregion(subregion: string);
CountryStateCity.getCountryTranslation(countryCode, locale);
CountryStateCity.getCoverageReport();
CountryStateCity.getCountryCoverage(countryCode: string);
```
### State Methods
```typescript
CountryStateCity.getAllStates(format?: 'json' | 'csv' | 'xml' | 'yaml');
CountryStateCity.getStateById(id: number);
CountryStateCity.getStatesByCountryId(countryId: number, format?: string);
CountryStateCity.getStatesByCountryCode(countryCode: string, format?: string);
CountryStateCity.searchStates(query: string, countryId?: number);
```
### City Methods
```typescript
CountryStateCity.getAllCities(format?: 'json' | 'csv' | 'xml' | 'yaml');
CountryStateCity.getCityById(id: number);
CountryStateCity.getCitiesByStateId(stateId: number, format?: string);
CountryStateCity.getCitiesByCountryId(countryId: number, format?: string);
CountryStateCity.searchCities(query: string, stateId?: number, countryId?: number);
```
### Canonical Entity Methods
The legacy state/city methods retain the imported collections. For comparable administrative levels and a clean settlement layer, use:
```typescript
CountryStateCity.getAdministrativeAreas({
countryCode?: string,
level?: number, // defaults to 1
lifecycleStatus?: 'current' | 'historical' | 'review-required' | 'all',
sourceLayer?: 'state' | 'city' | 'all', // defaults to state
});
CountryStateCity.getSettlements({
countryCode?: string,
stateId?: number,
lifecycleStatus?: 'current' | 'historical' | 'review-required' | 'all',
});
```
Every state/city row has explicit entity, level, parent, place type, lifecycle, validity, confidence, and policy-source fields. The migration contract and country mappings are exported as `data/entity-level-policy.json`.
### District Methods
The district layer currently covers Türkiye. Legacy city records remain available for backward compatibility; use these methods when the administrative district grain is required.
```typescript
CountryStateCity.getAllDistricts(format?: 'json' | 'csv' | 'xml' | 'yaml');
CountryStateCity.getDistrictById(id: number);
CountryStateCity.getDistrictByPublicId(publicId: string);
CountryStateCity.getDistrictsByStateId(stateId: number, format?: string);
CountryStateCity.getDistrictsByCountryCode(countryCode: string);
CountryStateCity.searchDistricts(query: string, stateId?: number);
```
`csc:district:*` IDs belong to CountryStateCity. Türkiye's public validation source does not publish an authoritative district code, so the dataset keeps `officialDistrictCode: null` instead of inventing one.
### Utility Methods
```typescript
CountryStateCity.getStats();
CountryStateCity.getCoverageReport();
CountryStateCity.getCountryCoverage(countryCode: string);
CountryStateCity.getAllRegions();
CountryStateCity.getAllSubregions();
CountryStateCity.getAllTimezones();
CountryStateCity.getTimezoneOffset(zoneName, at?);
CountryStateCity.getAllCurrencies();
CountryStateCity.searchLocations(query, options?);
CountryStateCity.nearestCenters(point, options?);
CountryStateCity.nearestCentersBatch(points, options?);
CountryStateCity.exportData(dataType, format, options?);
```
`nearestCenters()` compares a coordinate with representative centre points. It does **not** prove that a point is inside an administrative area. For containment, supply compatible GeoJSON to `PolygonLookupIndex` or `locatePointInPolygons()`. Versioned polygon coverage currently includes Türkiye admin-1/admin-2 only.
### Direct Data Exports
Use package exports instead of repository-relative paths:
```typescript
import countries from '@tansuasici/country-state-city/data/countries.json' with { type: 'json' };
import states from '@tansuasici/country-state-city/data/states.json' with { type: 'json' };
import compactCities from '@tansuasici/country-state-city/data/cities.optimized.json' with { type: 'json' };
import turkeyDistricts from '@tansuasici/country-state-city/data/districts/tr.json' with { type: 'json' };
```
The compact city keys are `i` (ID), `n` (name), `s` (state ID), `c` (country ID), `la`/`lo` (coordinates), and optional `w` (Wikidata QID). Use the class API when full `City` objects are preferred.
### TypeScript
```typescript
import { CountryStateCity } from '@tansuasici/country-state-city';
import type {
Country,
State,
City,
District,
DataFormat,
FormatOptions,
} from '@tansuasici/country-state-city';
```
## Migrating to v3
Version 3 keeps the familiar country/state/city methods while tightening the data contract and adding canonical administrative, search, district, timezone, and spatial APIs.
- `@tansuasici/country-state-city/data/cities.json` was removed. Use the class API or `data/cities.optimized.json`.
- Country and state coordinates can be `null` when no trustworthy value is available.
- Country translations are normalized to typed locale keys and values can be `null` when a translation is unavailable.
- State and city records now include explicit entity classification, administrative level, lifecycle, parent, and source fields.
- Centre-based reverse geocoding and polygon containment are deliberately separate APIs.
Review nullable fields when upgrading TypeScript consumers and prefer immutable `csc:*` public IDs for persisted references.
## Development
```bash
# Install dependencies
npm install
# Start dev server
npm run dev
# Build NPM package
npm run build:lib
# Build MCP server
npm run build:mcp
# Run tests
npm test
# Validate the pinned data source and patch manifests
npm run test:data-sync
# Validate immutable public IDs and versioned migration contracts
npm run test:identity
# Check whether a newer upstream release exists
npm run data:check-freshness
# Build and validate a reproducible candidate dataset (does not modify production data)
npm run data:sync -- --check --verify-reproducible
# Regenerate non-destructive subdivision display names and types
npm run data:display-metadata
```
## Contributing
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## License
- Source code is licensed under the [MIT License](LICENSE).
- The country, state, and city database is a derivative database made available under the [Open Database License (ODbL) v1.0](DATA_LICENSE.md).
The data is derived from [Countries States Cities Database](https://github.com/dr5hn/countries-states-cities-database). Attribution is required. Machine-readable source and version details are in [data/provenance.json](data/provenance.json).
---
<p align="center">
Made with ❤️ by <a href="https://tansuasici.com/">tansuasici</a>
</p>
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues