# Authentication Source: https://docs.countrystatecity.in/api/authentication Learn how to authenticate your requests to the Country State City API using API keys ## API Key Authentication The Country State City API uses API key authentication for all endpoints. Your API key must be included in every request using the `X-CSCAPI-KEY` header. Register at [our portal](https://app.countrystatecity.in) to obtain your API key. API keys are generated instantly and are ready to use immediately. Include your API key in the `X-CSCAPI-KEY` header for every request: ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); ``` ```python Python theme={null} import requests headers = { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers=headers ) ``` Check for authentication errors and implement proper error handling: ```javascript theme={null} if (response.status === 401) { console.error('Invalid API key'); } else if (response.ok) { const data = await response.json(); console.log('Success:', data); } ``` ## Security Best Practices **Keep your API key secure!** Never expose it in client-side code, public repositories, or logs. ### ✅ Do This * Store API keys in environment variables * Use server-side configurations * Rotate keys regularly * Monitor API key usage * Implement proper error handling ### ❌ Don't Do This * Hard-code API keys in source code * Commit API keys to version control * Share API keys in plain text * Use the same key across all environments * Ignore authentication errors ## Environment Variables Store your API key securely using environment variables: ```javascript .env theme={null} CSC_API_KEY=your_api_key_here ``` ```javascript app.js theme={null} const apiKey = process.env.CSC_API_KEY; const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': apiKey } }); ``` ```bash .env theme={null} CSC_API_KEY=your_api_key_here ``` ```python app.py theme={null} import os import requests api_key = os.getenv('CSC_API_KEY') response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers={'X-CSCAPI-KEY': api_key} ) ``` ```php .env theme={null} CSC_API_KEY=your_api_key_here ``` ```php app.php theme={null} ``` ## Authentication Errors When authentication fails, the API returns a `401 Unauthorized` status with an error message: ```bash Invalid API Key theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: invalid_key' ``` ```json Error Response theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Authentication Issues **Cause**: Invalid, missing, or expired API key **Solution**: * Verify your API key is correct * Check the header name is exactly `X-CSCAPI-KEY` * Ensure the key hasn't been revoked * Register for a new key if needed **Cause**: HTTP client configuration issues **Solution**: * Verify headers are properly set * Check for typos in header name * Ensure headers aren't being stripped by proxies * Test with a simple cURL command first **Cause**: Browser blocking cross-origin requests **Solution**: * Make API calls from your server instead * Use a proxy server for development * Never use API keys in browser applications ## Testing Your Authentication Use this simple test to verify your API key is working: ```bash cURL Test theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' \ -w "HTTP Status: %{http_code}\n" ``` ```javascript JavaScript Test theme={null} const testAuth = async () => { try { const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.ok) { console.log('✅ Authentication successful'); const data = await response.json(); console.log(`Found ${data.length} countries`); } else { console.log('❌ Authentication failed:', response.status); } } catch (error) { console.error('Request failed:', error); } }; testAuth(); ``` ```python Python Test theme={null} import requests def test_auth(): try: response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: print('✅ Authentication successful') data = response.json() print(f'Found {len(data)} countries') else: print(f'❌ Authentication failed: {response.status_code}') print(response.json()) except Exception as error: print(f'Request failed: {error}') test_auth() ``` A successful authentication test should return HTTP 200 with a list of countries. If you get different results, check your API key and request format. # Convert ISO Code Source: https://docs.countrystatecity.in/api/endpoints/convert-iso-code GET /iso/country/convert Translate a country code between ISO 3166-1 alpha-2, alpha-3, and numeric formats Convert a country code between the three ISO 3166-1 formats in a single call: * alpha-2 ↔ alpha-3 (e.g. `US` ↔ `USA`) * alpha-2 ↔ numeric (e.g. `US` ↔ `840`) * alpha-3 ↔ numeric (e.g. `USA` ↔ `840`) Useful when an upstream system gives you one format and a downstream service expects another. **Availability:** Starter plan and above. Returns `403` on Community plan. **Cache reuse:** Conversions share the same cache slot as [`/v1/iso/country`](/api/endpoints/lookup-country-by-iso). A previous `?iso2=US` lookup serves any `?from=iso2&value=US&to=...` conversion without a fresh database hit. The target column is extracted after the cache read, so different `to` values reuse the same slot. Numeric inputs `4` and `004` resolve to the same cache slot. ```bash cURL (alpha-2 → alpha-3) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country/convert?from=iso2&to=iso3&value=US' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (alpha-3 → numeric) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country/convert?from=iso3&to=numeric&value=IND' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (numeric → alpha-2) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country/convert?from=numeric&to=iso2&value=840' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const params = new URLSearchParams({ from: 'iso2', to: 'iso3', value: 'DE' }); const response = await fetch( `https://api.countrystatecity.in/v1/iso/country/convert?${params}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const { result } = await response.json(); console.log(result); // "DEU" ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/iso/country/convert', params={'from': 'iso2', 'to': 'numeric', 'value': 'JP'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) print(response.json()['result']) # "392" ``` ```json 200 - Success (alpha-2 → alpha-3) theme={null} { "from": "iso2", "to": "iso3", "input": "US", "result": "USA" } ``` ```json 200 - Success (alpha-3 → numeric) theme={null} { "from": "iso3", "to": "numeric", "input": "IND", "result": "356" } ``` ```json 400 - Same from/to theme={null} { "status": "error", "message": "Invalid query parameters: from and to must be different" } ``` ```json 400 - Value doesn't match from theme={null} { "status": "error", "message": "Invalid query parameters: value does not match the format required by from" } ``` ```json 400 - Invalid format name theme={null} { "status": "error", "message": "Invalid query parameters: from: must be one of: iso2, iso3, numeric" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "isoLookup", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - Source code unknown theme={null} { "status": "error", "message": "Country not found for the given code" } ``` ```json 404 - Target column null theme={null} { "status": "error", "message": "No iso3 code available for this country" } ``` ## Related Endpoints * [Lookup Country by ISO Code](/api/endpoints/lookup-country-by-iso) — return the full country record instead of just the converted code * [Lookup State by ISO Code](/api/endpoints/lookup-state-by-iso) — ISO 3166-2 subdivision lookup # Fuzzy Search Source: https://docs.countrystatecity.in/api/endpoints/fuzzy-search GET /search/fuzzy Typo-tolerant search for cities, states, and countries using trigram similarity Search cities, states, and countries by an approximate query that tolerates **misspellings, transliterations, native-script variants, and stored translations**. Unlike the exact/substring [inline search](/api/endpoints/get-all-countries) (`?q=`), fuzzy search ranks results by how *similar* they are to your query, so `Mumbay` still resolves to **Mumbai** and `Beijng` to **Beijing**. Matching uses PostgreSQL trigram similarity over both the English `name` and the `native` name, returning a `match_score` so you can decide how confident a hit is. **Availability:** Supporter plan and above. Returns `403` on Community and Starter plans. Each result is scored against the English and native names. Queries with at least 3 characters also check stored translations. Responses are cached separately by query, filters, plan, locale, and translation output. ## Response Returns an array sorted by `match_score` descending. Each item carries the standard fields for the entity (`city`, `state`, or `country`) at your plan's data-access level — every plan with fuzzy search access (Supporter, Professional, Business) resolves to the **full** field set — plus two fuzzy-specific fields: A result found only through a translation can have a low `match_score`. The score compares the query with the English and native names, not with the translation that matched. ```bash cURL (typo) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/fuzzy?q=Mumbay&type=city&country=IN' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (country) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/fuzzy?q=Deutschlnd&type=country' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (looser threshold) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/fuzzy?q=Munchen&type=city&threshold=0.2&limit=5' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const params = new URLSearchParams({ q: 'Mumbay', type: 'city', country: 'IN' }); const response = await fetch( `https://api.countrystatecity.in/v1/search/fuzzy?${params}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const matches = await response.json(); matches.forEach((m) => console.log(`${m.name} (${m.match_score})`)); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/search/fuzzy', params={'q': 'Mumbay', 'type': 'city', 'country': 'IN'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) for match in response.json(): print(f"{match['name']} -> {match['match_score']}") ``` ```json 200 - City match (Supporter, Professional, Business) theme={null} [ { "id": 132649, "name": "Mumbai", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.07283000", "longitude": "72.88261000", "timezone": "Asia/Kolkata", "native": "मुंबई", "match_score": 0.87, "matched_alias": null } ] ``` ```json 200 - Native-script match theme={null} [ { "id": 132649, "name": "Mumbai", "native": "मुंबई", "match_score": 1, "matched_alias": "मुंबई" } ] ``` ```json 400 - Query too short theme={null} { "status": "error", "message": "Invalid query parameters: q: Search query must be at least 2 characters" } ``` ```json 400 - country with type=country theme={null} { "status": "error", "message": "Invalid query parameters: country: country is not a valid filter when type=country" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "fuzzySearch", "currentTier": "starter", "requiredTier": "supporter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Related Endpoints * [Autocomplete](/api/endpoints/get-search-autocomplete) — deterministic exact/starts-with/fuzzy ranking with a ready-to-display `label`, built for type-ahead UIs * [Get All Countries](/api/endpoints/get-all-countries) — exact/substring inline search via `?q=` * [Get Cities by Country](/api/endpoints/get-cities-by-country) — list cities, filterable by `?q=` * [Lookup Country by ISO Code](/api/endpoints/lookup-country-by-iso) — resolve a known ISO code to a country # Get All Countries Source: https://docs.countrystatecity.in/api/endpoints/get-all-countries GET /countries Retrieve a complete list of all countries with basic information Retrieve a complete list of all countries with basic information including ISO codes, phone codes, currencies, and regional data. **Availability:** All plans (Community and above). The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. Additional fields like `numeric_code`, `currency_name`, `currency_symbol`, `tld`, `nationality`, `population`, `gdp`, `area_sq_km`, `postal_code_format`, `postal_code_regex`, `emojiU`, `translations`, and `wikiDataId` are returned on higher tiers. See the **Tier-Based Field Availability** section below. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries?q=india' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) countries = response.json() print(countries) ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const countries = await response.json(); console.log(countries); ``` ```php PHP theme={null} 'https://api.countrystatecity.in/v1/countries', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); $countries = json_decode($response, true); echo json_encode($countries, JSON_PRETTY_PRINT); ?> ``` ```go Go theme={null} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { url := "https://api.countrystatecity.in/v1/countries" client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) fmt.Println(string(body)) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Countries { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries")) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' uri = URI("https://api.countrystatecity.in/v1/countries") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) countries = JSON.parse(response.body) puts JSON.pretty_generate(countries) ``` ```json 200 - Basic tier (Community, Starter, Legacy) theme={null} [ { "id": 101, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "native": "भारत", "emoji": "🇮🇳", "latitude": "20.00000000", "longitude": "77.00000000", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]" } ] ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} [ { "id": 101, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "native": "भारत", "emoji": "🇮🇳", "latitude": "20.00000000", "longitude": "77.00000000", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]", "numeric_code": "356", "currency_name": "Indian rupee", "currency_symbol": "₹", "tld": ".in", "nationality": "Indian", "population": 1380004385, "gdp": 2870504000000, "area_sq_km": 3287263, "postal_code_format": "######", "postal_code_regex": "^(\\d{6})$", "emojiU": "U+1F1EE U+1F1F3", "translations": "{\"kr\":\"인도\",\"de\":\"Indien\",\"es\":\"India\",\"fr\":\"Inde\",\"ja\":\"インド\"}", "wikiDataId": "Q668" } ] ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Tier-Based Field Availability The fields returned depend on your plan's data access level. | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `name`, `iso2`, `iso3`, `phonecode`, `capital`, `currency`, `native`, `emoji`, `latitude`, `longitude`, `region`, `region_id`, `subregion`, `subregion_id`, `timezones` | | **Full** | Supporter, Professional, Business | All Basic **+** `numeric_code`, `currency_name`, `currency_symbol`, `tld`, `nationality`, `population`, `gdp`, `area_sq_km`, `postal_code_format`, `postal_code_regex`, `emojiU`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. ## Common Use Cases Use this endpoint to populate country selection dropdowns in forms. ```javascript theme={null} const populateCountryDropdown = async () => { const countries = await fetch('/api/countries').then(r => r.json()); const select = document.getElementById('country-select'); countries.forEach(country => { const option = document.createElement('option'); option.value = country.iso2; option.textContent = country.name; select.appendChild(option); }); }; ``` Get currency information for financial applications. ```javascript theme={null} const getCurrencyInfo = (countries, countryCode) => { const country = countries.find(c => c.iso2 === countryCode); return { code: country.currency, symbol: country.emoji }; }; ``` Cache country data locally as it rarely changes. This reduces API calls and improves application performance. # Get All Regions Source: https://docs.countrystatecity.in/api/endpoints/get-all-regions GET /regions Retrieve a list of geographic regions Retrieve all geographic regions (Africa, Americas, Asia, Europe, Oceania, Polar). Supports optional search filtering. **Availability:** Starter plan and above. Returns `403` on lower tiers. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/regions' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/regions?q=asia' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/regions', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const regions = await response.json(); console.log(regions); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/regions', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) regions = response.json() print(regions) ``` ```json 200 - Success theme={null} [ { "id": 1, "name": "Africa" }, { "id": 2, "name": "Americas" }, { "id": 3, "name": "Asia" }, { "id": 4, "name": "Europe" }, { "id": 5, "name": "Oceania" }, { "id": 6, "name": "Polar" } ] ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "regionsApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Related Endpoints * [Get Subregions of a Region](/api/endpoints/get-subregions-by-region) — drill down into subregions * [Get Countries in a Subregion](/api/endpoints/get-countries-by-subregion) — countries within a subregion # Get All States Source: https://docs.countrystatecity.in/api/endpoints/get-all-states GET /states Retrieve a complete list of all states, provinces, and regions worldwide Retrieve a complete list of all states, provinces, regions, and territories from around the world with basic geographical information. **Availability:** Starter plan and above. Calling `/states` without filtering by country (`bulkStates`) is gated to Starter+. Community users must call `/countries/{iso2}/states` instead. The fields returned also vary by tier — see **Tier-Based Field Availability** below. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. Additional fields like `fips_code`, `iso3166_2`, `level`, `parent_id`, `native`, `population`, `translations`, and `wikiDataId` are returned on higher tiers. See **Tier-Based Field Availability** below. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/states' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/states?q=texas' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/states?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/states', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) states = response.json() print(f'Found {len(states)} states') ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/states', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const states = await response.json(); console.log(`Found ${states.length} states`); ``` ```php PHP theme={null} 'https://api.countrystatecity.in/v1/states', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); $states = json_decode($response, true); echo "Found " . count($states) . " states\n"; ?> ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) func main() { url := "https://api.countrystatecity.in/v1/states" client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) var states []map[string]interface{} json.Unmarshal(body, &states) fmt.Printf("Found %d states\n", len(states)) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class AllStates { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/states")) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); Gson gson = new Gson(); Type listType = new TypeToken>>(){}.getType(); List> states = gson.fromJson(response.body(), listType); System.out.println("Found " + states.size() + " states"); } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' uri = URI("https://api.countrystatecity.in/v1/states") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) states = JSON.parse(response.body) puts "Found #{states.length} states" ``` ```json 200 - Basic tier (Starter, Legacy) theme={null} [ { "id": 4008, "name": "Maharashtra", "country_id": 101, "country_code": "IN", "iso2": "MH", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata" } ] ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} [ { "id": 4008, "name": "Maharashtra", "country_id": 101, "country_code": "IN", "iso2": "MH", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata", "fips_code": "16", "iso3166_2": "IN-MH", "type": "state", "level": 1, "parent_id": null, "native": "महाराष्ट्र", "population": 112374333, "translations": "{\"kr\":\"마하라슈트라\",\"de\":\"Maharashtra\",\"fr\":\"Maharashtra\",\"cn\":\"马哈拉施特拉邦\"}", "wikiDataId": "Q1191" } ] ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Analyze administrative divisions across different countries. ```javascript theme={null} const analyzeStateTypes = (states) => { const typeCount = states.reduce((acc, state) => { acc[state.type] = (acc[state.type] || 0) + 1; return acc; }, {}); console.log('State types distribution:', typeCount); return typeCount; }; ``` Use coordinate data for mapping applications. ```javascript theme={null} const getStatesBounds = (states) => { const lats = states.map(s => parseFloat(s.latitude)); const lngs = states.map(s => parseFloat(s.longitude)); return { north: Math.max(...lats), south: Math.min(...lats), east: Math.max(...lngs), west: Math.min(...lngs) }; }; ``` This endpoint returns a large dataset (5,299+ states). Consider using the filtered endpoints for better performance in most applications. ## Tier-Based Field Availability This endpoint requires **Starter+** (Legacy plans also include it), so Community accounts never reach field access at all — they get a `403` first. | Tier | Plans | Fields | | - | - | - | | **Basic** | Starter, Legacy | `id`, `name`, `iso2`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone` | | **Full** | Supporter, Professional, Business | All Basic **+** `fips_code`, `iso3166_2`, `type`, `level`, `parent_id`, `native`, `population`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Cities by Country Source: https://docs.countrystatecity.in/api/endpoints/get-cities-by-country GET /countries/{ciso}/cities Retrieve all cities within a specific country Retrieve all cities within a specific country using the country's ISO2 code. This endpoint is useful for building location selectors and geographical applications. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. City responses on the **Basic** tier return `id`, `name`, and `kind`. Upgrading to **Supporter+** unlocks the full field set: `state_id`, `state_code`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone`, `population`, `type`, `level`, `parent_id`, `native`, `translations`, and `wikiDataId`. See **Tier-Based Field Availability** below. **Availability:** `/countries/{iso2}/cities` (all cities in a country) requires **Starter+**. Community users must use [Get Cities by State](/api/endpoints/get-cities-by-state) instead. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities?q=mumbai' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (filter by kind) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities?kind=settlement' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_cities_by_country(country_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/cities', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: cities = response.json() print(f'Found {len(cities)} cities in {country_code}') return cities else: print('Country not found or no cities available') return [] cities = get_cities_by_country('IN') ``` ```python Python (filter by kind) theme={null} import requests def get_settlements_by_country(country_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/cities', params={'kind': 'settlement'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: cities = response.json() print(f'Found {len(cities)} settlements in {country_code}') return cities else: print('Country not found or no cities available') return [] cities = get_settlements_by_country('IN') ``` ```javascript JavaScript theme={null} const getCitiesByCountry = async (countryCode) => { const response = await fetch(`https://api.countrystatecity.in/v1/countries/${countryCode}/cities`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.ok) { const cities = await response.json(); console.log(`Found ${cities.length} cities in ${countryCode}`); return cities; } else { console.error('Country not found or no cities available'); return []; } }; getCitiesByCountry('IN'); ``` ```javascript JavaScript (filter by kind) theme={null} const getSettlementsByCountry = async (countryCode) => { const response = await fetch( `https://api.countrystatecity.in/v1/countries/${countryCode}/cities?kind=settlement`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); if (response.ok) { const cities = await response.json(); console.log(`Found ${cities.length} settlements in ${countryCode}`); return cities; } else { console.error('Country not found or no cities available'); return []; } }; getSettlementsByCountry('IN'); ``` ```php PHP theme={null} "https://api.countrystatecity.in/v1/countries/{$countryCode}/cities", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode == 200) { $cities = json_decode($response, true); echo "Found " . count($cities) . " cities in {$countryCode}\n"; return $cities; } else { echo "Country not found or no cities available\n"; return []; } } $cities = getCitiesByCountry('IN'); ?> ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) func getCitiesByCountry(countryCode string) []map[string]interface{} { url := fmt.Sprintf("https://api.countrystatecity.in/v1/countries/%s/cities", countryCode) client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() if res.StatusCode == 200 { body, _ := ioutil.ReadAll(res.Body) var cities []map[string]interface{} json.Unmarshal(body, &cities) fmt.Printf("Found %d cities in %s\n", len(cities), countryCode) return cities } else { fmt.Printf("Country not found or no cities available\n") return nil } } func main() { cities := getCitiesByCountry("IN") fmt.Println(cities) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class CitiesByCountry { public static void main(String[] args) throws Exception { String countryCode = "IN"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries/" + countryCode + "/cities")) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { Gson gson = new Gson(); Type listType = new TypeToken>>(){}.getType(); List> cities = gson.fromJson(response.body(), listType); System.out.println("Found " + cities.size() + " cities in " + countryCode); } else { System.out.println("Country not found or no cities available"); } } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' def get_cities_by_country(country_code) uri = URI("https://api.countrystatecity.in/v1/countries/#{country_code}/cities") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) if response.code == '200' cities = JSON.parse(response.body) puts "Found #{cities.length} cities in #{country_code}" cities else puts 'Country not found or no cities available' [] end end cities = get_cities_by_country('IN') ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} [ { "id": 133024, "name": "Mumbai", "kind": "settlement", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.07283000", "longitude": "72.88261000", "timezone": "Asia/Kolkata", "population": 12442373, "type": "adm1", "level": null, "parent_id": null, "native": "मुंबई", "translations": "{\"de\":\"Mumbai\",\"fr\":\"Bombay\",\"ja\":\"ムンバイ\",\"cn\":\"孟买\"}", "wikiDataId": "Q1156" } ] ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Implement type-ahead city search for a specific country. ```javascript theme={null} class CountryCityAutocomplete { constructor(countryCode) { this.countryCode = countryCode; this.cities = []; this.loadCities(); } async loadCities() { this.cities = await getCitiesByCountry(this.countryCode); } search(query) { const lowerQuery = query.toLowerCase(); return this.cities.filter(city => city.name.toLowerCase().includes(lowerQuery) ).slice(0, 10); } setupAutocomplete(inputElement) { inputElement.addEventListener('input', (e) => { const results = this.search(e.target.value); // Display results in dropdown this.displaySuggestions(results); }); } } ``` Group cities for shipping or service area calculations. ```javascript theme={null} const setupDeliveryZones = async (countryCode) => { const cities = await getCitiesByCountry(countryCode); // Group cities alphabetically for easier management const zones = cities.reduce((acc, city) => { const firstLetter = city.name.charAt(0).toUpperCase(); acc[firstLetter] = acc[firstLetter] || []; acc[firstLetter].push(city); return acc; }, {}); return zones; }; ``` **Large Datasets**: Countries like the United States, India, and China have thousands of cities. Consider implementing pagination or using the state-filtered endpoint for better performance. For better user experience, consider loading cities by state instead of by country for countries with many administrative divisions. ## Tier-Based Field Availability This endpoint requires **Starter+** (Legacy plans also include it), so Community accounts never reach field access at all — they get a `403` first. | Tier | Plans | Fields | | - | - | - | | **Basic** | Starter, Legacy | `id`, `name`, `kind` | | **Full** | Supporter, Professional, Business | All Basic **+** `state_id`, `state_code`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone`, `population`, `type`, `level`, `parent_id`, `native`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Cities by State Source: https://docs.countrystatecity.in/api/endpoints/get-cities-by-state GET /countries/{ciso}/states/{siso}/cities Retrieve all cities within a specific state/province of a country Retrieve all cities within a specific state or province using both the country's ISO2 code and the state's ISO2 code. This provides the most targeted city data. **Availability:** All plans (Community and above). The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. City responses on the **Basic** tier return `id`, `name`, and `kind`. Upgrading to **Supporter+** unlocks the full field set: `state_id`, `state_code`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone`, `population`, `type`, `level`, `parent_id`, `native`, `translations`, and `wikiDataId`. See **Tier-Based Field Availability** below. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH/cities' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH/cities?q=pune' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (filter by kind) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH/cities?kind=settlement' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH/cities?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_cities_by_state(country_code, state_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/states/{state_code}/cities', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: cities = response.json() print(f'Found {len(cities)} cities in {state_code}, {country_code}') return cities else: print('State not found or no cities available') return [] cities = get_cities_by_state('IN', 'MH') ``` ```python Python (filter by kind) theme={null} import requests def get_settlements_by_state(country_code, state_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/states/{state_code}/cities', params={'kind': 'settlement'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: cities = response.json() print(f'Found {len(cities)} settlements in {state_code}, {country_code}') return cities else: print('State not found or no cities available') return [] cities = get_settlements_by_state('IN', 'MH') ``` ```javascript JavaScript theme={null} const getCitiesByState = async (countryCode, stateCode) => { const response = await fetch( `https://api.countrystatecity.in/v1/countries/${countryCode}/states/${stateCode}/cities`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); if (response.ok) { const cities = await response.json(); console.log(`Found ${cities.length} cities in ${stateCode}, ${countryCode}`); return cities; } else { console.error('State not found or no cities available'); return []; } }; getCitiesByState('IN', 'MH'); ``` ```javascript JavaScript (filter by kind) theme={null} const getSettlementsByState = async (countryCode, stateCode) => { const response = await fetch( `https://api.countrystatecity.in/v1/countries/${countryCode}/states/${stateCode}/cities?kind=settlement`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); if (response.ok) { const cities = await response.json(); console.log(`Found ${cities.length} settlements in ${stateCode}, ${countryCode}`); return cities; } else { console.error('State not found or no cities available'); return []; } }; getSettlementsByState('IN', 'MH'); ``` ```php PHP theme={null} "https://api.countrystatecity.in/v1/countries/{$countryCode}/states/{$stateCode}/cities", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode == 200) { $cities = json_decode($response, true); echo "Found " . count($cities) . " cities in {$stateCode}, {$countryCode}\n"; return $cities; } else { echo "State not found or no cities available\n"; return []; } } $cities = getCitiesByState('IN', 'MH'); ?> ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) func getCitiesByState(countryCode, stateCode string) []map[string]interface{} { url := fmt.Sprintf("https://api.countrystatecity.in/v1/countries/%s/states/%s/cities", countryCode, stateCode) client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() if res.StatusCode == 200 { body, _ := ioutil.ReadAll(res.Body) var cities []map[string]interface{} json.Unmarshal(body, &cities) fmt.Printf("Found %d cities in %s, %s\n", len(cities), stateCode, countryCode) return cities } else { fmt.Printf("State not found or no cities available\n") return nil } } func main() { cities := getCitiesByState("IN", "MH") fmt.Println(cities) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class CitiesByState { public static void main(String[] args) throws Exception { String countryCode = "IN"; String stateCode = "MH"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries/" + countryCode + "/states/" + stateCode + "/cities")) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { Gson gson = new Gson(); Type listType = new TypeToken>>(){}.getType(); List> cities = gson.fromJson(response.body(), listType); System.out.println("Found " + cities.size() + " cities in " + stateCode + ", " + countryCode); } else { System.out.println("State not found or no cities available"); } } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' def get_cities_by_state(country_code, state_code) uri = URI("https://api.countrystatecity.in/v1/countries/#{country_code}/states/#{state_code}/cities") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) if response.code == '200' cities = JSON.parse(response.body) puts "Found #{cities.length} cities in #{state_code}, #{country_code}" cities else puts 'State not found or no cities available' [] end end cities = get_cities_by_state('IN', 'MH') ``` ```json 200 - Basic tier (Community, Starter, Legacy) theme={null} [ { "id": 133024, "name": "Mumbai", "kind": "settlement" }, { "id": 133504, "name": "Pune", "kind": "settlement" }, { "id": 133177, "name": "Nashik", "kind": "settlement" } ] ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} [ { "id": 133024, "name": "Mumbai", "kind": "settlement", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.07283000", "longitude": "72.88261000", "timezone": "Asia/Kolkata", "population": 12442373, "type": "adm1", "level": null, "parent_id": null, "native": "मुंबई", "translations": "{\"de\":\"Mumbai\",\"fr\":\"Bombay\",\"ja\":\"ムンバイ\",\"cn\":\"孟买\"}", "wikiDataId": "Q1156" } ] ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Build three-level dropdowns for Country → State → City selection. ```javascript theme={null} class LocationSelector { constructor() { this.setupEventListeners(); } setupEventListeners() { const countrySelect = document.getElementById('country'); const stateSelect = document.getElementById('state'); const citySelect = document.getElementById('city'); countrySelect.addEventListener('change', async (e) => { const countryCode = e.target.value; if (countryCode) { await this.populateStates(countryCode); this.clearCities(); } }); stateSelect.addEventListener('change', async (e) => { const stateCode = e.target.value; const countryCode = countrySelect.value; if (stateCode && countryCode) { await this.populateCities(countryCode, stateCode); } }); } async populateCities(countryCode, stateCode) { const cities = await getCitiesByState(countryCode, stateCode); const citySelect = document.getElementById('city'); citySelect.innerHTML = ''; cities.forEach(city => { const option = document.createElement('option'); option.value = city.id; option.textContent = city.name; citySelect.appendChild(option); }); } } ``` Define service coverage areas by state and city combinations. ```javascript theme={null} const createServiceArea = async (serviceAreas) => { const coverage = {}; for (const area of serviceAreas) { const { countryCode, stateCode, serviceName, deliveryTime } = area; const cities = await getCitiesByState(countryCode, stateCode); coverage[`${countryCode}-${stateCode}`] = { serviceName, deliveryTime, cities: cities.length, cityIds: cities.map(city => city.id) }; } return coverage; }; // Usage const areas = [ { countryCode: 'IN', stateCode: 'MH', serviceName: 'Express', deliveryTime: '1-2 days' }, { countryCode: 'US', stateCode: 'CA', serviceName: 'Standard', deliveryTime: '2-3 days' } ]; const serviceMap = await createServiceArea(areas); ``` This is the most efficient endpoint for building cascading location selectors as it provides the smallest, most relevant dataset. This endpoint provides the optimal balance between data size and specificity, making it ideal for form controls and location-based filtering. ## Tier-Based Field Availability | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `name`, `kind` | | **Full** | Supporter, Professional, Business | All Basic **+** `state_id`, `state_code`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone`, `population`, `type`, `level`, `parent_id`, `native`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Countries by Subregion Source: https://docs.countrystatecity.in/api/endpoints/get-countries-by-subregion GET /subregions/{id}/countries Retrieve countries belonging to a specific subregion Retrieve all countries within a subregion. For example, subregion "Southern Asia" contains India, Pakistan, Bangladesh, etc. **Availability:** Starter plan and above. Returns `403` on Community and Legacy plans. The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. ## Response Returns the same country object as [Get All Countries](/api/endpoints/get-all-countries), filtered to the subregion. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/subregions/14/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/subregions/14/countries?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/subregions/14/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const countries = await response.json(); console.log(countries); ``` ```json 200 - Basic tier (Starter) — abridged theme={null} [ { "id": 101, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "native": "भारत", "emoji": "🇮🇳", "latitude": "20.00000000", "longitude": "77.00000000", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]" } ] ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) — abridged theme={null} [ { "id": 101, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "currency_name": "Indian rupee", "currency_symbol": "₹", "tld": ".in", "nationality": "Indian", "native": "भारत", "emoji": "🇮🇳", "emojiU": "U+1F1EE U+1F1F3", "latitude": "20.00000000", "longitude": "77.00000000", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]", "population": 1380004385, "gdp": 2870504000000, "area_sq_km": 3287263, "postal_code_format": "######", "postal_code_regex": "^(\\d{6})$", "numeric_code": "356", "translations": "{\"kr\":\"인도\",\"de\":\"Indien\",\"es\":\"India\",\"fr\":\"Inde\",\"ja\":\"インド\"}", "wikiDataId": "Q668" } ] ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "regionsApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Tier-Based Field Availability This endpoint requires **Starter+**, so Community and Legacy accounts never reach field access at all — they get a `403` first. Returned country fields match [Get Country Details](/api/endpoints/get-country-details): | Tier | Plans | Fields | | - | - | - | | **Basic** | Starter | `id`, `name`, `iso2`, `iso3`, `phonecode`, `capital`, `currency`, `native`, `emoji`, `latitude`, `longitude`, `region`, `region_id`, `subregion`, `subregion_id`, `timezones` | | **Full** | Supporter, Professional, Business | All Basic **+** `numeric_code`, `currency_name`, `currency_symbol`, `tld`, `nationality`, `population`, `gdp`, `area_sq_km`, `postal_code_format`, `postal_code_regex`, `emojiU`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Country Details Source: https://docs.countrystatecity.in/api/endpoints/get-country-details GET /countries/{ciso} Retrieve detailed information for a specific country using its ISO2 code Retrieve detailed information for a specific country using its ISO2 code, including extended geographical, currency, and timezone data. **Availability:** All plans (Community and above). The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim response columns:** Add `?fields=name,iso2,...` to receive only the columns you need. Available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_country_details(country_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: return response.json() else: print('Country not found') return None country = get_country_details('IN') print(country) ``` ```javascript JavaScript theme={null} const getCountryDetails = async (countryCode) => { const response = await fetch(`https://api.countrystatecity.in/v1/countries/${countryCode}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.ok) { const country = await response.json(); console.log(country); return country; } else { console.error('Country not found'); } }; getCountryDetails('IN'); ``` ```php PHP theme={null} "https://api.countrystatecity.in/v1/countries/{$countryCode}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode == 200) { $country = json_decode($response, true); echo json_encode($country, JSON_PRETTY_PRINT); } else { echo "Country not found"; } ?> ``` ```go Go theme={null} package main import ( "fmt" "io/ioutil" "net/http" ) func getCountryDetails(countryCode string) { url := fmt.Sprintf("https://api.countrystatecity.in/v1/countries/%s", countryCode) client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() if res.StatusCode == 200 { body, _ := ioutil.ReadAll(res.Body) fmt.Println(string(body)) } else { fmt.Println("Country not found") } } func main() { getCountryDetails("IN") } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CountryDetails { public static void main(String[] args) throws Exception { String countryCode = "IN"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries/" + countryCode)) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { System.out.println(response.body()); } else { System.out.println("Country not found"); } } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' def get_country_details(country_code) uri = URI("https://api.countrystatecity.in/v1/countries/#{country_code}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) if response.code == '200' country = JSON.parse(response.body) puts JSON.pretty_generate(country) else puts 'Country not found' end end get_country_details('IN') ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} { "id": 101, "name": "India", "iso3": "IND", "numeric_code": "356", "iso2": "IN", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "currency_name": "Indian rupee", "currency_symbol": "₹", "tld": ".in", "native": "भारत", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "nationality": "Indian", "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]", "translations": "{\"kr\":\"인도\",\"pt-BR\":\"Índia\",\"pt\":\"Índia\",\"nl\":\"India\",\"hr\":\"Indija\",\"fa\":\"هند\",\"de\":\"Indien\",\"es\":\"India\",\"fr\":\"Inde\",\"ja\":\"インド\",\"it\":\"India\",\"cn\":\"印度\",\"tr\":\"Hindistan\"}", "latitude": "20.00000000", "longitude": "77.00000000", "emoji": "🇮🇳", "emojiU": "U+1F1EE U+1F1F3", "population": 1380004385, "gdp": 2870504000000, "area_sq_km": 3287263, "postal_code_format": "######", "postal_code_regex": "^(\\d{6})$", "wikiDataId": "Q668" } ``` ```json 200 - Basic tier (Community, Starter, Legacy) theme={null} { "id": 101, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "91", "capital": "New Delhi", "currency": "INR", "native": "भारत", "emoji": "🇮🇳", "latitude": "20.00000000", "longitude": "77.00000000", "region": "Asia", "region_id": 3, "subregion": "Southern Asia", "subregion_id": 14, "timezones": "[{\"zoneName\":\"Asia/Kolkata\",\"gmtOffset\":19800,\"gmtOffsetName\":\"UTC+05:30\",\"abbreviation\":\"IST\",\"tzName\":\"Indian Standard Time\"}]" } ``` ```json 404 - Not Found theme={null} { "error": "Country not found." } ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Use native names and translations for multi-language applications. ```javascript theme={null} const getLocalizedCountryName = (country, language = 'en') => { if (language === 'native') return country.native; const translations = JSON.parse(country.translations || '{}'); return translations[language] || country.name; }; ``` Parse and use timezone data for scheduling applications. ```javascript theme={null} const getTimezoneInfo = (country) => { const timezones = JSON.parse(country.timezones || '[]'); return timezones.map(tz => ({ name: tz.zoneName, offset: tz.gmtOffsetName, abbreviation: tz.abbreviation })); }; ``` Use this endpoint when you need detailed country information for user profiles, shipping calculations, or localization features. ## Tier-Based Field Availability | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `name`, `iso2`, `iso3`, `phonecode`, `capital`, `currency`, `native`, `emoji`, `latitude`, `longitude`, `region`, `region_id`, `subregion`, `subregion_id`, `timezones` | | **Full** | Supporter, Professional, Business | All Basic **+** `numeric_code`, `currency_name`, `currency_symbol`, `tld`, `nationality`, `population`, `gdp`, `area_sq_km`, `postal_code_format`, `postal_code_regex`, `emojiU`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Currency by Country Source: https://docs.countrystatecity.in/api/endpoints/get-currency-by-country GET /currency/{ciso} Retrieve the official currency for a single country by ISO2, ISO3, or numeric ID Retrieve the currency used by a single country. The path parameter accepts **three forms** for flexibility: * ISO 3166-1 alpha-2 code (e.g., `US`, `IN`) * ISO 3166-1 alpha-3 code (e.g., `USA`, `IND`) * Numeric country ID (e.g., `233` for the United States in our database) **Availability:** Starter plan and above. Returns `403` on lower tiers. Responses are cached server-side for 24 hours. Equivalent identifier forms (e.g., `1` and `001`) resolve to the same cache slot. ```bash cURL (ISO2) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/currency/US' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (ISO3) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/currency/IND' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/currency/US', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const { currency } = await response.json(); console.log(`${currency.symbol} ${currency.code}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/currency/US', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) data = response.json() print(f"{data['country']} uses {data['currency']['code']}") ``` ```json 200 - Success theme={null} { "country": "US", "currency": { "code": "USD", "name": "United States dollar", "symbol": "$" } } ``` ```json 200 - Edge case (symbol null) theme={null} { "country": "CU", "currency": { "code": "CUP", "name": "Cuban peso", "symbol": null } } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "currencyApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - Country Not Found theme={null} { "error": "Country not found." } ``` ```json 404 - No Currency Data theme={null} { "error": "No currency data available for this country." } ``` ## Related Endpoints * [List All Currencies](/api/endpoints/list-all-currencies) — every country's currency in one call, with optional reverse lookup * [Get Country Details](/api/endpoints/get-country-details) — full country record including currency fields inline # Data Change Feed Source: https://docs.countrystatecity.in/api/endpoints/get-data-changes GET /changes Keep your copy of Country State City data current without downloading the full dataset again If your app stores Country State City data in its own database, a full download is wasteful when only a few places changed. The change feed returns the countries, states, and cities that were added, removed, or updated so you can apply only those changes. **Available on Professional and Business plans.** Returns `403` on Community, Starter, Supporter, and Legacy plans. [Compare plans](https://countrystatecity.in/pricing?source=docs\&campaign=data_change_feed) to add the change feed to your API key. ## When to use it Use the feed to keep a local search index, checkout form, analytics database, or cached copy up to date. Every change has a stable `change_id`, the affected `place_id`, and the `data_version` that produced it. Changes are available for **90 days**. Run your sync regularly and save the `change_id` values you have applied so retrying a page cannot create duplicates. ## Change types | Value | Meaning | | - | - | | `added` | A new place was added. | | `removed` | A place was removed. | | `renamed` | Its name changed. | | `place_group_changed` | Its region, administrative type, or city kind changed. | | `parent_changed` | A state moved to another country, or a city moved to another state/country. | | `coordinates_changed` | Its latitude or longitude changed. | | `other_fields_changed` | Another public field changed. | When several fields change in one release, the record has one main `change_type`, but `old_values` and `new_values` include every changed field. ## Response The API returns changes from oldest to newest. ```json 200 - Renamed city theme={null} { "results": [ { "change_id": "6b30c790-c46f-4a0f-b7f2-ea1a1a9d8362", "data_version": "v3.2-export.7-2026.08.24", "changed_at": "2026-08-24T08:15:00.000Z", "place_type": "city", "place_id": "132649", "change_type": "renamed", "old_values": { "name": "Bombay" }, "new_values": { "name": "Mumbai" } } ], "next_page_token": "H2gwOstvc3hy5O7cW1bK0T5S1m4f8h2wYQ" } ``` `old_values` and `new_values` contain only fields allowed for your API key. The endpoint is not cached, so it does not return `ETag` or `X-Cache`. ## Read every page safely The first request fixes the result set. Later pages do not suddenly include a release published while you are syncing. After the last page, start a new request to collect newer changes. For page two and later, send the token by itself. Do not change the original filters. ```javascript JavaScript theme={null} let url = new URL('https://api.countrystatecity.in/v1/changes'); url.searchParams.set('start_date', '2026-08-01T00:00:00Z'); url.searchParams.set('country_code', 'IN'); url.searchParams.set('limit', '100'); while (url) { const response = await fetch(url, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (!response.ok) throw new Error(await response.text()); const page = await response.json(); for (const change of page.results) { console.log(change); // Apply it to your database and save change.change_id. } url = page.next_page_token ? new URL(`https://api.countrystatecity.in/v1/changes?next_page_token=${encodeURIComponent(page.next_page_token)}`) : null; } ``` ```bash cURL theme={null} curl 'https://api.countrystatecity.in/v1/changes?start_date=2026-08-01T00:00:00Z&country_code=IN&limit=100' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/changes', params={'start_date': '2026-08-01T00:00:00Z', 'country_code': 'IN'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'}, ) response.raise_for_status() page = response.json() ``` ## Recover from errors If `start_date` is older than the retained data, use `details.earliestAvailableDate` to restart from the oldest available change: ```json 400 - Start date is too old theme={null} { "status": "error", "message": "start_date is older than the earliest available change. Changes are kept for 90 days.", "details": { "earliestAvailableDate": "2026-05-26T00:00:00.000Z" } } ``` If a token expires, is changed, or is invalid, start again with `start_date` and no token. Reusing saved `change_id` values prevents duplicate work. If your sync has been offline for more than 90 days, download a fresh full dataset before continuing. Lower plans receive a plan-gate response: ```json 403 - Professional plan required theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "dataChangeFeed", "currentTier": "supporter", "requiredTier": "professional", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Start syncing changes Choose Professional or Business to keep your database current with small incremental requests. Check which dataset release your API responses use. # Get Data Version Source: https://docs.countrystatecity.in/api/endpoints/get-data-version GET /meta/data-version Check which geographic data release your API responses are currently served from Returns the version of the underlying geographic dataset currently live behind the API — which upstream release it was exported from, when that release was imported, and a record-count snapshot taken at import time. **Availability:** All plans (Community and above) — no tier restriction. The request still counts toward your daily/monthly usage quota like any other endpoint. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/meta/data-version' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_data_version(): """Fetch the geographic data release currently live behind the API.""" response = requests.get( 'https://api.countrystatecity.in/v1/meta/data-version', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: return response.json() else: print('Data version temporarily unavailable') return None version = get_data_version() ``` ```javascript JavaScript theme={null} const getDataVersion = async () => { const response = await fetch('https://api.countrystatecity.in/v1/meta/data-version', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.ok) { return await response.json(); } else { console.error('Data version temporarily unavailable'); return null; } }; getDataVersion(); ``` ```json 200 - Success theme={null} { "dataVersion": "v3.2-export.7-2026.07.30", "sourceRelease": "v3.2-export.7", "updatedAt": "2026-07-30T09:01:18.000Z", "recordCounts": { "regions": 6, "subregions": 22, "countries": 250, "states": 5308, "cities": 152970 } } ``` ```json 503 - Temporarily unavailable theme={null} { "status": "error", "message": "Data version information is temporarily unavailable." } ``` ## Response headers on every request Two of the fields above are also stamped as headers on **every** `/v1/*` response, not just this endpoint — so you can track the live data version without an extra request: | Header | Meaning | | - | - | | `X-CSC-Data-Version` | Same value as `dataVersion` above | | `X-CSC-Data-Updated-At` | Same value as `updatedAt` above | | `X-Request-ID` | Server-generated UUID, unique per request — include it when contacting support about a specific request | **This metadata is cached, so it can trail a fresh import.** This route reads through a 5-minute-TTL cache backed by Redis, so right after an import it can report the previous release for up to about 5 minutes while the new data is already being served. The headers are stamped from an in-memory copy that refreshes every 30 seconds on top of that same cache, so they can trail the imported data by about 5.5 minutes — a deliberate tradeoff so stamping them never adds latency or a new failure mode to every request. Both catch up on their own; no action is needed on your side. `X-Request-ID` is always present. The two `X-CSC-Data-*` headers are omitted until the in-memory copy has warmed after a server restart. All three appear on error responses too, because they're set before routing rather than per endpoint — see [Errors & Rate Limits](/api/errors). ## npm package parity The [`@countrystatecity/countries`](https://www.npmjs.com/package/@countrystatecity/countries) and [`@countrystatecity/countries-browser`](https://www.npmjs.com/package/@countrystatecity/countries-browser) npm packages ship a `getDataVersion()` loader with the same `dataVersion`/`sourceRelease`/`updatedAt` fields, read from a small bundled `version.json` instead of a network call. See [npm Packages](/api/sdks/npm#data-updates) for usage. **Compare `sourceRelease`, not `dataVersion`.** Both sides build `dataVersion` from the same release tag, but they append different dates: the npm package uses the date the release was **published**, while the API uses the date that release was **imported**. An import lands after the release is published, so the two strings usually differ by a day or more even when both sides hold exactly the same data. `sourceRelease` is the same string on both sides, which makes it the reliable parity key. For example, the same `v3.2-export.7` release can read as `v3.2-export.7-2026.07.29` in the package and `v3.2-export.7-2026.07.30` from the API. The package's `recordCounts` is also narrower than the API's: `countries`/`states`/`cities` only, no `regions`/`subregions`. ## Related The `status`/`message` envelope used by the 503 response above, plus usage limits by tier. Get the same version info without a network call via `getDataVersion()`. # Get Dial Code by Country Source: https://docs.countrystatecity.in/api/endpoints/get-dial-code-by-country GET /phone/{ciso} Retrieve the international dial code for a single country by ISO2, ISO3, or numeric ID Get the dial code for one country. The path parameter accepts **three forms** — the same identifier flexibility as the rest of the geographic API: * ISO 3166-1 alpha-2 (e.g. `US`, `IN`) * ISO 3166-1 alpha-3 (e.g. `USA`, `IND`) * Numeric CSC country ID (e.g. `233`) **Availability:** Starter plan and above. Returns `403` on lower tiers. Responses are cached server-side for 24 hours. Numeric IDs `1` and `001` resolve to the same cache slot; letter codes are case-insensitive. ## Response A single phone entry (object, **not** an array — unlike the list endpoint). ```bash cURL (ISO2) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/US' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (ISO3) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/IND' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (numeric ID) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/233' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/phone/US', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const { dial_code } = await response.json(); console.log(dial_code); // "+1" ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/phone/IN', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) print(response.json()['dial_code']) # "+91" ``` ```json 200 - Standard country theme={null} { "country": "IN", "dial_code": "+91", "iso2": "IN", "iso3": "IND" } ``` ```json 200 - NANP entry (with area_code) theme={null} { "country": "BB", "dial_code": "+1", "area_code": "246", "iso2": "BB", "iso3": "BRB" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "phoneDialCode", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - Country Not Found theme={null} { "status": "error", "message": "Country not found." } ``` ```json 404 - No Dial Code on Record theme={null} { "status": "error", "message": "No dial code available for this country." } ``` ## Related Endpoints * [List Dial Codes](/api/endpoints/list-dial-codes) — every country, plus reverse lookup by dial code * [Parse Phone Number](/api/endpoints/parse-phone-number) — given an E.164 number, identify the country * [Get Country Details](/api/endpoints/get-country-details) — full country record, including `phonecode` # Get Postcode by Code Source: https://docs.countrystatecity.in/api/endpoints/get-postcode-by-code GET /countries/{ciso}/postcodes/{code} Look up every postcode record that matches an exact code within a country Look up a postcode / ZIP code within a country and get back every matching record. Use this to confirm that a user-supplied postcode appears in the dataset. **Free on every plan**, including Community. Exact lookup counts against your normal daily/monthly quota like any other request. Paginated listing and search use [Get Postcodes by Country](/api/endpoints/get-postcodes-by-country) and require a Starter+ plan — exact lookup has no such gate. This is informational postcode data, not an address verification or mail-deliverability service. A match confirms the code exists in the dataset — it does not confirm a delivery point, standardize an address, or guarantee mail will arrive. ## Why the response is an array A postcode is not always unique to one place. The same code can legitimately cover more than one locality, so this endpoint returns **every** matching record instead of picking one — an empty result is a `404`, never an empty success. Fields above **Basic** are stripped for lower plans, not just hidden — a Community or Starter key never receives `country_id`, `state_id`, `city_id`, `latitude`, or `longitude` in the response body. See **Tier-based field availability** below. `id` and every foreign-key field are returned as numeric strings rather than JSON numbers — parse them as strings in your client. ```json 200 - Duplicate exact match, Basic tier (Community, Starter, Legacy) theme={null} { "data": [ { "id": "670115", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Mabola", "type": "full" }, { "id": "670116", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Watinapaha", "type": "full" }, { "id": "670117", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Wattala", "type": "full" } ], "meta": { "country_code": "LK", "query": "11104", "match_count": 3 } } ``` ```json 200 - Duplicate exact match, Full tier (Supporter, Professional, Business) theme={null} { "data": [ { "id": "670115", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Mabola", "type": "full", "country_id": "208", "state_id": "2804", "city_id": null, "latitude": 7.0062833, "longitude": 79.8924828, "source": "geonames", "wikiDataId": null }, { "id": "670116", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Watinapaha", "type": "full", "country_id": "208", "state_id": "2804", "city_id": null, "latitude": 7.1853118, "longitude": 80.0003875, "source": "geonames", "wikiDataId": null }, { "id": "670117", "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Wattala", "type": "full", "country_id": "208", "state_id": "2804", "city_id": null, "latitude": 6.9906677, "longitude": 79.8931709, "source": "geonames", "wikiDataId": null } ], "meta": { "country_code": "LK", "query": "11104", "match_count": 3 } } ``` ## Tier-based field availability | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `code`, `country_code`, `state_code`, `locality_name`, `type` | | **Full** | Supporter, Professional, Business | All Basic **+** `country_id`, `state_id`, `city_id`, `latitude`, `longitude`, `source`, `wikiDataId` | See [Pricing](https://countrystatecity.in/pricing?source=docs\&campaign=postcode_lookup) for plan details. ## Caching and conditional requests Every response sets `ETag`, `Cache-Control`, and `X-Cache` (`HIT`/`MISS`), cached for 24 hours. Send the last `ETag` back as `If-None-Match` to get a bodyless `304` when nothing changed instead of re-downloading the result: ```javascript theme={null} const response = await fetch(url, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY', 'If-None-Match': savedEtag } }); if (response.status === 304) { // Nothing changed — reuse your cached copy. } else { savedEtag = response.headers.get('ETag'); } ``` ```bash cURL theme={null} curl 'https://api.countrystatecity.in/v1/countries/LK/postcodes/11104' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_postcode(country_code, code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/postcodes/{code}', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) response.raise_for_status() return response.json() result = get_postcode('LK', '11104') print(f"{result['meta']['match_count']} match(es) for {result['meta']['query']}") ``` ```javascript JavaScript theme={null} const getPostcode = async (countryCode, code) => { const response = await fetch( `https://api.countrystatecity.in/v1/countries/${countryCode}/postcodes/${encodeURIComponent(code)}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); if (!response.ok) throw new Error(await response.text()); return response.json(); }; const result = await getPostcode('LK', '11104'); console.log(`${result.meta.match_count} match(es) for ${result.meta.query}`); ``` ## Errors Distinguishes the three `404` cases below. Check this instead of parsing `message`. ```json 404 - Unknown country theme={null} { "status": "error", "message": "Country not found.", "details": { "reason": "country_not_found" } } ``` ```json 404 - No coverage for this country theme={null} { "status": "error", "message": "No postcode coverage is available for this country.", "details": { "reason": "postcode_coverage_unavailable" } } ``` ```json 404 - Code has no match theme={null} { "status": "error", "message": "No postcode matches the given code.", "details": { "reason": "postcode_not_found" } } ``` ```json 400 - Invalid code theme={null} { "status": "error", "message": "postcode must be at most 20 characters" } ``` `code` is also rejected as empty, or for containing a `/`, `\`, or control character. See [Errors & Rate Limits](/api/errors) for the `401`/`429` shapes and usage headers common to every endpoint. ## Related Page through or search a country's postcodes. Requires Starter+. Coverage stats, postcode types, and self-hosted query examples. # Get Postcodes by Country Source: https://docs.countrystatecity.in/api/endpoints/get-postcodes-by-country GET /countries/{ciso}/postcodes Page through or search a country's postcodes with cursor pagination Page through a country's postcodes / ZIP codes, or narrow the list with a code/locality search, state, city, or type filter. Use this to build postcode discovery instead of downloading the full dataset. **Requires a Starter+ plan** — including a bare list request with no filters at all. This is intentional: it stops a Community or Legacy key from paginating through and scraping the full hosted dataset. [Get Postcode by Code](/api/endpoints/get-postcode-by-code) (exact lookup) has no such gate and stays free on every plan. This is informational postcode data, not an address verification or mail-deliverability service. Results confirm codes exist in the dataset — they do not confirm delivery points or standardize addresses. **Trim results:** Add `?fields=` to limit the columns returned. Requires **Starter+**. There is no `?sort=` on this endpoint — results are always ordered by code, then `id`, so cursor pagination stays stable. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for `?fields=` syntax. There is no total-count field. Counting every match up front would mean a second expensive query on top of a 844k-row table, so check `has_more` and keep paging instead of expecting a total. ```json 200 - Search with q, Basic tier (Starter) theme={null} { "data": [ { "id": "131878", "code": "110001", "country_code": "IN", "state_code": "DL", "locality_name": "Sansad Marg", "type": "full" } ], "pagination": { "limit": 50, "next_cursor": null, "has_more": false } } ``` ```json 200 - Search with q, Full tier (Supporter, Professional, Business) theme={null} { "data": [ { "id": "131878", "code": "110001", "country_code": "IN", "state_code": "DL", "locality_name": "Sansad Marg", "type": "full", "country_id": "101", "state_id": "4021", "city_id": null, "latitude": null, "longitude": null, "source": "geonames", "wikiDataId": null } ], "pagination": { "limit": 50, "next_cursor": null, "has_more": false } } ``` ## Tier-based field availability Community and Legacy lack the `searchEndpoint` feature this endpoint requires, so the **Basic** row below is only reachable on **Starter** — Community and Legacy get a `403` on this endpoint before field access matters. | Tier | Plans | Fields | | - | - | - | | **Basic** | Starter | `id`, `code`, `country_code`, `state_code`, `locality_name`, `type` | | **Full** | Supporter, Professional, Business | All Basic **+** `country_id`, `state_id`, `city_id`, `latitude`, `longitude`, `source`, `wikiDataId` | See [Pricing](https://countrystatecity.in/pricing?source=docs\&campaign=postcode_search) for plan details. ## Caching and conditional requests Every response sets `ETag`, `Cache-Control` (`public, max-age=3600`), and `X-Cache` (`HIT`/`MISS`), cached for 1 hour regardless of whether `q` is present. Send the last `ETag` back as `If-None-Match` to get a bodyless `304` when nothing changed instead of re-downloading the page: ```javascript theme={null} const response = await fetch(url, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY', 'If-None-Match': savedEtag } }); if (response.status === 304) { // Nothing changed — reuse your cached copy. } else { savedEtag = response.headers.get('ETag'); } ``` ```bash cURL theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/postcodes?limit=20' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (search) theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/postcodes?q=110001&type=full' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def search_postcodes(country_code, **params): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/postcodes', params=params, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) response.raise_for_status() return response.json() page = search_postcodes('IN', q='110001', type='full') ``` ```javascript JavaScript theme={null} const searchPostcodes = async (countryCode, params = {}) => { const url = new URL(`https://api.countrystatecity.in/v1/countries/${countryCode}/postcodes`); for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value); const response = await fetch(url, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (!response.ok) throw new Error(await response.text()); return response.json(); }; const page = await searchPostcodes('IN', { q: '110001', type: 'full' }); ``` ```javascript JavaScript (page through every result) theme={null} async function* allPostcodes(countryCode, params = {}) { let cursor; do { const url = new URL(`https://api.countrystatecity.in/v1/countries/${countryCode}/postcodes`); for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value); if (cursor) url.searchParams.set('cursor', cursor); const response = await fetch(url, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (!response.ok) throw new Error(await response.text()); const page = await response.json(); yield* page.data; cursor = page.pagination.has_more ? page.pagination.next_cursor : null; } while (cursor); } for await (const postcode of allPostcodes('IN', { state_code: 'DL' })) { console.log(postcode.code, postcode.locality_name); } ``` ## Errors ```json 400 - Invalid country code theme={null} { "status": "error", "message": "countryCode must be a 2-letter ISO code" } ``` ```json 400 - Cursor filters don't match this request theme={null} { "status": "error", "message": "cursor filters do not match this request." } ``` A cursor also `400`s as `"cursor belongs to a different country."` when reused across countries, `"cursor is invalid or has been tampered with."` when it's malformed or its signature doesn't verify, and `"cursor has expired. Start again without a cursor."` after 24 hours. ```json 403 - Requires Starter+ theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "searchEndpoint", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://countrystatecity.in/pricing" } } ``` This `403` fires on **every** request to this endpoint from a Community or Legacy key — including a bare list with no `q`, `state_code`, `city_id`, or `type` at all. Only exact lookup ([Get Postcode by Code](/api/endpoints/get-postcode-by-code)) is free on those plans. ```json 200 - Country with no postcode coverage theme={null} { "data": [], "pagination": { "limit": 50, "next_cursor": null, "has_more": false } } ``` A country that exists but has no postcodes, or a filter that matches nothing, returns `200` with an empty `data` array — not a `404`. The `404` cases (`country_not_found`) match [Get Postcode by Code](/api/endpoints/get-postcode-by-code#errors) for an unknown country. See [Errors & Rate Limits](/api/errors) for the `401`/`429` shapes and usage headers common to every endpoint. This endpoint requires a Starter+ plan, for listing as well as searching. [Compare plans](https://countrystatecity.in/pricing?source=docs\&campaign=postcode_search) to add it to your API key. ## Related Exact lookup, free on every plan — start here before adding search. Coverage stats, postcode types, and self-hosted query examples. # Get Region Details Source: https://docs.countrystatecity.in/api/endpoints/get-region-details GET /regions/{id} Retrieve a single geographic region by its numeric ID Retrieve detailed information for a single geographic region (e.g., Africa, Americas, Asia, Europe, Oceania, Polar) by its numeric ID. **Availability:** Starter plan and above. Returns `403` on lower tiers. **Trim response columns:** Add `?fields=id,name` to receive only the columns you need. Available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/regions/3' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/regions/3?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/regions/3', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const region = await response.json(); console.log(region); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/regions/3', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) region = response.json() print(region) ``` ```json 200 - Basic tier (Starter) theme={null} { "id": 3, "name": "Asia" } ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} { "id": 3, "name": "Asia", "wikiDataId": "Q48", "translations": "{\"kr\":\"아시아\",\"pt\":\"Ásia\",\"nl\":\"Azië\",\"hr\":\"Azija\",\"fa\":\"آسیا\",\"de\":\"Asien\",\"es\":\"Asia\",\"fr\":\"Asie\",\"ja\":\"アジア\",\"it\":\"Asia\",\"cn\":\"亚洲\",\"tr\":\"Asya\"}" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "regionsApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - Not Found theme={null} { "error": "Region not found." } ``` ## Related Endpoints * [Get All Regions](/api/endpoints/get-all-regions) — list every region * [Get Subregions of a Region](/api/endpoints/get-subregions-by-region) — drill down into subregions # Autocomplete Source: https://docs.countrystatecity.in/api/endpoints/get-search-autocomplete GET /search/autocomplete Type-ahead search for cities, states, and countries with deterministic ranking and ready-to-display labels Search cities, states, and countries for a type-ahead / autocomplete UI. Unlike [Fuzzy Search](/api/endpoints/fuzzy-search) — which ranks purely by trigram similarity — autocomplete uses a stricter, deterministic order built for a dropdown: **exact matches first, then starts-with matches, then the closest remaining fuzzy matches**, with population and then record ID breaking any ties. The same query always returns the same order. Each result also carries a ready-to-display `label` (e.g. `"Mumbai, Maharashtra, India"`) so you don't have to assemble one yourself. **Availability:** Supporter, Professional, and Business plans. Returns `403` on Community, Starter, and Legacy plans. [Compare plans](https://countrystatecity.in/pricing?source=playground\&campaign=autocomplete_api), or see **Trying it without a paid plan** below. Matching checks the English name, native name, and stored translations. Translation matching starts at 3 characters. Responses are cached separately by query, filters, plan, locale, and translation output. ## Response Returns an array. Each item carries the standard fields for the entity (`city`, `state`, or `country`) at your plan's data-access level, plus: City results always include `country_code` and `state_code`, regardless of your plan's data-access level — even the basic-tier field set includes them here, since you can't build a useful `label` or scope a follow-up request without them. (States already include `country_code` at every tier under the normal field rules, so no such exception is needed there.) ## Ranking Results are ordered deterministically — the same query always returns the same order: | Priority | Rule | | - | - | | 1 | Exact match (case-insensitive) on `name` or `native` | | 2 | Starts-with match on `name` or `native` | | 3 | Closest remaining fuzzy match, by `match_score` | | 4 | Larger `population` breaks ties within the same rank | | 5 | Lower `id` breaks any remaining tie | ```bash cURL (typo) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/autocomplete?q=Mumbay&type=city&country=IN' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (state, scoped to a country) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/autocomplete?q=Maharashtra&type=state&country=IN' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (city, scoped to a state) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/autocomplete?q=Pune&type=city&country=IN&state=MH&limit=5' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const params = new URLSearchParams({ q: 'Mumbay', type: 'city', country: 'IN' }); const response = await fetch( `https://api.countrystatecity.in/v1/search/autocomplete?${params}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const results = await response.json(); results.forEach((r) => console.log(`${r.label} (${r.match_score})`)); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/search/autocomplete', params={'q': 'Mumbay', 'type': 'city', 'country': 'IN'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) for result in response.json(): print(f"{result['label']} -> {result['match_score']}") ``` ```json 200 - City typo match (Supporter, Professional, Business) theme={null} [ { "id": 132332, "name": "Mumbai", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.07283000", "longitude": "72.88261000", "timezone": "Asia/Kolkata", "native": "मुंबई", "type": "city", "label": "Mumbai, Maharashtra, India", "match_score": 0.75, "matched_field": "name" } ] ``` ```json 200 - State match theme={null} [ { "id": 4008, "name": "Maharashtra", "iso2": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata", "type": "state", "label": "Maharashtra, India", "match_score": 1, "matched_field": "name" } ] ``` ```json 400 - Query too short theme={null} { "status": "error", "message": "Invalid query parameters: q: Search query must be at least 2 characters" } ``` ```json 400 - state without country theme={null} { "status": "error", "message": "Invalid query parameters: state: country is required when filtering by state" } ``` ```json 400 - state with a non-city type theme={null} { "status": "error", "message": "Invalid query parameters: state: state is only a valid filter when type=city" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "autocomplete", "currentTier": "starter", "requiredTier": "supporter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Trying it without a paid plan A separate **Playground** plan powers the interactive API documentation — a basic-tier field set on a shared rate limit sized for many concurrent visitors, not a per-visitor quota. It isn't something you can sign up for directly; it is only for the docs playground. For your own integration, [choose a Supporter plan or higher](https://countrystatecity.in/pricing?source=playground\&campaign=autocomplete_api). ## Related Endpoints * [Fuzzy Search](/api/endpoints/fuzzy-search) — pure similarity ranking, without the tiered exact/starts-with/fuzzy order or a computed `label` * [Get All Countries](/api/endpoints/get-all-countries) — exact/substring inline search via `?q=` * [Get Cities by Country](/api/endpoints/get-cities-by-country) — list cities, filterable by `?q=` # Nearby Search Source: https://docs.countrystatecity.in/api/endpoints/get-search-nearby GET /search/nearby Find countries, states, and cities near a coordinate, ranked by straight-line distance Find countries, states, or cities near a `lat`/`lng` coordinate. Results are ordered from nearest to farthest. **This is not address search.** It does not: * Find a street address * Check whether a point is inside a country or state border * Return country/state map shapes * Calculate travel time or road distance It measures straight-line distance to the stored coordinate for a city, state, or country. **Availability:** Supporter, Professional, and Business plans. Other plans receive `403`. [Compare plans](https://countrystatecity.in/pricing?source=playground\&campaign=nearby_search), or see **Trying it without a paid plan** below. ## Response Returns an array, nearest-first. Each item carries the standard fields for the entity at your plan's data-access level, plus: State and city results always include `country_name` (and, for cities, `state_name`), regardless of your plan's data-access level — useful for display without a second lookup. This differs from [Autocomplete](/api/endpoints/get-search-autocomplete), which instead always includes `country_code`/`state_code` on city results; nearby search doesn't force those two. ```bash cURL (cities near Mumbai) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/nearby?lat=19.0760&lng=72.8777&radius=25&kind=settlement' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (states within 300km, minimum population) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/nearby?lat=19.0760&lng=72.8777&type=state&radius=300&min_population=1000000' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (cities within a specific state) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/search/nearby?lat=19.0760&lng=72.8777&country=IN&state=MH&limit=5' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const params = new URLSearchParams({ lat: '19.0760', lng: '72.8777', radius: '25' }); const response = await fetch( `https://api.countrystatecity.in/v1/search/nearby?${params}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const results = await response.json(); results.forEach((r) => console.log(`${r.name} — ${r.distance_km} km`)); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/search/nearby', params={'lat': 19.0760, 'lng': 72.8777, 'radius': 25}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) for result in response.json(): print(f"{result['name']} — {result['distance_km']} km") ``` ```json 200 - Cities near Mumbai (Supporter, Professional, Business) theme={null} [ { "id": 57606, "name": "Mumbai", "kind": "settlement", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.07600000", "longitude": "72.87770000", "population": 12442373, "timezone": "Asia/Kolkata", "wikiDataId": "Q1156", "country_name": "India", "state_name": "Maharashtra", "distance_km": 0 }, { "id": 57612, "name": "Thane", "kind": "settlement", "state_id": 4008, "state_code": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.21830000", "longitude": "72.97810000", "population": 1841488, "timezone": "Asia/Kolkata", "wikiDataId": "Q210421", "country_name": "India", "state_name": "Maharashtra", "distance_km": 17.36 } ] ``` ```json 400 - Latitude out of range theme={null} { "status": "error", "message": "Invalid query parameters: lat: lat must be between -90 and 90" } ``` ```json 400 - state without country theme={null} { "status": "error", "message": "Invalid query parameters: state: country is required when filtering by state" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "nearbySearch", "currentTier": "starter", "requiredTier": "supporter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Trying it without a paid plan The interactive API documentation uses a shared **Playground** plan. It is only for trying the endpoint in the docs. For your own app, [choose a Supporter plan or higher](https://countrystatecity.in/pricing?source=playground\&campaign=nearby_search). ## Related Endpoints * [Autocomplete](/api/endpoints/get-search-autocomplete) — type-ahead search by name, not location * [Fuzzy Search](/api/endpoints/fuzzy-search) — typo-tolerant search by name * [Get Cities by Country](/api/endpoints/get-cities-by-country) — list cities, filterable by `?q=` The [CLI](/api/sdks/cli) also provides a `csc nearby` command for this endpoint. # Get State Details Source: https://docs.countrystatecity.in/api/endpoints/get-state-details GET /countries/{ciso}/states/{siso} Retrieve detailed information for a specific state using country and state ISO2 codes Retrieve detailed information for a specific state or province using the country's ISO2 code and the state's ISO2 code. **Availability:** All plans (Community and above). The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim response columns:** Add `?fields=name,iso2,...` to receive only the columns you need. Available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax. Additional fields like `fips_code`, `iso3166_2`, `level`, `parent_id`, `native`, `population`, `translations`, and `wikiDataId` are returned on higher tiers. See **Tier-Based Field Availability** below. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states/MH?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_state_details(country_code, state_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/states/{state_code}', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: state = response.json() print(f"{state['name']} is located at {state['latitude']}, {state['longitude']}") return state else: print('State not found') return None state = get_state_details('IN', 'MH') ``` ```javascript JavaScript theme={null} const getStateDetails = async (countryCode, stateCode) => { const response = await fetch( `https://api.countrystatecity.in/v1/countries/${countryCode}/states/${stateCode}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); if (response.ok) { const state = await response.json(); console.log(`${state.name} is located at ${state.latitude}, ${state.longitude}`); return state; } else { console.error('State not found'); } }; getStateDetails('IN', 'MH'); ``` ```php PHP theme={null} "https://api.countrystatecity.in/v1/countries/{$countryCode}/states/{$stateCode}", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode == 200) { $state = json_decode($response, true); echo $state['name'] . " is located at " . $state['latitude'] . ", " . $state['longitude'] . "\n"; return $state; } else { echo "State not found\n"; return null; } } $state = getStateDetails('IN', 'MH'); ?> ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) func getStateDetails(countryCode, stateCode string) { url := fmt.Sprintf("https://api.countrystatecity.in/v1/countries/%s/states/%s", countryCode, stateCode) client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() if res.StatusCode == 200 { body, _ := ioutil.ReadAll(res.Body) var state map[string]interface{} json.Unmarshal(body, &state) fmt.Printf("%s is located at %s, %s\n", state["name"], state["latitude"], state["longitude"]) } else { fmt.Println("State not found") } } func main() { getStateDetails("IN", "MH") } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.Map; public class StateDetails { public static void main(String[] args) throws Exception { String countryCode = "IN"; String stateCode = "MH"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries/" + countryCode + "/states/" + stateCode)) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { Gson gson = new Gson(); Type mapType = new TypeToken>(){}.getType(); Map state = gson.fromJson(response.body(), mapType); System.out.println(state.get("name") + " is located at " + state.get("latitude") + ", " + state.get("longitude")); } else { System.out.println("State not found"); } } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' def get_state_details(country_code, state_code) uri = URI("https://api.countrystatecity.in/v1/countries/#{country_code}/states/#{state_code}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) if response.code == '200' state = JSON.parse(response.body) puts "#{state['name']} is located at #{state['latitude']}, #{state['longitude']}" state else puts 'State not found' nil end end state = get_state_details('IN', 'MH') ``` ```json 200 - Basic tier (Community, Starter, Legacy) theme={null} { "id": 4008, "name": "Maharashtra", "country_id": 101, "country_code": "IN", "iso2": "MH", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata" } ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} { "id": 4008, "name": "Maharashtra", "country_id": 101, "country_code": "IN", "iso2": "MH", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata", "fips_code": "16", "iso3166_2": "IN-MH", "type": "state", "level": 1, "parent_id": null, "native": "महाराष्ट्र", "population": 112374333, "translations": "{\"kr\":\"마하라슈트라\",\"de\":\"Maharashtra\",\"fr\":\"Maharashtra\",\"cn\":\"马哈拉施特拉邦\"}", "wikiDataId": "Q1191" } ``` ```json 404 - Not Found theme={null} { "error": "No State/Region found." } ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Use coordinates for distance calculations and mapping. ```javascript theme={null} const calculateDistance = (lat1, lng1, lat2, lng2) => { const R = 6371; // Earth's radius in km const dLat = (lat2 - lat1) * Math.PI / 180; const dLng = (lng2 - lng1) * Math.PI / 180; const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng/2) * Math.sin(dLng/2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); }; const getDistanceFromState = async (countryCode, stateCode, targetLat, targetLng) => { const state = await getStateDetails(countryCode, stateCode); return calculateDistance( parseFloat(state.latitude), parseFloat(state.longitude), targetLat, targetLng ); }; ``` Validate administrative division types for data processing. ```javascript theme={null} const isStateType = async (countryCode, stateCode, expectedType) => { const state = await getStateDetails(countryCode, stateCode); return state && state.type === expectedType; }; ``` Use this endpoint when you need precise geographical coordinates or want to validate specific state/country combinations. ## Tier-Based Field Availability | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `name`, `iso2`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone` | | **Full** | Supporter, Professional, Business | All Basic **+** `fips_code`, `iso3166_2`, `type`, `level`, `parent_id`, `native`, `population`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get States by Country Source: https://docs.countrystatecity.in/api/endpoints/get-states-by-country GET /countries/{ciso}/states Retrieve all states/provinces for a specific country Retrieve all states, provinces, regions, and territories for a specific country using the country's ISO2 code. **Availability:** All plans (Community and above). The fields returned vary by tier — see **Tier-Based Field Availability** below. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. Additional fields like `fips_code`, `iso3166_2`, `type`, `level`, `parent_id`, `native`, `population`, `translations`, and `wikiDataId` are returned on higher tiers. See **Tier-Based Field Availability** below. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with search) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states?q=maha' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/states?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```python Python theme={null} import requests def get_states_by_country(country_code): response = requests.get( f'https://api.countrystatecity.in/v1/countries/{country_code}/states', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) if response.ok: states = response.json() print(f'Found {len(states)} states in {country_code}') return states else: print('Country not found or no states available') return [] states = get_states_by_country('IN') ``` ```javascript JavaScript theme={null} const getStatesByCountry = async (countryCode) => { const response = await fetch(`https://api.countrystatecity.in/v1/countries/${countryCode}/states`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.ok) { const states = await response.json(); console.log(`Found ${states.length} states in ${countryCode}`); return states; } else { console.error('Country not found or no states available'); } }; getStatesByCountry('IN'); ``` ```php PHP theme={null} "https://api.countrystatecity.in/v1/countries/{$countryCode}/states", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array( 'X-CSCAPI-KEY: YOUR_API_KEY' ), )); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode == 200) { $states = json_decode($response, true); echo "Found " . count($states) . " states in {$countryCode}\n"; return $states; } else { echo "Country not found or no states available\n"; return []; } } $states = getStatesByCountry('IN'); ?> ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) func getStatesByCountry(countryCode string) []map[string]interface{} { url := fmt.Sprintf("https://api.countrystatecity.in/v1/countries/%s/states", countryCode) client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-CSCAPI-KEY", "YOUR_API_KEY") res, _ := client.Do(req) defer res.Body.Close() if res.StatusCode == 200 { body, _ := ioutil.ReadAll(res.Body) var states []map[string]interface{} json.Unmarshal(body, &states) fmt.Printf("Found %d states in %s\n", len(states), countryCode) return states } else { fmt.Printf("Country not found or no states available\n") return nil } } func main() { states := getStatesByCountry("IN") fmt.Println(states) } ``` ```java Java theme={null} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class StatesByCountry { public static void main(String[] args) throws Exception { String countryCode = "IN"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.countrystatecity.in/v1/countries/" + countryCode + "/states")) .header("X-CSCAPI-KEY", "YOUR_API_KEY") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { Gson gson = new Gson(); Type listType = new TypeToken>>(){}.getType(); List> states = gson.fromJson(response.body(), listType); System.out.println("Found " + states.size() + " states in " + countryCode); } else { System.out.println("Country not found or no states available"); } } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' def get_states_by_country(country_code) uri = URI("https://api.countrystatecity.in/v1/countries/#{country_code}/states") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = 'YOUR_API_KEY' response = http.request(request) if response.code == '200' states = JSON.parse(response.body) puts "Found #{states.length} states in #{country_code}" states else puts 'Country not found or no states available' [] end end states = get_states_by_country('IN') ``` ```json 200 - Basic tier (Community, Starter, Legacy) theme={null} [ { "id": 4008, "name": "Maharashtra", "iso2": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata" } ] ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} [ { "id": 4008, "name": "Maharashtra", "iso2": "MH", "country_id": 101, "country_code": "IN", "latitude": "19.75147980", "longitude": "75.71388840", "timezone": "Asia/Kolkata", "fips_code": "16", "iso3166_2": "IN-MH", "type": "state", "level": 1, "parent_id": null, "native": "महाराष्ट्र", "population": 112374333, "translations": "{\"kr\":\"마하라슈트라\",\"de\":\"Maharashtra\",\"fr\":\"Maharashtra\",\"cn\":\"马哈拉施特拉邦\"}", "wikiDataId": "Q1191" } ] ``` ```json 404 - Not Found theme={null} { "error": "No States/Regions found." } ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ## Common Use Cases Create dependent dropdowns where states populate based on country selection. ```javascript theme={null} const populateStatesDropdown = async (countryCode) => { const states = await getStatesByCountry(countryCode); const select = document.getElementById('state-select'); // Clear existing options select.innerHTML = ''; states.forEach(state => { const option = document.createElement('option'); option.value = state.iso2; option.textContent = state.name; select.appendChild(option); }); }; // Listen for country changes document.getElementById('country-select').addEventListener('change', (e) => { populateStatesDropdown(e.target.value); }); ``` Validate state codes against specific countries. ```javascript theme={null} const validateStateForCountry = async (countryCode, stateCode) => { const states = await getStatesByCountry(countryCode); return states.some(state => state.iso2 === stateCode); }; ``` Cache states by country as they rarely change. Use this endpoint instead of filtering all states for better performance. ## Tier-Based Field Availability | Tier | Plans | Fields | | - | - | - | | **Basic** | Community, Starter, Legacy | `id`, `name`, `iso2`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone` | | **Full** | Supporter, Professional, Business | All Basic **+** `fips_code`, `iso3166_2`, `type`, `level`, `parent_id`, `native`, `population`, `translations`, `wikiDataId`, `localized_name`, `matched_locale` | `translations` is part of Full access but is not returned by default: add `include_translations=true`, or name it in `?fields=`. `localized_name` and `matched_locale` are computed from the translations rather than stored, and appear only when you pass `locale`. See [Localized Place Names](/api/localization). See [Pricing](https://app.countrystatecity.in/pricing) for plan details. # Get Subregion Details Source: https://docs.countrystatecity.in/api/endpoints/get-subregion-details GET /subregions/{id} Retrieve a single subregion by its numeric ID Retrieve detailed information for a single subregion (e.g., Southern Asia, Western Europe, Caribbean) by its numeric ID. **Availability:** Starter plan and above. Returns `403` on lower tiers. **Trim response columns:** Add `?fields=name,region_id` to receive only the columns you need. Available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/subregions/14' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (with translations) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/subregions/14?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/subregions/14', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const subregion = await response.json(); console.log(subregion); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/subregions/14', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) subregion = response.json() print(subregion) ``` ```json 200 - Basic tier (Starter) theme={null} { "id": 14, "name": "Southern Asia", "region_id": 3 } ``` ```json 200 - Full tier, with translations (Supporter, Professional, Business) theme={null} { "id": 14, "name": "Southern Asia", "region_id": 3, "wikiDataId": "Q771405", "translations": "{\"fr\":\"Asie du Sud\",\"de\":\"Südasien\",\"es\":\"Asia meridional\",\"ja\":\"南アジア\",\"cn\":\"南亚\"}" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "regionsApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - Not Found theme={null} { "error": "Subregion not found." } ``` ## Related Endpoints * [Get Subregions of a Region](/api/endpoints/get-subregions-by-region) — list subregions for a parent region * [Get Countries in a Subregion](/api/endpoints/get-countries-by-subregion) — drill into countries # Get Subregions by Region Source: https://docs.countrystatecity.in/api/endpoints/get-subregions-by-region GET /regions/{id}/subregions Retrieve subregions within a geographic region Retrieve all subregions belonging to a specific region. For example, region "Asia" contains subregions like "Southern Asia", "Eastern Asia", etc. **Availability:** Starter plan and above. **Trim and order results:** Add `?fields=` to limit columns returned, or `?sort=` to order the list. Both are available on **Starter+** plans. See the [Field Filtering & Sorting](/api/field-filtering-and-sorting) guide for syntax and per-entity sortable fields. ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/regions/3/subregions' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/regions/3/subregions', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const subregions = await response.json(); console.log(subregions); ``` ```json 200 - Success theme={null} [ { "id": 14, "name": "Southern Asia", "region_id": 3 }, { "id": 15, "name": "Eastern Asia", "region_id": 3 }, { "id": 16, "name": "South-Eastern Asia", "region_id": 3 }, { "id": 17, "name": "Western Asia", "region_id": 3 }, { "id": 18, "name": "Central Asia", "region_id": 3 } ] ``` # Get Timezone by City Source: https://docs.countrystatecity.in/api/endpoints/get-timezone-by-city GET /timezone/{ciso}/{siso}/{city_id} Retrieve the timezone for a specific city Get the timezone for a specific city. The country and state codes in the path are not redundant — they're enforced server-side as an **ownership check**: a city ID is only resolved if it actually belongs to the given state inside the given country. This prevents "city-ID spoofing" — a caller can't learn the timezone of an arbitrary city by trying its ID under a different country/state pair. **Availability:** All plans (Community and above). Only an API key is required. Responses are cached server-side for 24 hours. The cache key includes country, state, **and** city ID, so the ownership check holds on cache HIT — a city ID looked up under one (ciso, siso) pair won't satisfy a request under a different pair. ## Response Same shape as the country and state timezone endpoints: ```bash cURL (San Francisco, CA) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/US/CA/24295' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (Mumbai, IN-MH) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/IN/MH/57614' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.countrystatecity.in/v1/timezone/US/CA/24295', // San Francisco { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const tz = await response.json(); const localNow = new Date().toLocaleString('en-US', { timeZone: tz.iana }); console.log(`Current local time: ${localNow}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/timezone/US/NY/124394', # New York City headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) print(response.json()['iana']) # "America/New_York" ``` ```json 200 - San Francisco (DST in summer) theme={null} { "iana": "America/Los_Angeles", "abbreviation": "PDT", "offset_utc": "-08:00", "dst_offset_utc": "-07:00", "is_dst_now": true } ``` ```json 404 - City not in this country/state theme={null} { "error": "City not found." } ``` ```json 404 - No timezone on record theme={null} { "error": "No timezone data available for this city." } ``` ## Related Endpoints * [Get Timezone by Country](/api/endpoints/get-timezone-by-country) — country-level (uses the capital's timezone) * [Get Timezone by State](/api/endpoints/get-timezone-by-state) — state-level precision * [Get Cities by State](/api/endpoints/get-cities-by-state) — list cities to find the right city ID ## Notes on `city_id` The `city_id` is the same numeric ID returned by all list endpoints (`/v1/countries/:ciso/states/:siso/cities`, `/v1/cities`, etc.). If you stored a city ID in your own database, this endpoint is the canonical way to look up its timezone without re-fetching the full city record. # Get Timezone by Country Source: https://docs.countrystatecity.in/api/endpoints/get-timezone-by-country GET /timezone/{ciso} Retrieve the canonical timezone for a country including current DST state Return the canonical IANA timezone for a country, plus its standard and DST UTC offsets and whether the country is observing DST at request time. Resolution order: 1. The capital city's timezone — most countries with multiple zones use this as the canonical default. 2. The first entry in the country's `timezones` JSON column — fallback when no capital-city match exists. **Availability:** All plans (Community and above). Only an API key is required. Responses are cached server-side for 24 hours. **Known DST sampling limitation:** standard and DST offsets are determined by sampling Jan 1 and Jul 1 of the current year, which is correct for almost every populated IANA zone. A small number of zones with non-standard DST windows (most notably `Africa/Casablanca` with Ramadan-based transitions) can report incorrect `offset_utc` / `dst_offset_utc` / `is_dst_now` values during their actual DST window. Use a dedicated timezone library if you need exact DST behaviour for those edge cases. ```bash cURL (ISO2) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/US' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (ISO3) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/IND' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (numeric ID) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/233' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/timezone/US', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const tz = await response.json(); // Format current time in that zone const local = new Date().toLocaleString('en-US', { timeZone: tz.iana }); console.log(`${tz.iana} | ${local} | DST: ${tz.is_dst_now}`); ``` ```python Python theme={null} import requests from datetime import datetime from zoneinfo import ZoneInfo response = requests.get( 'https://api.countrystatecity.in/v1/timezone/IN', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) tz = response.json() now_local = datetime.now(ZoneInfo(tz['iana'])) print(f"{tz['iana']} ({tz['abbreviation']}) — {now_local}") ``` ```json 200 - DST-observing country (in summer) theme={null} { "iana": "America/New_York", "abbreviation": "EDT", "offset_utc": "-05:00", "dst_offset_utc": "-04:00", "is_dst_now": true } ``` ```json 200 - Non-DST country theme={null} { "iana": "Asia/Kolkata", "abbreviation": "GMT+5:30", "offset_utc": "+05:30", "dst_offset_utc": "+05:30", "is_dst_now": false } ``` ```json 401 - Unauthorized theme={null} { "error": "Unauthorized. You shouldn't be here." } ``` ```json 404 - Country Not Found theme={null} { "error": "Country not found." } ``` ```json 404 - No Timezone Data theme={null} { "error": "No timezone data available for this country." } ``` ## Related Endpoints * [Get Timezone by State](/api/endpoints/get-timezone-by-state) — finer-grained timezone for federations / countries with multiple zones (US, Canada, Russia, Australia, etc.) * [Get Timezone by City](/api/endpoints/get-timezone-by-city) — per-city precision * [Get Country Details](/api/endpoints/get-country-details) — includes the raw `timezones` JSON column # Get Timezone by State Source: https://docs.countrystatecity.in/api/endpoints/get-timezone-by-state GET /timezone/{ciso}/{siso} Retrieve the timezone for a specific state or province Get the timezone for a state/province within a country. Useful for federations or countries that span multiple zones — US states, Canadian provinces, Russian federal subjects, Australian states, etc. **Availability:** All plans (Community and above). Only an API key is required. Responses are cached server-side for 24 hours. The cache key includes both the country and state codes, so cross-country state-code collisions never cause cache poisoning. ## Response Identical shape to [`/v1/timezone/:ciso`](/api/endpoints/get-timezone-by-country): ```bash cURL (California) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/US/CA' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (Maharashtra, India) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/IN/MH' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (Bavaria, Germany — ISO3 country) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/timezone/DEU/BY' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.countrystatecity.in/v1/timezone/US/NY', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const tz = await response.json(); console.log(`${tz.iana} | DST in effect: ${tz.is_dst_now}`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/timezone/US/HI', # Hawaii — no DST headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) print(response.json()) # {"iana": "Pacific/Honolulu", "abbreviation": "HST", "offset_utc": "-10:00", # "dst_offset_utc": "-10:00", "is_dst_now": false} ``` ```json 200 - California (DST in summer) theme={null} { "iana": "America/Los_Angeles", "abbreviation": "PDT", "offset_utc": "-08:00", "dst_offset_utc": "-07:00", "is_dst_now": true } ``` ```json 200 - Hawaii (no DST) theme={null} { "iana": "Pacific/Honolulu", "abbreviation": "HST", "offset_utc": "-10:00", "dst_offset_utc": "-10:00", "is_dst_now": false } ``` ```json 404 - State Not Found theme={null} { "error": "State not found." } ``` ```json 404 - No Timezone Data theme={null} { "error": "No timezone data available for this state." } ``` ## Related Endpoints * [Get Timezone by Country](/api/endpoints/get-timezone-by-country) — country-level resolution (uses the capital's timezone) * [Get Timezone by City](/api/endpoints/get-timezone-by-city) — per-city precision * [Get State Details](/api/endpoints/get-state-details) — full state record including the raw `timezone` field # List All Currencies Source: https://docs.countrystatecity.in/api/endpoints/list-all-currencies GET /currency Retrieve the currency used by every country, with optional reverse lookup by ISO 4217 code Retrieve the currency mapping for every country in the database. Each item links a country (by ISO2) to its currency `code`, `name`, and `symbol`. Use the optional `?code=` query parameter to perform a reverse lookup and find every country that uses a given ISO 4217 currency code (e.g., all Eurozone countries). **Availability:** Starter plan and above. Returns `403` on lower tiers. The list is alphabetically ordered by country ISO2 and excludes countries without currency data (\~0.1% of records). Results are cached server-side for 24 hours. ```bash cURL (all currencies) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/currency' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (Eurozone lookup) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/currency?code=EUR' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/currency?code=EUR', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const eurozone = await response.json(); console.log(`${eurozone.length} countries use EUR`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/currency', params={'code': 'EUR'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) eurozone = response.json() print(f"{len(eurozone)} countries use EUR") ``` ```json 200 - Full List (truncated) theme={null} [ { "country": "AD", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } }, { "country": "AE", "currency": { "code": "AED", "name": "United Arab Emirates dirham", "symbol": "د.إ" } }, { "country": "AF", "currency": { "code": "AFN", "name": "Afghan afghani", "symbol": "؋" } } ] ``` ```json 200 - Filtered (?code=EUR) theme={null} [ { "country": "AD", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } }, { "country": "AT", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } }, { "country": "BE", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } }, { "country": "DE", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } }, { "country": "FR", "currency": { "code": "EUR", "name": "Euro", "symbol": "€" } } ] ``` ```json 400 - Bad Request theme={null} { "status": "error", "message": "Invalid currency code. Must be a 3-letter ISO 4217 code (e.g., USD, EUR)." } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "currencyApi", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Related Endpoints * [Get Currency by Country](/api/endpoints/get-currency-by-country) — fetch the currency for a single country * [Get Country Details](/api/endpoints/get-country-details) — full country record (includes currency fields inline) # List Dial Codes Source: https://docs.countrystatecity.in/api/endpoints/list-dial-codes GET /phone List every country's international dial code, with optional reverse lookup by dial code Return every country's international dial code in one call, or do the reverse — find every country that shares a given dial code (useful for NANP `+1` countries like the US and Canada). Dial codes follow ITU-T E.164 and are returned with a leading `+`. For NANP countries with an area-code prefix in the underlying data (e.g. Barbados as `+1-246`), the area code is returned as a separate `area_code` field. **Availability:** Starter plan and above. Returns `403` on lower tiers. Responses are cached server-side for 24 hours. Equivalent dial-code inputs (`+91`, `91`, `091`, `0091`) all resolve to the same cache slot. ## Response The response is **an array of phone entries**, even when the result is a single country. ```bash cURL (all countries) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (reverse lookup) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone?code=%2B1' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (without URL-encoded +) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone?code=91' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.countrystatecity.in/v1/phone?code=' + encodeURIComponent('+44'), { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const entries = await response.json(); // entries: [{ country: "GB", dial_code: "+44", iso2: "GB", iso3: "GBR" }, ...] ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/phone', params={'code': '+1'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) nanp_countries = response.json() print(f"{len(nanp_countries)} countries share +1") ``` ```json 200 - Reverse lookup (?code=+1) theme={null} [ { "country": "AG", "dial_code": "+1", "area_code": "268", "iso2": "AG", "iso3": "ATG" }, { "country": "AI", "dial_code": "+1", "area_code": "264", "iso2": "AI", "iso3": "AIA" }, { "country": "BB", "dial_code": "+1", "area_code": "246", "iso2": "BB", "iso3": "BRB" }, { "country": "BS", "dial_code": "+1", "area_code": "242", "iso2": "BS", "iso3": "BHS" }, { "country": "CA", "dial_code": "+1", "iso2": "CA", "iso3": "CAN" }, { "country": "US", "dial_code": "+1", "iso2": "US", "iso3": "USA" } ] ``` ```json 200 - Single country (?code=+91) theme={null} [ { "country": "IN", "dial_code": "+91", "iso2": "IN", "iso3": "IND" } ] ``` ```json 400 - Empty code theme={null} { "status": "error", "message": "Invalid dial code. Provide a non-empty value (e.g., +1, 44)." } ``` ```json 400 - Non-numeric / wrong length theme={null} { "status": "error", "message": "Invalid dial code format. Expected digits (e.g., 91, 1, 44)." } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "phoneDialCode", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ## Related Endpoints * [Get Dial Code by Country](/api/endpoints/get-dial-code-by-country) — one country at a time when you already know the country * [Parse Phone Number](/api/endpoints/parse-phone-number) — given an E.164 number, identify the country # Lookup Country by ISO Code Source: https://docs.countrystatecity.in/api/endpoints/lookup-country-by-iso GET /iso/country Find a country by ISO 3166-1 alpha-2, alpha-3, or numeric code Look up a single country by **any one** of the three ISO 3166-1 codes: 2-letter (alpha-2), 3-letter (alpha-3), or 3-digit numeric. Exactly one query parameter must be provided. This is the canonical lookup when you have an ISO code from an external system (payment gateway, geolocation service, address parser) and need to resolve it to the country record. **Availability:** Starter plan and above. Returns `403` on Community plan. Responses are cached server-side for 1 hour. The cache slot is shared with [`/v1/iso/country/convert`](/api/endpoints/convert-iso-code) — warming the cache via one endpoint serves the other for free. Numeric codes `4` and `004` resolve to the same cache slot. ## Query Parameters Provide **exactly one** of the three. Sending two or zero returns `400`. ```bash cURL (alpha-2) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country?iso2=US' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (alpha-3) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country?iso3=IND' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (numeric) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/country?numeric=840' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.countrystatecity.in/v1/iso/country?iso2=US', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const country = await response.json(); console.log(`${country.name} (id ${country.id})`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/iso/country', params={'iso3': 'USA'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) country = response.json() print(f"{country['name']} → numeric {country['numeric_code']}") ``` ```json 200 - Success theme={null} { "id": 233, "name": "United States", "iso2": "US", "iso3": "USA", "numeric_code": "840" } ``` ```json 400 - Wrong number of params theme={null} { "status": "error", "message": "Invalid query parameters: Provide exactly one of: iso2, iso3, numeric" } ``` ```json 400 - Malformed code theme={null} { "status": "error", "message": "Invalid query parameters: iso2: must be a 2-letter code" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "isoLookup", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - No Match theme={null} { "status": "error", "message": "Country not found for the given code" } ``` ## Related Endpoints * [Lookup State by ISO Code](/api/endpoints/lookup-state-by-iso) — same idea for ISO 3166-2 subdivision codes * [Convert ISO Code](/api/endpoints/convert-iso-code) — translate between alpha-2, alpha-3, and numeric in one call * [Get Country Details](/api/endpoints/get-country-details) — full country record (all fields, tier-gated) # Lookup State by ISO Code Source: https://docs.countrystatecity.in/api/endpoints/lookup-state-by-iso GET /iso/state Find a state or province by ISO 3166-2 subdivision code Resolve an ISO 3166-2 subdivision code (e.g. `US-CA`, `IN-MH`, `DE-BY`) to the state/province record. The match prefers the canonical `iso3166_2` column. When that column is `NULL` for a row (common in older imports), the endpoint falls back to matching the legacy `iso2` column scoped to the same country — so the lookup works even for partially-coded rows without risking cross-country false matches. **Availability:** Starter plan and above. Returns `403` on Community plan. Responses are cached server-side for 1 hour. The lookup key is the full ISO 3166-2 code, so `US-CA` and `us-ca` collapse to the same cache slot (input is auto-uppercased). ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/state?iso=US-CA' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (case-insensitive) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/iso/state?iso=in-mh' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.countrystatecity.in/v1/iso/state?iso=US-CA', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const state = await response.json(); console.log(`${state.name}, ${state.country_code} (id ${state.id})`); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/iso/state', params={'iso': 'DE-BY'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) state = response.json() print(state['name']) # "Bavaria" ``` ```json 200 - Success theme={null} { "id": 1416, "name": "California", "iso2": "CA", "iso3166_2": "US-CA", "country_id": 233, "country_code": "US" } ``` ```json 200 - Resolved via legacy fallback theme={null} { "id": 4022, "name": "Maharashtra", "iso2": "MH", "iso3166_2": null, "country_id": 101, "country_code": "IN" } ``` ```json 400 - Missing param theme={null} { "status": "error", "message": "Invalid query parameters: iso: is required (e.g. iso=US-CA)" } ``` ```json 400 - Malformed code theme={null} { "status": "error", "message": "Invalid query parameters: iso: must be an ISO 3166-2 code (e.g. US-CA)" } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "isoLookup", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - No Match theme={null} { "status": "error", "message": "State not found for the given ISO code" } ``` ## Related Endpoints * [Lookup Country by ISO Code](/api/endpoints/lookup-country-by-iso) — country-level ISO lookup (alpha-2/alpha-3/numeric) * [Get State Details](/api/endpoints/get-state-details) — full state record (all fields, tier-gated) * [Get States by Country](/api/endpoints/get-states-by-country) — all states for a given country # Parse Phone Number Source: https://docs.countrystatecity.in/api/endpoints/parse-phone-number GET /phone/parse Parse an E.164 phone number into its country and national parts Given a phone number in E.164 format (`+` followed by country code and national digits), identify the originating country and split out the national portion. The matcher walks 3-digit → 2-digit → 1-digit ITU prefixes, so longer-prefix countries (like `+972` Israel) win over shorter-prefix ones that share the leading digit. For NANP `+1` numbers, the area code is matched against known territory area codes — `+1-246-555-1234` resolves to Barbados (`BB`), not the US. **Availability:** Starter plan and above. Returns `403` on lower tiers. **The response echoes the phone number** (`e164` and `national_number`), so it's PII. The endpoint returns `Cache-Control: no-store` and emits no `ETag` — Cloudflare, browsers, and intermediate proxies will not retain the response. Do not change this if you're proxying through additional caching layers; the `Cache-Control` is intentional. The **internal phone-prefix index** is cached server-side for 24 hours. The `X-Parse-Index-Cache: HIT|MISS` header reports whether that internal lookup was a cache hit — it does **not** describe the response body. ```bash cURL (US number) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/parse?number=%2B14155552671' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (Indian number) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/parse?number=%2B919876543210' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```bash cURL (NANP territory — Barbados) theme={null} curl -X GET 'https://api.countrystatecity.in/v1/phone/parse?number=%2B12465551234' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const number = '+14155552671'; const response = await fetch( `https://api.countrystatecity.in/v1/phone/parse?number=${encodeURIComponent(number)}`, { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } } ); const parsed = await response.json(); // { country: "US", dial_code: "+1", iso2: "US", iso3: "USA", // national_number: "4155552671", e164: "+14155552671" } ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/phone/parse', params={'number': '+919876543210'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) parsed = response.json() print(f"{parsed['country']} | {parsed['dial_code']} | national {parsed['national_number']}") ``` ```json 200 - US number theme={null} { "country": "US", "dial_code": "+1", "iso2": "US", "iso3": "USA", "national_number": "4155552671", "e164": "+14155552671" } ``` ```json 200 - NANP territory (area-code disambiguation) theme={null} { "country": "BB", "dial_code": "+1", "area_code": "246", "iso2": "BB", "iso3": "BRB", "national_number": "2465551234", "e164": "+12465551234" } ``` ```json 200 - 3-digit ITU prefix wins theme={null} { "country": "IL", "dial_code": "+972", "iso2": "IL", "iso3": "ISR", "national_number": "501234567", "e164": "+972501234567" } ``` ```json 400 - Missing param theme={null} { "status": "error", "message": "Missing required query parameter: number" } ``` ```json 400 - No leading + theme={null} { "status": "error", "message": "Phone number must be in E.164 format starting with + (e.g., +14155552671)." } ``` ```json 400 - Wrong digit count theme={null} { "status": "error", "message": "Invalid phone number. Must contain 5-15 digits after the +." } ``` ```json 403 - Feature Restricted theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "phoneDialCode", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` ```json 404 - No matching country theme={null} { "status": "error", "message": "Could not identify a country for the given phone number." } ``` ## Notes on Matching The matcher tries 3-digit, then 2-digit, then 1-digit ITU prefixes: * `+972...` matches Israel (3-digit prefix), not Yemen (`+967`) or any 1-digit `+9` country (none exist, but the logic generalizes). * `+44...` matches the UK (2-digit prefix). * `+1...` is the NANP — the next 3 digits are checked against known area codes (Barbados `246`, Antigua `268`, etc.). If no area code matches, the request falls back to the US. When the data carries a `+1-XXX` area-code prefix for a country, the matcher prefers that more-specific entry over the bare `+1`. ## Related Endpoints * [Get Dial Code by Country](/api/endpoints/get-dial-code-by-country) — when you already know the country * [List Dial Codes](/api/endpoints/list-dial-codes) — every country plus reverse lookup # Errors & Rate Limits Source: https://docs.countrystatecity.in/api/errors The two error shapes the API returns, the 403 plan gate, the 429 rate limit, and the usage headers The Country State City API returns errors in two shapes. Which one you get depends on the endpoint and the kind of failure, so write your error handling to read both. ## The two error shapes **Structured envelope** — used by plan-gate `403` responses, `429` rate limits, and the `?fields=`/`?sort=` validation errors: ```json theme={null} { "status": "error", "message": "", "details": { ... } } ``` `details` is optional. It's present when the error carries structured metadata, like the `403` and `429` responses below. Other errors in this shape return just `status` and `message`. **Bare error object** — used by a small set of geographical `404` responses: country details, state details, region details, subregion details, and currency by country: ```json theme={null} { "error": "" } ``` There is no single envelope that covers every failure. Read the message from `message` **or** `error`, and treat `details` as optional. ```javascript Reading either shape theme={null} // Normalises both error shapes into one object. async function readError(response) { const body = await response.json().catch(() => ({})); return { status: response.status, message: body.message ?? body.error ?? 'Unknown error', details: body.details ?? null }; } ``` Each endpoint page lists the exact error bodies that endpoint returns. When you need the precise wording for one endpoint, check its page — for example [Get Currency by Country](/api/endpoints/get-currency-by-country) or [Parse Phone Number](/api/endpoints/parse-phone-number). ## HTTP status codes | Code | Meaning | | - | - | | 400 | Bad Request — malformed or invalid query parameter | | 401 | Unauthorized — missing or invalid API key | | 403 | Forbidden — authenticated, but the request isn't allowed (see below) | | 404 | Not Found — the requested resource doesn't exist (see below) | | 409 | Conflict — the resource already exists | | 429 | Too Many Requests — usage limit exceeded (see below) | | 503 | Service Unavailable — temporary infrastructure issue, safe to retry | A `401` is rejected by the authentication layer before any plan or usage check runs. See [Authentication](/api/authentication) for its response body and the usual causes. ## 403 — Forbidden A `403` means your API key authenticated fine, but the request isn't allowed. Most `403` responses are **plan gates**, and those carry a `details` object. ### Plan-gate 403 Returned when the endpoint, query parameter, or feature you're using isn't included in your plan — for example, calling [Fuzzy Search](/api/endpoints/fuzzy-search) on a Community plan: ```json 403 - Feature not available theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "fuzzySearch", "currentTier": "community", "requiredTier": "supporter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` Internal name of the gated feature (e.g. `fuzzySearch`, `searchEndpoint`, `fieldsFiltering`, `sortParameter`, `regionsApi`, `currencyApi`). Your plan's tier at the time of the request. The minimum tier that unlocks this feature, or `null`. See **Usage limits by tier** below for what each tier includes. Direct link to upgrade your plan. `requiredTier` is `null` when there is no higher active plan the API can safely recommend, or when the live plan catalogue could not be read. The gate itself is still real — only the "upgrade to this tier" hint is missing. Follow `details.upgradeUrl` (or see [Pricing](https://app.countrystatecity.in/pricing)) to compare the available plans. Handle it as `details.requiredTier ?? 'see pricing'` rather than printing `null` to your users. ### Other 403s Not every `403` is a plan gate. A `403` raised for any other reason returns the envelope with **no** `details` object: ```json 403 - No details theme={null} { "status": "error", "message": "" } ``` Check that `details` exists before you branch on it. `error.details?.feature` tells you it's a plan gate; a missing `details` means the message is all you get, and upgrading won't help. ## 404 — Not Found Most `404` responses use the structured envelope. Five geographical handlers deliberately return the bare error object instead: country details, state details, region details, subregion details, and currency by country. They use it for two cases: ```json 404 - Record doesn't exist theme={null} { "error": "Country not found." } ``` ```json 404 - No data for this record theme={null} { "error": "No currency data available for this country." } ``` The first means the code or ID you passed matches nothing. The second means the record exists, but has nothing on file for the attribute you asked for — a country with no currency on record, a city with no timezone. Both are `404`, and the wording varies per endpoint. Retrying won't change a `404`. Treat "no data on record" as a legitimate empty result in your integration rather than as a failure to retry. ## 429 — Rate limit exceeded Every plan has a daily and a monthly request quota. Exceeding either returns `429 Too Many Requests`: ```json 429 - Daily limit exceeded theme={null} { "status": "error", "message": "Daily usage limit exceeded. Please try again tomorrow or contact support for higher limits.", "details": { "limit": 100, "period": "daily", "resetAt": "2026-08-14T00:00:00.000Z", "upgradeUrl": "https://app.countrystatecity.in/pricing", "tier": "community" } } ``` ```json 429 - Monthly limit exceeded theme={null} { "status": "error", "message": "Monthly usage limit exceeded. Please upgrade for higher limits.", "details": { "limit": 3000, "period": "monthly", "resetAt": "2026-09-01T00:00:00.000Z", "upgradeUrl": "https://app.countrystatecity.in/pricing", "tier": "community" } } ``` The quota you exceeded, in requests. `daily` or `monthly` — which quota was exceeded. ISO 8601 UTC timestamp for when the quota resets: next UTC midnight for `daily`, the 1st of next UTC month for `monthly`. Use this to schedule your retry instead of guessing a backoff window. Your plan's tier at the time of the request. ### Handling a 429 in your integration ```javascript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries/IN/cities', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); if (response.status === 429) { // readError() is defined in "The two error shapes" above. const { message, details } = await readError(response); console.error(message); if (details?.resetAt) { const waitMinutes = Math.ceil((new Date(details.resetAt).getTime() - Date.now()) / 60000); console.log(`Limit resets in ${waitMinutes} minutes`); } } ``` ## Usage headers When a request is authenticated and counted against your quota — the normal case on a successful `/v1` call — the response carries your plan and current usage as headers. Use them to watch your quota instead of waiting for a `429`: | Header | Example | Meaning | | - | - | - | | `X-CSC-Plan` | `community` | The tier your API key is on — the same value as `details.currentTier` on a plan-gate `403` and `details.tier` on a `429`. | | `X-CSC-Daily-Used` | `42` | Requests counted against today's quota so far. | | `X-CSC-Daily-Limit` | `100` | Your daily quota — `unlimited` on the Custom tier. | | `X-CSC-Monthly-Used` | `1203` | Requests counted against this month's quota so far. | | `X-CSC-Monthly-Limit` | `3000` | Your monthly quota — `unlimited` on the Custom tier. | These headers are not on every `/v1` response. They're added when the request is authenticated and counted, so error responses may not carry them. In particular: * **`401`** — rejected before any usage is counted, so no usage headers. * **`429`** — the request was refused, so no usage headers. Read `details.limit` and `details.resetAt` from the body instead. Treat a missing header as *unknown*, never as `0`. ```javascript Reading the usage headers safely theme={null} const plan = response.headers.get('X-CSC-Plan'); const used = response.headers.get('X-CSC-Daily-Used'); const limit = response.headers.get('X-CSC-Daily-Limit'); if (used === null || limit === null) { console.log('Usage headers not present on this response'); } else if (limit === 'unlimited') { console.log(`${used} requests today on the ${plan} plan (no daily limit)`); } else { console.log(`${Number(limit) - Number(used)} requests left today on the ${plan} plan`); } ``` ## Usage limits by tier | Tier | Daily | Monthly | | - | -: | -: | | Community | 100 | 3,000 | | Starter | 300 | 9,000 | | Supporter | 1,000 | 30,000 | | Professional | 2,100 | 63,000 | | Business | 3,300 | 100,000 | | Legacy | 50,000 | 1,500,000 | | Custom | Negotiated | Negotiated | See [Pricing](https://app.countrystatecity.in/pricing) for what each tier unlocks beyond request volume — data fields, `?fields=`/`?sort=`, search, and other gated features. ## Related API key setup and the 401 Unauthorized response. `?fields=`/`?sort=` — the other common source of 400 and 403 responses. Common questions, including rate-limit troubleshooting. # Integration Examples Source: https://docs.countrystatecity.in/api/examples Comprehensive integration examples and real-world use cases for the Country State City API in multiple programming languages ## Complete Integration Examples ### Basic API Client Setup Set up a robust API client with proper authentication and error handling: ```javascript JavaScript/Node.js theme={null} class CountryStateCityAPI { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = 'https://api.countrystatecity.in/v1'; } async request(endpoint) { try { const response = await fetch(`${this.baseURL}${endpoint}`, { headers: { 'X-CSCAPI-KEY': this.apiKey, 'Content-Type': 'application/json' } }); if (!response.ok) { if (response.status === 401) { throw new Error('Invalid API key. Please check your credentials.'); } else if (response.status === 429) { throw new Error('Too many requests. Please try again later.'); } else { throw new Error(`API error: ${response.status}`); } } return await response.json(); } catch (error) { console.error('API request failed:', error); throw error; } } async getCountries() { return await this.request('/countries'); } async getStates(countryCode) { return await this.request(`/countries/${countryCode}/states`); } async getCities(countryCode, stateCode = null) { const endpoint = stateCode ? `/countries/${countryCode}/states/${stateCode}/cities` : `/countries/${countryCode}/cities`; return await this.request(endpoint); } async getCountryDetails(countryCode) { return await this.request(`/countries/${countryCode}`); } } // Usage const api = new CountryStateCityAPI('YOUR_API_KEY'); const countries = await api.getCountries(); const usStates = await api.getStates('US'); const californiaCities = await api.getCities('US', 'CA'); ``` ```python Python theme={null} import requests from typing import Optional, Dict, List class CountryStateCityAPI: def __init__(self, api_key: str): self.api_key = api_key self.base_url = 'https://api.countrystatecity.in/v1' self.session = requests.Session() self.session.headers.update({ 'X-CSCAPI-KEY': api_key, 'Content-Type': 'application/json' }) def _request(self, endpoint: str) -> Dict: """Make authenticated request to API endpoint""" try: response = self.session.get(f"{self.base_url}{endpoint}") if response.status_code == 401: raise ValueError('Invalid API key. Please check your credentials.') elif response.status_code == 429: raise ValueError('Too many requests. Please try again later.') elif not response.ok: raise ValueError(f'API error: {response.status_code}') return response.json() except requests.exceptions.RequestException as e: raise ConnectionError(f'Request failed: {str(e)}') def get_countries(self) -> List[Dict]: """Get all countries""" return self._request('/countries') def get_states(self, country_code: str) -> List[Dict]: """Get states for a specific country""" return self._request(f'/countries/{country_code}/states') def get_cities(self, country_code: str, state_code: Optional[str] = None) -> List[Dict]: """Get cities for a country, optionally filtered by state""" endpoint = f'/countries/{country_code}/states/{state_code}/cities' if state_code else f'/countries/{country_code}/cities' return self._request(endpoint) def get_country_details(self, country_code: str) -> Dict: """Get detailed information for a specific country""" return self._request(f'/countries/{country_code}') # Usage api = CountryStateCityAPI('YOUR_API_KEY') countries = api.get_countries() us_states = api.get_states('US') california_cities = api.get_cities('US', 'CA') ``` ```php PHP theme={null} apiKey = $apiKey; } private function request($endpoint) { $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $this->baseUrl . $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-CSCAPI-KEY: ' . $this->apiKey, 'Content-Type: application/json' ], CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if (curl_error($curl)) { curl_close($curl); throw new Exception('Request failed: ' . curl_error($curl)); } curl_close($curl); if ($httpCode === 401) { throw new Exception('Invalid API key. Please check your credentials.'); } elseif ($httpCode === 429) { throw new Exception('Too many requests. Please try again later.'); } elseif ($httpCode !== 200) { throw new Exception('API error: ' . $httpCode); } return json_decode($response, true); } public function getCountries() { return $this->request('/countries'); } public function getStates($countryCode) { return $this->request("/countries/{$countryCode}/states"); } public function getCities($countryCode, $stateCode = null) { $endpoint = $stateCode ? "/countries/{$countryCode}/states/{$stateCode}/cities" : "/countries/{$countryCode}/cities"; return $this->request($endpoint); } } // Usage $api = new CountryStateCityAPI('YOUR_API_KEY'); $countries = $api->getCountries(); $usStates = $api->getStates('US'); ?> ``` ```java Java theme={null} import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import java.time.Duration; public class CountryStateCityAPI { private final String apiKey; private final String baseUrl = "https://api.countrystatecity.in/v1"; private final HttpClient httpClient; public CountryStateCityAPI(String apiKey) { this.apiKey = apiKey; this.httpClient = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(30)) .build(); } private String request(String endpoint) throws IOException, InterruptedException { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + endpoint)) .header("X-CSCAPI-KEY", apiKey) .header("Content-Type", "application/json") .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 401) { throw new RuntimeException("Invalid API key. Please check your credentials."); } else if (response.statusCode() == 429) { throw new RuntimeException("Too many requests. Please try again later."); } else if (response.statusCode() != 200) { throw new RuntimeException("API error: " + response.statusCode()); } return response.body(); } public String getCountries() throws IOException, InterruptedException { return request("/countries"); } public String getStates(String countryCode) throws IOException, InterruptedException { return request("/countries/" + countryCode + "/states"); } } // Usage CountryStateCityAPI api = new CountryStateCityAPI("YOUR_API_KEY"); String countries = api.getCountries(); ``` ```csharp C# theme={null} using System; using System.Net.Http; using System.Threading.Tasks; public class CountryStateCityAPI { private readonly HttpClient _httpClient; private readonly string _baseUrl = "https://api.countrystatecity.in/v1"; public CountryStateCityAPI(string apiKey) { _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("X-CSCAPI-KEY", apiKey); _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json"); _httpClient.Timeout = TimeSpan.FromSeconds(30); } private async Task RequestAsync(string endpoint) { try { var response = await _httpClient.GetAsync($"{_baseUrl}{endpoint}"); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { throw new UnauthorizedAccessException("Invalid API key. Please check your credentials."); } else if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests) { throw new InvalidOperationException("Too many requests. Please try again later."); } response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException ex) { throw new Exception($"Request failed: {ex.Message}"); } } public async Task GetCountriesAsync() { return await RequestAsync("/countries"); } public async Task GetStatesAsync(string countryCode) { return await RequestAsync($"/countries/{countryCode}/states"); } public void Dispose() { _httpClient?.Dispose(); } } // Usage var api = new CountryStateCityAPI("YOUR_API_KEY"); var countries = await api.GetCountriesAsync(); ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" "time" ) type CountryStateCityAPI struct { apiKey string baseURL string client *http.Client } func NewCountryStateCityAPI(apiKey string) *CountryStateCityAPI { return &CountryStateCityAPI{ apiKey: apiKey, baseURL: "https://api.countrystatecity.in/v1", client: &http.Client{ Timeout: 30 * time.Second, }, } } func (api *CountryStateCityAPI) request(endpoint string) ([]byte, error) { req, err := http.NewRequest("GET", api.baseURL+endpoint, nil) if err != nil { return nil, err } req.Header.Set("X-CSCAPI-KEY", api.apiKey) req.Header.Set("Content-Type", "application/json") resp, err := api.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode == 401 { return nil, fmt.Errorf("invalid API key") } else if resp.StatusCode == 429 { return nil, fmt.Errorf("too many requests") } else if resp.StatusCode != 200 { return nil, fmt.Errorf("API error: %d", resp.StatusCode) } return io.ReadAll(resp.Body) } func (api *CountryStateCityAPI) GetCountries() ([]byte, error) { return api.request("/countries") } func (api *CountryStateCityAPI) GetStates(countryCode string) ([]byte, error) { return api.request("/countries/" + countryCode + "/states") } // Usage func main() { api := NewCountryStateCityAPI("YOUR_API_KEY") countries, err := api.GetCountries() if err != nil { fmt.Println("Error:", err) } } ``` ```ruby Ruby theme={null} require 'net/http' require 'json' require 'uri' class CountryStateCityAPI def initialize(api_key) @api_key = api_key @base_url = 'https://api.countrystatecity.in/v1' end private def request(endpoint) uri = URI("#{@base_url}#{endpoint}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.read_timeout = 30 request = Net::HTTP::Get.new(uri) request['X-CSCAPI-KEY'] = @api_key request['Content-Type'] = 'application/json' response = http.request(request) case response.code.to_i when 401 raise 'Invalid API key. Please check your credentials.' when 429 raise 'Too many requests. Please try again later.' when 200 JSON.parse(response.body) else raise "API error: #{response.code}" end end public def get_countries request('/countries') end def get_states(country_code) request("/countries/#{country_code}/states") end def get_cities(country_code, state_code = nil) endpoint = state_code ? "/countries/#{country_code}/states/#{state_code}/cities" : "/countries/#{country_code}/cities" request(endpoint) end end # Usage api = CountryStateCityAPI.new('YOUR_API_KEY') countries = api.get_countries us_states = api.get_states('US') ``` ## Real-World Use Cases ### 1. Complete Registration Form with Cascading Dropdowns Build a comprehensive user registration form with dependent location selectors: ```javascript React Example theme={null} import React, { useState, useEffect } from 'react'; const LocationSelector = ({ apiKey, onLocationChange }) => { const [countries, setCountries] = useState([]); const [states, setStates] = useState([]); const [cities, setCities] = useState([]); const [selectedCountry, setSelectedCountry] = useState(''); const [selectedState, setSelectedState] = useState(''); const [selectedCity, setSelectedCity] = useState(''); const [loading, setLoading] = useState(false); const api = new CountryStateCityAPI(apiKey); // Load countries on component mount useEffect(() => { const loadCountries = async () => { try { setLoading(true); const countriesData = await api.getCountries(); setCountries(countriesData); } catch (error) { console.error('Failed to load countries:', error); } finally { setLoading(false); } }; loadCountries(); }, []); // Load states when country changes useEffect(() => { if (selectedCountry) { const loadStates = async () => { try { setLoading(true); const statesData = await api.getStates(selectedCountry); setStates(statesData); setSelectedState(''); // Reset state selection setCities([]); // Clear cities setSelectedCity(''); // Reset city selection } catch (error) { console.error('Failed to load states:', error); } finally { setLoading(false); } }; loadStates(); } }, [selectedCountry]); // Load cities when state changes useEffect(() => { if (selectedCountry && selectedState) { const loadCities = async () => { try { setLoading(true); const citiesData = await api.getCities(selectedCountry, selectedState); setCities(citiesData); setSelectedCity(''); // Reset city selection } catch (error) { console.error('Failed to load cities:', error); } finally { setLoading(false); } }; loadCities(); } }, [selectedState]); return (
{loading &&
Loading...
}
); }; ``` ```python Flask Example theme={null} from flask import Flask, render_template, request, jsonify app = Flask(__name__) class LocationService: def __init__(self, api_key): self.api = CountryStateCityAPI(api_key) def get_countries_for_dropdown(self): """Format countries for HTML select dropdown""" try: countries = self.api.get_countries() return [ {'value': country['iso2'], 'text': f"{country.get('emoji', '')} {country['name']}"} for country in countries ] except Exception as e: print(f"Error fetching countries: {e}") return [] def get_states_for_dropdown(self, country_code): """Format states for HTML select dropdown""" try: states = self.api.get_states(country_code) return [ {'value': state['iso2'], 'text': state['name']} for state in states ] except Exception as e: print(f"Error fetching states: {e}") return [] location_service = LocationService('YOUR_API_KEY') @app.route('/') def registration_form(): countries = location_service.get_countries_for_dropdown() return render_template('registration.html', countries=countries) @app.route('/api/states/') def get_states(country_code): states = location_service.get_states_for_dropdown(country_code) return jsonify(states) @app.route('/register', methods=['POST']) def register_user(): user_data = { 'name': request.form.get('name'), 'email': request.form.get('email'), 'country': request.form.get('country'), 'state': request.form.get('state'), 'city': request.form.get('city') } if not all([user_data['country'], user_data['state'], user_data['city']]): return jsonify({'error': 'All location fields are required'}), 400 return jsonify({'message': 'User registered successfully', 'user': user_data}) if __name__ == '__main__': app.run(debug=True) ```
### 2. E-commerce Shipping Calculator Calculate shipping costs and delivery times based on geographical locations: ```javascript theme={null} class ShippingCalculator { constructor(apiKey) { this.api = new CountryStateCityAPI(apiKey); this.shippingRates = { domestic: { base: 5.99, perKg: 1.50, deliveryDays: '2-3' }, international: { asia: { base: 25.99, perKg: 8.50, deliveryDays: '7-10' }, europe: { base: 29.99, perKg: 9.50, deliveryDays: '7-12' }, americas: { base: 19.99, perKg: 7.50, deliveryDays: '5-8' }, africa: { base: 35.99, perKg: 12.50, deliveryDays: '10-15' } } }; } async calculateShipping(fromCountryCode, toCountryCode, weight) { try { const [fromCountry, toCountry] = await Promise.all([ this.api.getCountryDetails(fromCountryCode), this.api.getCountryDetails(toCountryCode) ]); // Domestic shipping if (fromCountryCode === toCountryCode) { const rate = this.shippingRates.domestic; return { cost: rate.base + (weight * rate.perKg), deliveryTime: rate.deliveryDays, type: 'Domestic', from: fromCountry.name, to: toCountry.name }; } // International shipping const toRegion = this.getRegion(toCountry.region); const rate = this.shippingRates.international[toRegion]; return { cost: rate.base + (weight * rate.perKg), deliveryTime: rate.deliveryDays, type: 'International', region: toRegion, from: fromCountry.name, to: toCountry.name }; } catch (error) { throw new Error(`Shipping calculation failed: ${error.message}`); } } getRegion(countryRegion) { const regionMap = { 'Asia': 'asia', 'Europe': 'europe', 'Americas': 'americas', 'Africa': 'africa' }; return regionMap[countryRegion] || 'international'; } } // Usage const calculator = new ShippingCalculator('YOUR_API_KEY'); const shippingInfo = await calculator.calculateShipping('US', 'CA', 2.5); console.log(`Shipping cost: $${shippingInfo.cost.toFixed(2)}`); console.log(`Delivery: ${shippingInfo.deliveryTime} business days`); ``` ### 3. Multi-Currency Price Display Display prices in local currencies based on user location: ```javascript theme={null} class PriceLocalization { constructor(apiKey) { this.api = new CountryStateCityAPI(apiKey); this.exchangeRates = {}; // Cache exchange rates this.baseCurrency = 'USD'; } async getLocalizedPrice(basePrice, userCountryCode) { try { const country = await this.api.getCountryDetails(userCountryCode); const currency = country.currency; if (currency === this.baseCurrency) { return { amount: basePrice, currency: currency, symbol: country.currency_symbol || '$', formatted: `${country.currency_symbol || '$'}${basePrice.toFixed(2)}` }; } // Get exchange rate (you'd typically use a service like exchangerates-api.io) const rate = await this.getExchangeRate(this.baseCurrency, currency); const localAmount = basePrice * rate; return { amount: localAmount, currency: currency, symbol: country.currency_symbol || '', formatted: `${country.currency_symbol || ''}${localAmount.toFixed(2)}`, exchangeRate: rate, originalPrice: basePrice, originalCurrency: this.baseCurrency }; } catch (error) { console.error('Price localization failed:', error); // Fallback to base price return { amount: basePrice, currency: this.baseCurrency, symbol: '$', formatted: `$${basePrice.toFixed(2)}` }; } } async getExchangeRate(from, to) { // This is a placeholder - use a real exchange rate service const cacheKey = `${from}-${to}`; if (this.exchangeRates[cacheKey] && this.exchangeRates[cacheKey].timestamp > Date.now() - 3600000) { return this.exchangeRates[cacheKey].rate; } // Mock exchange rate - replace with real service const mockRates = { 'EUR': 0.85, 'GBP': 0.73, 'CAD': 1.25, 'INR': 74.5 }; const rate = mockRates[to] || 1; this.exchangeRates[cacheKey] = { rate: rate, timestamp: Date.now() }; return rate; } } ``` ## Best Practices and Optimization ### Caching Strategy for Performance Implement intelligent caching to minimize API calls and improve performance: ```javascript theme={null} class OptimizedCountryStateCityAPI extends CountryStateCityAPI { constructor(apiKey, cacheOptions = {}) { super(apiKey); this.cache = new Map(); this.cacheTTL = cacheOptions.ttl || 3600000; // 1 hour default this.requestQueue = new Map(); // Prevent duplicate requests } async request(endpoint) { // Check cache first const cached = this.cache.get(endpoint); if (cached && Date.now() - cached.timestamp < this.cacheTTL) { return cached.data; } // Check if request is already in progress if (this.requestQueue.has(endpoint)) { return await this.requestQueue.get(endpoint); } // Make request and cache the promise const requestPromise = super.request(endpoint); this.requestQueue.set(endpoint, requestPromise); try { const data = await requestPromise; this.cache.set(endpoint, { data, timestamp: Date.now() }); return data; } finally { this.requestQueue.delete(endpoint); } } // Preload commonly used data async preloadEssentials() { const essentials = [ '/countries', '/countries/US/states', '/countries/GB/states', '/countries/CA/states' ]; await Promise.all(essentials.map(endpoint => this.request(endpoint))); } } ``` ## Need More Help? Complete API documentation with all endpoints and parameters. Official SDKs and community libraries for popular programming languages. Get help from our community and support team. All code examples are production-ready and include proper error handling and caching. Remember to replace `YOUR_API_KEY` with your actual API key from the [Country State City portal](https://app.countrystatecity.in). # Frequently Asked Questions Source: https://docs.countrystatecity.in/api/faq Common questions and solutions for using the Country State City API effectively ## Getting Started The process is simple: 1. Visit [app.countrystatecity.in](https://app.countrystatecity.in) 2. Sign up or log in to your account 3. Create your API key instantly You can make your first API call within minutes: ```bash theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Your API key is ready to use immediately after registration - no waiting period required. Yes. We maintain official live API clients for: * [TypeScript and JavaScript](/api/sdks/typescript) * [Python](/api/sdks/python#live-api-sdk) The REST API also works with any language that can make HTTP requests. We provide comprehensive [integration examples](/api/examples) for: * PHP * Java * C# * Go * Ruby ## Authentication & Security Authentication is done using API key headers. Include your API key in every request: ``` X-CSCAPI-KEY: YOUR_API_KEY ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) ``` ```bash cURL theme={null} curl -H 'X-CSCAPI-KEY: YOUR_API_KEY' \ 'https://api.countrystatecity.in/v1/countries' ``` **Never expose your API key in client-side code** such as browser JavaScript, mobile apps, or public repositories. **Safe practices:** * Use server-side applications or serverless functions * Store API keys in environment variables * Implement your own backend API that securely calls our API * Use build-time environment variables for static sites **Why this matters:** * API keys in client-side code are visible to anyone * Malicious users could extract and abuse your API key * This could lead to unexpected usage and charges Yes! You can manage your API keys through the [developer portal](https://app.countrystatecity.in): 1. Log into your account 2. Navigate to API key management 3. Generate a new key 4. Update your applications with the new key 5. Revoke the old key once migration is complete Keep both keys active during migration to avoid service interruption. A 401 error typically indicates an authentication issue: **Common causes and solutions:** * **Invalid API key**: Double-check your API key for typos * **Wrong header name**: Ensure you're using `X-CSCAPI-KEY` exactly * **Missing header**: Verify the header is being sent with every request * **Expired/revoked key**: Generate a new API key if needed **Test your authentication:** ```bash theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' \ -w "HTTP Status: %{http_code}\n" ``` A successful test returns HTTP 200 with country data. Exceeding your daily or monthly usage limit returns a `429 Too Many Requests` response: ```json theme={null} { "status": "error", "message": "Daily usage limit exceeded. Please try again tomorrow or contact support for higher limits.", "details": { "limit": 100, "period": "daily", "resetAt": "2026-08-14T00:00:00.000Z", "upgradeUrl": "https://app.countrystatecity.in/pricing", "tier": "community" } } ``` `details.resetAt` is an ISO 8601 UTC timestamp — next UTC midnight for a daily limit, the 1st of next UTC month for a monthly limit. Use it to schedule your retry instead of guessing a backoff window. **Best practices for handling rate limits:** * Read `details.resetAt` and wait until then before retrying * Cache responses to reduce API calls * Monitor usage proactively via the `X-CSC-Daily-Used`/`X-CSC-Daily-Limit` response headers on successful requests — they aren't returned on the `429` itself * Consider upgrading your plan if limits are consistently hit — plans start at \$5/month for higher limits. Visit [https://app.countrystatecity.in/pricing](https://app.countrystatecity.in/pricing) to compare plans. See [Errors & Rate Limits](/api/errors) for the monthly-limit variant, the usage headers, and limits per tier. A `403` means your API key authenticated fine, but the request itself isn't allowed — most commonly because the endpoint, parameter, or feature you're using isn't included in your plan: ```json theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "fuzzySearch", "currentTier": "community", "requiredTier": "supporter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` Check `details.requiredTier` for the minimum plan that unlocks it, and `details.upgradeUrl` to upgrade directly. `requiredTier` can be `null`. That means there is no higher active plan the API can safely recommend, or the live plan catalogue could not be read. The gate is still real. Use `details.upgradeUrl` (or the [pricing page](https://app.countrystatecity.in/pricing)) to compare the available plans. Not every `403` is a plan gate. A `403` raised for another reason returns `status` and `message` with no `details` object, so check `details` exists before reading `details.feature` or `details.requiredTier`. See [Errors & Rate Limits](/api/errors) for the full 403/429 reference. **Effective caching strategies:** * Cache country/state data for 24+ hours (rarely changes) * Cache city data for 12+ hours * Implement local fallbacks for critical data **Efficient request patterns:** * Fetch broader datasets when possible (all countries vs individual requests) * Use hierarchical endpoints strategically * Batch related requests together **Example caching implementation:** ```javascript theme={null} class APICache { constructor(ttl = 24 * 60 * 60 * 1000) { this.cache = new Map(); this.ttl = ttl; } get(key) { const item = this.cache.get(key); if (!item || Date.now() - item.timestamp > this.ttl) { return null; } return item.data; } set(key, data) { this.cache.set(key, { data, timestamp: Date.now() }); } } ``` ## Data & Endpoints **Countries include:** * ISO2 and ISO3 codes * Country name and native name * Phone code and currency information * Flag emoji and region information * Latitude and longitude coordinates **States include:** * State name and ISO2 code * Country association * State type (state, province, region, etc.) * Latitude and longitude coordinates **Cities include:** * City name and unique ID * Country and state association * Latitude and longitude coordinates * Timezone information (when available) Use the detailed endpoints (`/countries/{iso2}`) to get complete information including additional metadata. Our data is continuously maintained and updated: * **Country data**: Updated as needed for political changes, currency updates, etc. * **State/Province data**: Updated for administrative boundary changes * **City data**: Regular additions and corrections based on authoritative sources We implement automated validation and quality checks to ensure data accuracy. Major updates are announced through our changelog. The dataset includes 250 countries, 5,299+ states/provinces, and 153,765+ cities worldwide. No, for performance and efficiency reasons, our API is designed with hierarchical endpoints: * `/countries` - All countries * `/countries/{countryCode}/states` - States within a country * `/countries/{countryCode}/cities` - Cities within a country * `/countries/{countryCode}/states/{stateCode}/cities` - Cities within a state **Why this design:** * Faster response times for specific data * Reduced bandwidth usage * Better caching opportunities * More manageable response sizes Requesting all cities globally would result in 153,765+ records, which would be slow and consume significant bandwidth. Yes! Latitude and longitude coordinates are included for: * ✅ **Countries**: Approximate center coordinates * ✅ **States/Provinces**: Administrative center coordinates * ✅ **Cities**: City center coordinates **Example city response:** ```json theme={null} { "id": 12345, "name": "Los Angeles", "latitude": "34.05223", "longitude": "-118.24368", "country_code": "US", "state_code": "CA" } ``` Coordinates are provided as strings for precision and can be easily converted to numbers for calculations. Yes, we follow international standards: **Country Codes:** * ISO 3166-1 alpha-2 (2-letter codes like "US", "GB") * ISO 3166-1 alpha-3 (3-letter codes like "USA", "GBR") **Currency Codes:** * ISO 4217 standard (3-letter codes like "USD", "EUR") **Phone Codes:** * ITU-T E.164 standard international calling codes This ensures compatibility with other international systems and databases. ## Integration & Development We strongly recommend against making direct API calls from browser applications due to security concerns with exposing API keys. **Recommended approaches:** 1. **Server-side proxy**: Create your own API endpoint that calls our API 2. **Serverless functions**: Use services like Vercel Functions, Netlify Functions, or AWS Lambda 3. **Static site generation**: Fetch data at build time for static sites **Why avoid direct browser calls:** * API keys are visible in browser developer tools * Risk of key theft and unauthorized usage * Potential CORS restrictions Implement cascading dropdowns by chaining API calls based on user selections: **React example pattern:** ```javascript theme={null} const [selectedCountry, setSelectedCountry] = useState(''); const [states, setStates] = useState([]); const [cities, setCities] = useState([]); // Load states when country changes useEffect(() => { if (selectedCountry) { loadStates(selectedCountry); setCities([]); // Clear cities when country changes } }, [selectedCountry]); const loadStates = async (countryCode) => { const statesData = await api.getStates(countryCode); setStates(statesData); }; ``` See our comprehensive cascading dropdown implementation guide Implement comprehensive error handling for different scenarios: **Error types and handling:** ```javascript theme={null} async function makeAPIRequest(endpoint) { try { const response = await fetch(endpoint, { headers }); if (response.status === 401) { throw new Error('INVALID_API_KEY'); } else if (response.status === 404) { throw new Error('NOT_FOUND'); } else if (!response.ok) { throw new Error('API_ERROR'); } return await response.json(); } catch (error) { if (error.message === 'INVALID_API_KEY') { // Notify user to check API configuration console.error('Invalid API key configuration'); } throw error; } } ``` **Best practices:** * Always check response status codes * Implement retry logic for temporary failures * Provide meaningful error messages to users * Log errors for debugging purposes Yes, caching is highly recommended and beneficial: **Recommended cache durations:** * **Countries**: 24-48 hours (very stable data) * **States**: 12-24 hours (occasionally updated) * **Cities**: 6-12 hours (more frequently updated) **Implementation strategies:** ```javascript theme={null} // Browser localStorage const cacheKey = 'countries_data'; const cachedData = localStorage.getItem(cacheKey); const isExpired = Date.now() - cachedData?.timestamp > 24 * 60 * 60 * 1000; if (cachedData && !isExpired) { return JSON.parse(cachedData).data; } // Server-side caching (Redis, Memcached, etc.) const redis = require('redis'); const client = redis.createClient(); const cached = await client.get('countries'); if (cached) return JSON.parse(cached); const data = await fetchFromAPI(); await client.setex('countries', 86400, JSON.stringify(data)); // 24h TTL ``` Caching reduces API usage, improves performance, and provides offline fallbacks. ## Troubleshooting This can happen for several reasons: **Common causes:** 1. **Incorrect country/state codes**: Verify you're using the correct ISO codes 2. **Case sensitivity**: Country codes should be uppercase ("US", not "us") 3. **State code format**: Use 2-letter state codes when available 4. **Data availability**: Some territories may have incomplete subdivision data **Debugging steps:** ```javascript theme={null} // First, verify the country exists const countries = await api.getCountries(); const country = countries.find(c => c.iso2 === 'US'); console.log('Country found:', country); // Then check states const states = await api.getStates('US'); console.log('States count:', states.length); ``` Use our detailed country endpoint to verify exact country and state codes available. **Performance optimization strategies:** 1. **Implement caching** (most important) 2. **Use specific endpoints** instead of filtering large datasets 3. **Parallel requests** for independent data 4. **CDN/Edge caching** for static geographical data **Example parallel requests:** ```javascript theme={null} // Instead of sequential requests const countries = await api.getCountries(); const states = await api.getStates('US'); // Use parallel requests const [countries, states] = await Promise.all([ api.getCountries(), api.getStates('US') ]); ``` **Server-side optimizations:** * Use HTTP/2 for multiplexed requests * Implement connection pooling * Add response compression This is typically due to caching issues or incorrect API usage: **Check these common issues:** 1. **Browser caching**: Different cache states in development 2. **API versioning**: Ensure you're using the same API version 3. **Mixed endpoints**: Don't mix different API base URLs 4. **Concurrent updates**: Our data is occasionally updated **Troubleshooting steps:** ```javascript theme={null} // Add request debugging console.log('Request URL:', fullUrl); console.log('Request headers:', headers); console.log('Response status:', response.status); console.log('Response headers:', [...response.headers.entries()]); ``` **Force fresh data:** ```javascript theme={null} // Add cache-busting parameter for testing const url = `${baseUrl}?_t=${Date.now()}`; ``` If issues persist, contact support with specific request/response examples. **Testing strategy:** 1. **Start with cURL** to verify basic connectivity 2. **Test each endpoint** individually 3. **Verify error handling** with invalid inputs **Basic connectivity test:** ```bash theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' \ -w "HTTP Status: %{http_code}\n" ``` **Automated testing example:** ```javascript theme={null} describe('Country State City API', () => { test('should fetch countries successfully', async () => { const countries = await api.getCountries(); expect(countries).toBeInstanceOf(Array); expect(countries.length).toBeGreaterThan(0); expect(countries[0]).toHaveProperty('iso2'); }); test('should handle invalid country code', async () => { await expect(api.getStates('INVALID')).rejects.toThrow(); }); }); ``` A successful countries request should return 250 countries with proper ISO codes. ## Still Need Help? Comprehensive integration examples and real-world use cases Complete API documentation with all endpoints and parameters Get help from our support team Can't find what you're looking for? [Contact our support team](mailto:api@countrystatecity.in) with specific questions about your integration or use case. # Field Filtering and Sorting Source: https://docs.countrystatecity.in/api/field-filtering-and-sorting Reduce payload size and order results with ?fields= and ?sort= on every geographic endpoint Every geographic endpoint accepts two query parameters that let you shape the response payload without changing the URL: * **`?fields=`** — return only the fields you ask for. Trim large list responses down to what your UI actually renders. * **`?sort=`** — order list results by one or more fields, ascending or descending. **Availability:** Both parameters require the **Starter plan or above**. Detail endpoints accept `?fields=`; list endpoints accept both `?fields=` and `?sort=`. Which fields you can select or sort by still depends on your plan's data access: Starter returns the basic field set, so examples on this page that use `population` or other full-access fields need **Supporter+**. Filtering and sorting are applied **server-side after cache**, so a typical request still takes under 10 ms even on large responses. The full payload is cached once; each `?fields=`/`?sort=` permutation receives its own ETag. *** ## `?fields=` — request only the columns you need Pass a comma-separated list of field names. The response includes only those fields, plus `id` (always returned so responses are never empty). ```bash theme={null} # Return only name + iso2 for every country curl -X GET 'https://api.countrystatecity.in/v1/countries?fields=name,iso2' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```json 200 - Trimmed response theme={null} [ { "id": 1, "name": "Afghanistan", "iso2": "AF" }, { "id": 2, "name": "Albania", "iso2": "AL" }, { "id": 3, "name": "Algeria", "iso2": "DZ" } ] ``` ### Behaviour rules | Input | Behaviour | | - | - | | Field name is **unknown** for that entity | `400 Bad Request` listing valid fields | | Field is **valid but outside your tier** | Silently dropped — response stays a 200, that field just doesn't appear | | Field list is **empty** (`?fields=`) | Treated as if the parameter wasn't sent (no filtering). Still trips the Starter+ check. | | `id` not in your list | Added automatically — every response item carries `id` | | `?fields=` sent multiple times | `400 Bad Request` — repeated params arrive as an array, which is rejected. Send one comma-separated list. | ### Per-entity field lists The fields available to `?fields=` are the same as the columns returned by each endpoint (after tier gating). For the full lists, see the **Tier-Based Field Availability** table at the bottom of every endpoint page. *** ## `?sort=` — order list results Pass a comma-separated list of `field` or `field:asc|desc` tokens. Sort is applied to the list **after filtering**. ```bash theme={null} # Largest countries by population, descending curl -X GET 'https://api.countrystatecity.in/v1/countries?fields=name,population&sort=population:desc' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```json 200 - Top of response theme={null} [ { "id": 45, "name": "China", "population": 1444216107 }, { "id": 101, "name": "India", "population": 1380004385 }, { "id": 233, "name": "United States", "population": 331002651 } ] ``` ### Multi-field sort Chain fields with commas — earlier tokens take precedence on ties. ```bash theme={null} # Country first (A→Z), population second (largest first within each country) curl -X GET 'https://api.countrystatecity.in/v1/states?sort=country_code:asc,population:desc' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ### Sortable fields per entity Only fields with comparable scalar values are sortable. Translation blobs, JSON columns, and similar non-scalar columns are not. | Entity | Sortable fields | | - | - | | countries | `id`, `name`, `iso2`, `iso3`, `population`, `gdp`, `area_sq_km`, `latitude`, `longitude` | | states | `id`, `name`, `iso2`, `country_code`, `population`, `latitude`, `longitude` | | cities | `id`, `name`, `state_code`, `country_code`, `population`, `latitude`, `longitude` | | regions | `id`, `name` | | subregions | `id`, `name`, `region_id` | ### Behaviour rules | Input | Behaviour | | - | - | | Field is not in the sortable list above | `400 Bad Request` listing the valid fields | | Field is sortable but **outside your tier** (e.g. `population` on Starter) | `400 Bad Request` — sort surfaces a clear error instead of silently dropping, so you don't get nonsense order | | Direction is anything other than `asc` or `desc` (case-insensitive) | `400 Bad Request` | | Token has more than one `:` (e.g. `name:asc:extra`) | `400 Bad Request` — guards against silently swallowed typos | | Empty `?sort=` | Treated as if the parameter wasn't sent. Still trips the Starter+ check. | | `?sort=` sent on a detail endpoint (e.g. `/countries/IN`) | Ignored — sort only applies to list endpoints | | Numeric strings (`latitude`, `longitude`) | Compared numerically, not lexicographically. `"9.0"` sorts before `"10.0"`. | | `null` values | Sorted first in `asc`, last in `desc` | ### Whitespace Whitespace inside a sort token is tolerated. Both `sort=name:desc` and `sort=name: desc` are valid. *** ## Combining `?fields=` and `?sort=` You can use both together. Sort is applied first, then fields are trimmed. ```bash theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities?fields=name,population&sort=population:desc' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` You can sort by a field you didn't include in `?fields=` — the sort happens on the full row, then unwanted columns are dropped. ```bash theme={null} # Sort by population (not returned), keep only the city name curl -X GET 'https://api.countrystatecity.in/v1/countries/IN/cities?fields=name&sort=population:desc' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` *** ## Caching | Header | Meaning | | - | - | | `Cache-Control: public, max-age=3600` | Clients and the edge may reuse the response for 1 h. (Separately, the server keeps the unfiltered payload for 24 h, or 1 h for searches.) | | `ETag` | Each `?fields=`/`?sort=` permutation gets its own ETag so conditional requests work per-permutation | | `X-Cache: HIT` / `MISS` | Whether the **underlying** entity payload was served from cache. Filtering and sorting always run on the response — they're cheap relative to network round-trip. | *** ## Error responses `?fields=` and `?sort=` errors use the `status`/`message`/`details` envelope shown below. A small set of geographical `404`s return a bare `{"error": "..."}` object instead. See [Errors & Rate Limits](/api/errors) for both shapes and how to read either one. ```json 400 - Unknown field in ?fields= theme={null} { "status": "error", "message": "Unknown field(s): fakeColumn. Valid fields: id, name, iso2, iso3, ..." } ``` ```json 400 - Non-sortable field theme={null} { "status": "error", "message": "Field \"timezones\" is not sortable. Sortable fields for countries: id, name, iso2, iso3, population, gdp, area_sq_km, latitude, longitude" } ``` ```json 400 - Sort field outside tier theme={null} { "status": "error", "message": "Field \"population\" is not available on your current plan." } ``` ```json 400 - Malformed sort token theme={null} { "status": "error", "message": "Invalid sort token \"name:asc:extra\". Expected \"field\" or \"field:asc|desc\"." } ``` ```json 403 - Feature not on plan theme={null} { "status": "error", "message": "This feature is not available on your current plan.", "details": { "feature": "fieldsFiltering", "currentTier": "community", "requiredTier": "starter", "upgradeUrl": "https://app.countrystatecity.in/pricing" } } ``` *** ## When to use which parameter Use `?fields=` to keep only the columns the UI renders. A country selector usually needs `id`, `name`, `iso2`, `emoji` — that's roughly 1 KB per request instead of 50+ KB for the full response. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries?fields=name,iso2,emoji' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Use `?sort=` to push the work server-side. Sorting thousands of cities by population in your client is slow and ships a lot of data you'll discard. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/cities?sort=population:desc&fields=name,state_code,population' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Combine `?fields=` and `?sort=` to ship exactly the rendered columns in the rendered order. Pair with client-side slicing for pagination. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/states?sort=name:asc&fields=name,iso2,country_code' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ## Related Topics * [Authentication](/api/authentication) — how to send `X-CSCAPI-KEY` * [Pricing](https://app.countrystatecity.in/pricing) — which plans include filtering and sorting * Every endpoint page links back to this guide # GraphQL API Source: https://docs.countrystatecity.in/api/graphql POST https://api.countrystatecity.in/v1/graphql Query countries, states, cities, regions, and subregions with GraphQL instead of REST The GraphQL endpoint covers the same geographic data as the REST API — countries, states, cities, regions, and subregions — through a single endpoint. Ask for exactly the fields you need, and resolve nested relationships (a country's states, a state's cities) in one request instead of chaining several REST calls yourself. **Available on the Professional and Business plans.** [Compare plans](https://countrystatecity.in/pricing?source=docs\&campaign=graphql) to add GraphQL to your API key. ## When to use it Reach for GraphQL when a screen needs a nested shape — for example a country with its states and each state's cities — in one round trip. For a single flat list, the REST endpoints work just as well: they return the complete list in one response and are simpler to cache by URL. ## Authentication Your API key, the same header every REST endpoint uses. ## Sending a query Send a `POST` request with a JSON body containing a `query` string (and optional `variables`): ```bash cURL theme={null} curl -X POST 'https://api.countrystatecity.in/v1/graphql' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"query": "{ country(ciso: \"US\") { name capital currency } }"}' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/graphql', { method: 'POST', headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ query: '{ country(ciso: "US") { name capital currency } }', }), }); const { data } = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://api.countrystatecity.in/v1/graphql', json={'query': '{ country(ciso: "US") { name capital currency } }'}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'}, ) response.raise_for_status() data = response.json()['data'] ``` ```json 200 - Response theme={null} { "data": { "country": { "name": "United States", "capital": "Washington", "currency": "USD" } } } ``` ## Exploring the schema A `GET` request to the same endpoint with an `Accept` header that includes `text/html` (what a browser sends) returns GraphiQL, an interactive schema explorer with autocomplete and inline docs for every type and field. The page is protected like any query: the request that loads it must carry your `X-CSCAPI-KEY` header, and your plan must include GraphQL. Typing the URL into the address bar sends no key, so it returns `401` instead of the explorer. To open it: 1. Install a browser extension that adds custom request headers, and set `X-CSCAPI-KEY: YOUR_API_KEY` for `api.countrystatecity.in`. 2. Open `https://api.countrystatecity.in/v1/graphql`. 3. In GraphiQL's **Headers** panel, add `{"X-CSCAPI-KEY": "YOUR_API_KEY"}`. GraphiQL sends its queries with the headers from this panel. Alternatively, use an API client with GraphQL support, such as Postman or Insomnia, with the header set. It can load the schema by introspection over an ordinary `POST`. Loading the page and each request GraphiQL sends count toward your daily and monthly usage like any other request. If your key restricts allowed domains or IPs, GraphiQL's requests are checked against those restrictions too. ## Nested queries Fields that return related entities — `Country.states`, `State.cities`, `Region.subregions`, `Subregion.countries` — resolve in the same HTTP request, so one round trip replaces a chain of REST calls. Each nested list returns a page (20 rows per parent unless you pass `limit`), and every parent row adds a lookup to the [query budget](#query-budget), so keep the outer lists small: ```graphql theme={null} { countries(searchQuery: "United", limit: 2) { name states(limit: 10) { name cities(limit: 10) { name } } } } ``` This returns the first 2 matching countries (sorted by name), up to 10 states for each, and up to 10 cities for each of those states: a page at every level, not every match. It is estimated at 23 lookups (1 for `countries`, 1 per country for `states`, 1 per state for `cities`) and 222 rows, well inside the budget. Nested lookups are not merged into a single database query. The server still fetches each parent's children separately; it runs those lookups a limited number at a time in parallel and reuses a result when the same parent and page appear twice in one request. So the cities of 20 different states still cost 20 lookups. ## Fields by plan Two plan settings apply, separately: * **The GraphQL feature** decides whether you can call `/v1/graphql` at all. Professional and Business include it, and a custom plan can too. Supporter, for example, has full field access over REST but no GraphQL. * **Your data access level** decides which fields come back, using the same per-field filtering as REST. Professional and Business have **full** access, so every field in the schema is returned. A custom plan can have a lower level. A field above your access level returns `null` rather than an error, so one query can safely ask for fields you might not have: | Type | Basic | Full adds | | - | - | - | | `Country` | `id`, `name`, `iso2`, `iso3`, `phonecode`, `capital`, `currency`, `native`, `emoji`, `latitude`, `longitude`, `region`, `region_id`, `subregion`, `subregion_id`, `timezones` | `numeric_code`, `currency_name`, `currency_symbol`, `tld`, `nationality`, `population`, `gdp`, `area_sq_km`, `postal_code_format`, `postal_code_regex`, `emojiU`, `translations`, `wikiDataId` | | `State` | `id`, `name`, `iso2`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone` | `fips_code`, `iso3166_2`, `type`, `level`, `parent_id`, `native`, `population`, `translations`, `wikiDataId` | | `City` | `id`, `name`, `kind` | `state_id`, `state_code`, `country_id`, `country_code`, `latitude`, `longitude`, `timezone`, `population`, `type`, `level`, `parent_id`, `native`, `translations`, `wikiDataId` | | `Region` | `id`, `name` | `wikiDataId`, `translations` | | `Subregion` | `id`, `name`, `region_id` | `wikiDataId`, `translations` | A custom plan can also be set to an intermediate level that returns the Full fields except `translations` (and, for countries, states and cities, `wikiDataId`). Only fields defined in the GraphQL schema can be requested. REST's `localized_name` and `matched_locale` are not in it, and there are no `locale` or `include_translations` arguments: asking for `localized_name` is a validation error, not `null`. To show a name in another language, select `translations` (a JSON-encoded string keyed by language code) and pick the language in your app, or use the REST [`locale` parameter](/api/localization). ## Available queries | Query | Returns | | - | - | | `countries(searchQuery, limit, offset)` | All countries | | `country(ciso)` | One country by numeric ID or ISO2 code | | `states(country, searchQuery, limit, offset)` | States in one country | | `allStates(searchQuery, limit, offset)` | All states globally | | `state(country, iso)` | One state by country and state code | | `cities(country, state, searchQuery, limit, offset)` | Cities in a country, optionally narrowed to one state | | `city(id)` | One city by numeric ID | | `regions(searchQuery, limit, offset)` | All regions | | `region(id)` | One region by numeric ID | | `subregions(region, searchQuery, limit, offset)` | Subregions in one region (numeric region ID) | | `subregion(id)` | One subregion by numeric ID | `country(ciso)` takes a numeric ID passed as a string, or an ISO2 code. An ISO3 code such as `"USA"` passes validation but matches nothing, so the query returns `null`; convert it first with [Lookup Country by ISO Code](/api/endpoints/lookup-country-by-iso). The `country` argument of `states`, `state`, and `cities` takes an ISO2 code, and `state(iso)` and `cities(state)` take the state's code (for example `CA`). `searchQuery` (2 to 100 characters) keeps rows whose name (or, for countries, states, and cities, native name) contains it, ignoring case. A single-item query that matches nothing returns `null` with no error. `city(id)` has no REST equivalent — it's the one query only available through GraphQL, since REST only ever exposed cities scoped to a country or state. ## Limits Each HTTP request to `/v1/graphql` counts as one request against your plan's daily and monthly limits, however many lookups it runs, including a request rejected for exceeding the query budget. ### Pagination Every list field, at the root or nested, takes `limit` and `offset`. They are arguments on the field inside your query, not URL query parameters. | List field | Default `limit` | Maximum `limit` | | - | - | - | | Root queries: `countries`, `states`, `allStates`, `cities`, `regions`, `subregions` | 1,000 | 1,000 | | Nested fields: `Country.states`, `State.cities`, `Region.subregions`, `Subregion.countries` | 20 per parent | 1,000 | `offset` defaults to `0`. Lists are sorted by name, then ID, so stepping `offset` by `limit` pages through the full list. A `limit` above 1,000, or a negative `limit` or `offset`, is rejected rather than capped: it fails that field with a `BAD_REQUEST` error (see [Errors](#errors)). `limit: 0` returns an empty list. Unlike the REST list endpoints, a GraphQL list never returns more than one page. Watch the nested default in particular: `{ country(ciso: "US") { states { name } } }` returns only the first 20 states. Ask for more with `states(limit: 100)`, or page with `offset`. ### Query budget Before running a query, the API estimates its cost from the `limit` of every list field (the value you pass, or the default above), not from how many rows actually match: * **Lookups:** every field that returns an object or a list of objects costs one lookup per parent row it runs under. Scalar fields such as `name` are free. * **Rows:** every list field adds its `limit` multiplied by the number of parent rows. One query may use at most **50 lookups** and **25,000 rows**. A query over either limit is rejected with HTTP `400` before any data is fetched: ```json 400 - Query too expensive theme={null} { "errors": [ { "message": "Query is too expensive: its estimated cost exceeds the budget (1001 data lookups, 21000 rows; limits: 50 lookups, 25000 rows). Add or lower `limit:` arguments on list fields, remove a level of nesting, or split the request into several queries.", "extensions": { "code": "QUERY_TOO_EXPENSIVE", "statusCode": 400, "estimatedFetches": 1001, "estimatedRows": 21000, "maxFetches": 50, "maxRows": 25000 } } ] } ``` That response is for `{ countries { states { name } } }`: `countries` defaults to 1,000 rows, so `states` would run 1,000 lookups. To stay within the budget, add or lower `limit` on the outer lists, drop a level of nesting, or split the work into several requests. For example, fetch a page of countries first, then request each country's states and cities in a separate query, using `offset` for further pages. Queries are also limited to 5 levels of nesting. That is exactly the deepest chain the schema allows (`regions` → `subregions` → `countries` → `states` → `cities`), so in practice the budget is the limit you will reach. ## Errors The HTTP status and body depend on where a request fails: | Where it fails | HTTP status | Body | | - | - | - | | Before GraphQL runs: for example a missing or invalid key, a plan without GraphQL, or a usage limit reached | `401`, `403`, `429` | The REST error envelope (`status`, `message`, `details`); see [Errors & Rate Limits](/api/errors) | | Query budget (`QUERY_TOO_EXPENSIVE`), or a document too large to analyze (`QUERY_TOO_COMPLEX`) | `400` | `errors` only, no `data` | | Syntax error, or a field or argument that isn't in the schema | `200`, or `400` if you send `Accept: application/graphql-response+json` | `errors` only, no `data` | | Inside a field: invalid argument, a plan feature the field needs, temporary overload | `200` | `errors` plus whatever `data` could be resolved | Field errors carry `extensions.code` and `extensions.statusCode`: `BAD_REQUEST` (`400`) for an invalid argument such as a malformed country code or a `limit` over 1,000; `FORBIDDEN` (`403`) when your plan lacks a feature that field needs, with `extensions.details` naming the feature (standard Professional and Business plans include every feature these queries use); and `API_ERROR` for anything else. A `503` with `GraphQL is busy. Please retry shortly.` is temporary, so retry after a short wait. The failed field becomes `null`. When that field is nullable (the single-item queries such as `country`, and the nested lists such as `Country.states`), the rest of `data` still comes back. This response is for `{ country(ciso: "US") { name states(limit: 5000) { name } } }`: ```json 200 - Partial data theme={null} { "errors": [ { "message": "limit must not exceed 1000.", "extensions": { "code": "BAD_REQUEST", "statusCode": 400 } } ], "data": { "country": { "name": "United States", "states": null } } } ``` Root list queries (`countries`, `states`, `cities`, and the others) can't be `null`, so an error in one of them makes all of `data` `null`. ## Start querying Choose Professional or Business to add GraphQL to your API key. Handle the `401`, `403`, and `429` responses returned before a query runs. # Country State City API Source: https://docs.countrystatecity.in/api/introduction Access comprehensive geographical data with our RESTful API featuring 250 countries, 5,299+ states, and 153,765+ cities ## Overview The Country State City API provides developers with comprehensive access to geographical data covering 250 countries, 5,299+ states/provinces, and over 153,000 cities worldwide. Our RESTful API is designed for high performance, reliability, and ease of integration. Register and start accessing geographical data immediately. ## Key Features Access complete geographical hierarchies from countries down to individual cities with detailed metadata. Optimized endpoints with efficient data structures and caching for fast response times. RESTful design with consistent responses, comprehensive error handling, and extensive documentation. Regular updates ensure data accuracy with automated validation and quality checks. ## Quick Start Register at [our portal](https://app.countrystatecity.in) to obtain your free API key. Use your API key to fetch all countries: ```bash cURL theme={null} curl -X GET 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const countries = await response.json(); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/countries', headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) countries = response.json() ``` Browse our comprehensive endpoint documentation to integrate geographical data into your application. All endpoints return consistent JSON responses with proper HTTP status codes. ## Base URL ``` https://api.countrystatecity.in/v1 ``` ## Authentication All API requests require authentication using an API key sent in the request header: ``` X-CSCAPI-KEY: YOUR_API_KEY ``` Never expose your API key in client-side code or public repositories. Use environment variables or server-side configurations to keep your key secure. ## Available Endpoints Get all countries or specific country details with ISO codes, currencies, and regional information. Access states, provinces, and regions with filtering by country and detailed geographical data. Retrieve cities with coordinates, timezone data, and hierarchical filtering by country and state. ### Countries Endpoints * [Get All Countries](/api/endpoints/get-all-countries) - Retrieve list of all countries * [Get Country Details](/api/endpoints/get-country-details) - Get detailed information for a specific country ### States Endpoints * [Get All States](/api/endpoints/get-all-states) - Retrieve list of all states * [Get States by Country](/api/endpoints/get-states-by-country) - Get states within a specific country * [Get State Details](/api/endpoints/get-state-details) - Get detailed information for a specific state ### Cities Endpoints * [Get Cities by Country](/api/endpoints/get-cities-by-country) - Get cities within a specific country * [Get Cities by State](/api/endpoints/get-cities-by-state) - Get cities within a specific state ## Response Format All API endpoints return JSON responses with consistent structure: ### Success Response ```json theme={null} { "data": [...], // Array of results or single object "total": 250, // Total count (for list endpoints) "page": 1 // Current page (for paginated endpoints) } ``` ### Error Response ```json theme={null} { "error": "Unauthorized. You shouldn't be here.", "code": 401, "message": "Invalid or missing API key" } ``` ## HTTP Status Codes | Code | Description | | - | - | | 200 | Success - Request completed successfully | | 401 | Unauthorized - Invalid or missing API key | | 404 | Not Found - Resource doesn't exist | | 500 | Internal Server Error - Something went wrong on our end | For 5xx errors, implement exponential backoff in your retry logic. Most issues are temporary and resolve quickly. ## Need Help? Complete integration examples in multiple programming languages. Common questions, troubleshooting, and community support. # Localized Place Names Source: https://docs.countrystatecity.in/api/localization Display and search country, state, city, region, and subregion names in another language The API can return a place name in the language your user expects. You do not need to download and maintain a large translation file in your app. Use `locale` when you need one display name. Use `include_translations=true` only when you need every available translation. **Available on Supporter, Professional, and Business plans.** [Compare plans](https://countrystatecity.in/pricing?source=docs\&campaign=localized_place_names) to add localized names to your API key. ## Get one localized name Add `locale` to a country, state, city, region, subregion, fuzzy search, autocomplete, or nearby search request. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/states?locale=hi' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Each result keeps its normal English `name` and stable `id`. The API adds: The best name for the requested language. The language that supplied the value, or `native`/`en` when the API used a fallback. ```json theme={null} [ { "id": 4008, "name": "Maharashtra", "localized_name": "महाराष्ट्र", "matched_locale": "hi" } ] ``` ### Fallback order When a translation is missing, the API checks these names in order: 1. The exact locale, such as `pt-BR` 2. The base language, such as `pt` 3. The place's native name 4. The English `name` The API never replaces `name` or `id`, so your stored references remain stable. ## Get every translation Add `include_translations=true` when your app needs the complete stored value. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN?include_translations=true' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` The `translations` field is a JSON string. Parse it before reading a language value: ```javascript theme={null} const translations = JSON.parse(country.translations || '{}'); console.log(translations.ja); ``` Translations are hidden by default because returning every language for every row makes large responses much bigger. If you also use [`fields`](/api/field-filtering-and-sorting), list the fields you want. For example, `fields=name,translations` asks for the English name and the raw translations value. ## Search using translated names [Fuzzy Search](/api/endpoints/fuzzy-search) and [Autocomplete](/api/endpoints/get-search-autocomplete) can find a place from a stored translation when the query has at least 3 characters. For example, the Japanese query `ムンバイ` can find Mumbai. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/search/autocomplete?q=%E3%83%A0%E3%83%B3%E3%83%90%E3%82%A4&type=city&locale=ja' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Autocomplete reports `matched_field: "translation"` for a translation-only match. A fuzzy-search translation match can have a low `match_score` because that score still compares the query with the English and native names. `locale` controls the name returned for display. It does not limit search to one language; translated search checks all stored translations. [Nearby Search](/api/endpoints/get-search-nearby) also accepts `locale` and `include_translations`, but it finds places from coordinates rather than translated text. ## Validation and plan behavior * Use a locale such as `fr`, `hi`, `pt-BR`, or `zh-CN`. Invalid values return `400`. * `include_translations` accepts only `true` or `false`. Other values return `400`. * On regular geography routes, a lower plan still receives the normal response, without `localized_name`, `matched_locale`, or `translations`. * Fuzzy search, autocomplete, and nearby search have their own plan requirements and may return `403` before running the search. ## Start using localized names Choose Supporter, Professional, or Business when you are ready to serve translated place names without hosting the translation data yourself. See the request limits and features included with each plan. Build a search box with ready-to-display localized labels. # No-Code Integrations Source: https://docs.countrystatecity.in/api/no-code-integrations Connect Country State City API to your favorite platforms without writing code ## Notion Not every workflow requires custom development. If you want to bring Country, State, and City data into Notion without writing code, you can use [Note API Connector](https://noteapiconnector.com). With just a few clicks, Note API Connector lets you: * Connect to the Country State City API * Map fields into your Notion database * Keep your location reference tables updated automatically This approach is ideal for teams who rely on Notion as a central workspace and prefer a no-code setup. Instead of building scripts or maintaining infrastructure, you get a simple way to sync geographic data directly where your team works. Access the Country State City API integration in the Notion Integrations directory ### Benefits of No-Code Integration Instead of building scripts or maintaining infrastructure, you get a simple way to sync geographic data directly where your team works. This approach saves development time and reduces maintenance overhead while giving you access to the same comprehensive geographic data. ## Other No-Code Platforms The Country State City API can be integrated with various no-code platforms. If you've built an integration for another platform, [contact us](mailto:api@countrystatecity.in) to have it featured here. ### Popular No-Code Platforms The API works well with: * **Zapier** - Connect to 5,000+ apps with automated workflows * **Make (Integromat)** - Build complex automation scenarios * **n8n** - Self-hosted workflow automation * **Bubble** - No-code app development platform * **Airtable** - Using API connectors and scripts All these platforms support custom API integration through HTTP request modules. Use the same authentication pattern: add the `X-CSCAPI-KEY` header with your API key. ## Need Help? Review the complete API documentation for endpoint details Contact us for integration assistance # SDKs & Packages Source: https://docs.countrystatecity.in/api/sdks Official client packages and community libraries for the Country State City API ## Official Packages The Country State City ecosystem provides live REST API clients for TypeScript and Python, plus offline data packages for JavaScript/TypeScript and Python. Use a live client when you need managed, current data, server-side search, or plan-aware errors. Use an offline package when your application must run without an API key or network access. Six packages for Node.js and browser environments, including the API-backed CLI. Live sync and async API client, plus type-safe offline data packages. Live REST API client for Node.js and browser — search, usage tracking, and always-current data over HTTP. Official typed REST API client with synchronous and asynchronous interfaces. *** ## Coming Soon We're currently developing official SDKs for the following languages and platforms: Modern PHP SDK with PSR compliance Ruby gem for seamless Rails integration Idiomatic Go client with context support Java SDK compatible with Java 8+ C# SDK for .NET Core and .NET Framework Native iOS/macOS SDK ## Get Notified Want to be notified when another language SDK is available? [Contact our support team](mailto:api@countrystatecity.in) to join the early access list. ## In the Meantime For languages without an official SDK, you can integrate with our API using: Use your favorite HTTP client library to make requests directly to our API endpoints. Check our [API Reference](/api/introduction) for detailed endpoint documentation. Generate a client library in your preferred language using our [OpenAPI specification](https://github.com/OpenAPITools/openapi-generator). Check our [GitHub](https://github.com) for community-contributed libraries and examples. ## Request a Language Don't see your preferred language listed? Let us know! We prioritize SDK development based on community feedback. Tell us which language or platform you'd like to see supported # CLI Source: https://docs.countrystatecity.in/api/sdks/cli Official @countrystatecity/cli — search, explore, and generate code from the terminal, with typo-tolerant fuzzy search and live plan data Official CLI for the Country State City API — search, explore, and generate code from geographic data, right from your terminal. [Get a free API key](https://app.countrystatecity.in?source=sdk_docs\&campaign=sdk_api_migration\&package=cli) to start, then [compare plans](https://countrystatecity.in/pricing?source=sdk_docs\&campaign=sdk_api_migration\&package=cli) when you need more data, higher limits, fuzzy search, or autocomplete. Like the [TypeScript SDK](/api/sdks/typescript) — which the CLI is built on internally — this calls the **live API**, not a bundled snapshot. It needs an API key and is subject to your plan's usage quotas. For offline/bundled data with no API key, see [npm Packages](/api/sdks/npm). ## Installation ```bash theme={null} npm install -g @countrystatecity/cli ``` ## Quick Start ```bash theme={null} # 1. Get your free API key at https://app.countrystatecity.in csc auth login # 2. Search countries csc search countries # 3. Get country details csc get country IN ``` ## Global Flags These flags work on every command: | Flag | Short | Description | | - | - | - | | `--json` | | Output raw JSON instead of formatted tables | | `--quiet` | `-q` | Suppress all decorative output (spinners, tips) | | `--no-footer` | | Hide the API usage footer after each command | ## Authentication ```bash theme={null} csc auth login # Interactive login with API key csc auth login --key # Login with key directly csc auth status # Check current auth status csc auth logout # Remove stored API key ``` ## Search ```bash theme={null} csc search countries csc search countries --filter "united" csc search states --country IN csc search cities --country IN --state MH csc search regions # Requires Starter+ plan csc search india # Global search — matches country names ``` ### Fuzzy, typo-tolerant search **Requires Supporter+ plan.** Works two ways: **Global**, across a chosen entity type: ```bash theme={null} csc search "Mumbay" --type city --country IN --fuzzy ``` `--type` defaults to `city` if omitted. `--country` scopes results and isn't used with `--type country`. **Per-entity**, reusing `--filter`'s value as the fuzzy query: ```bash theme={null} csc search countries --filter "Indea" --fuzzy csc search states --country IN --filter "Maharastra" --fuzzy csc search cities --country IN --filter "Mumbay" --fuzzy ``` `--fuzzy` requires `--filter` on these subcommands — the CLI exits with an error if you pass `--fuzzy` without it. On `cities`, `--state` is ignored when `--fuzzy` is set, since fuzzy search only scopes by country. Fuzzy results replace the usual per-entity table with a shared one: | Column | Meaning | | - | - | | Type | `country`, `state`, or `city` | | Name | Matched name | | Location | State + country for cities, country for states, blank for countries | | ID | Record ID | | Score | Match confidence — the API's `match_score`, shown to 2 decimal places | ### `--fields` and `--sort` **Requires Starter+ plan.** Selecting or sorting by fields outside the basic set, such as `population`, needs Supporter+. Available on `search countries`/`states`/`cities` — passed straight through to the API instead of the CLI always fetching full objects: ```bash theme={null} csc search cities --country IN --fields name,population --sort population:desc csc search countries --fields name,iso2 --sort name:asc ``` When `--fields` narrows the response, output falls back to a generic table showing whatever columns actually came back — the CLI's usual hardcoded columns (ISO2, Capital, Phone, and so on) may not all be present anymore. ## Get Details ```bash theme={null} csc get country IN csc get country US --json csc get country # Interactive — prompts to pick a country (TTY only) csc get state IN MH csc get state # Interactive — prompts for country then state (TTY only) ``` ## Usage & Billing ```bash theme={null} csc usage csc usage --json # Returns { plan, price, daily, monthly } csc upgrade csc upgrade --json # Returns { plans, currentPlan } ``` `csc upgrade` fetches live plan and pricing data from the API on every run, so what you see always matches the current [pricing page](https://countrystatecity.in/pricing?source=sdk_docs\&campaign=sdk_api_migration\&package=cli) — it isn't a table baked into the CLI's own code that can drift out of date. ## Plan-Gated Features Hitting a command your plan doesn't include shows the specific plan you need and a direct upgrade link, not just a generic message: ``` Access denied — this endpoint requires a higher plan. Requires: Professional plan or higher. Run `csc upgrade` to view available plans: https://app.countrystatecity.in/pricing ``` ## Code Generation **Requires Supporter+ plan.** Generate ready-to-use components and seed files from live API data: ```bash theme={null} csc generate dropdown --entity countries --format react csc generate dropdown -e states -f react --country IN csc generate seed --entity countries --format prisma csc generate seed -e states -f prisma --country IN ``` ## Live Location Components These commands create live components that call the [Autocomplete API](/api/endpoints/get-search-autocomplete) as the user types. Unlike `dropdown` and `seed`, they do not download data when you generate the files, so the generator works offline. The generated component needs a **Supporter, Professional, or Business** API plan when it runs. [Get an API key](https://app.countrystatecity.in?source=cli_docs\&campaign=location_components\&package=cli) or [compare plans](https://countrystatecity.in/pricing?source=cli_docs\&campaign=location_components\&package=cli). ```bash theme={null} # Type-ahead search field csc generate autocomplete --target nextjs # server API route + client component (recommended) csc generate autocomplete --target react-browser # calls the API directly from the browser # Cascading country → state → city picker csc generate location-picker --target nextjs csc generate location-picker --target react-browser ``` `--target` is required: `nextjs` or `react-browser`. `--output ` defaults to the current directory. Both generators always produce TypeScript (`.tsx`) — there's no `--typescript`/`--no-typescript` toggle like `dropdown` has. ### Choosing a target Generates a server Route Handler (`app/api/csc-search/route.ts`) that holds `CSC_API_KEY` server-side and proxies to the Autocomplete API — the key never reaches the browser. `autocomplete` and `location-picker` share this same route file when both are generated with `nextjs`. No server — the generated component calls the API directly from the browser via the SDK, so the key ships in your JavaScript bundle. Before using a `react-browser`-generated component, create an API key in your CSC dashboard **restricted to your site's origin(s)**. Never use an unrestricted/server key here — it would be visible in the browser and every outgoing request. The CLI prints this warning before writing any files, and it's repeated in both the generated component's header comment and the generated README, so it survives past the initial run. ### What gets generated Every run also writes an `.env.example` and a short README: | Command | Files | | - | - | | `autocomplete --target nextjs` | `app/api/csc-search/route.ts`, `components/LocationAutocomplete.tsx`, `.env.example`, `CSC-AUTOCOMPLETE-README.md` | | `autocomplete --target react-browser` | `components/LocationAutocomplete.tsx`, `.env.example`, `CSC-AUTOCOMPLETE-README.md` | | `location-picker --target nextjs` | `app/api/csc-search/route.ts`, `components/LocationPicker.tsx`, `.env.example`, `CSC-LOCATION-PICKER-README.md` | | `location-picker --target react-browser` | `components/LocationPicker.tsx`, `.env.example`, `CSC-LOCATION-PICKER-README.md` | `.env.example` uses a placeholder, never a real key — `CSC_API_KEY=YOUR_API_KEY_HERE` for `nextjs`, `VITE_CSC_API_KEY=YOUR_API_KEY_HERE` for `react-browser`. For safety, the command stops before writing anything if one of these files already exists. It never replaces your existing files. ### Behavior Both components share the same search-field logic: * Debounced input (\~250ms), 2-character minimum before searching * Cancels the previous in-flight request when new input arrives, so a slow response never overwrites a newer one * Five distinct states — `loading`, `empty`, `plan-restricted`, `rate-limited`, `network-error` — each with its own message, not one generic error * Full keyboard support: `ArrowUp`/`ArrowDown` to move, `Enter` to select the highlighted result, `Escape` or `Tab` to close * ARIA combobox pattern (`role="combobox"`, `aria-expanded`, `aria-controls`, `aria-autocomplete`, `aria-activedescendant`) for screen readers Selections carry stable identifiers (`id`, `iso2`, `country_code`, `state_code`, depending on entity) alongside the display `label`. Use the identifiers for anything programmatic — storing a selection, filtering a follow-up request, and so on. `label` is for display only. `location-picker` cascades three fields — country, then state, then city. The state field is disabled until a country is picked; the city field is disabled until a state is picked. Selecting a country clears both state and city; selecting a state clears only city: ```typescript theme={null} export interface LocationPickerValue { country: IAutocompleteResult | null; state: IAutocompleteResult | null; city: IAutocompleteResult | null; } ``` `autocomplete` reports its selection via `onSelect?: (result: IAutocompleteResult) => void`. `location-picker` reports the whole cascade via `onChange?: (value: LocationPickerValue) => void`, called after every level changes. ## Interactive Explorer ```bash theme={null} csc explore ``` Pick a country, then a state, then an action — view cities, view full details, or get the equivalent `csc generate` command to copy-paste. Requires an interactive terminal (TTY). Use `csc search` for scripts. ## Export ```bash theme={null} csc export # Opens the online bulk export tool in your browser csc export --json # Returns the export URL as JSON, useful for scripts ``` ## Source View the CLI source, open issues, or contribute. View published versions and changelog. # npm Packages Source: https://docs.countrystatecity.in/api/sdks/npm Official @countrystatecity npm packages for Node.js and browser environments — countries, timezones, currencies, translations, and a CLI tool The `@countrystatecity` monorepo publishes offline data packages, a CLI, and the live API SDK. The data packages work without an API key or network requests; the CLI and SDK call the REST API. Need data that updates without publishing a new package version, typo-tolerant search, or usage tracking? Pair these packages with the [TypeScript SDK](/api/sdks/typescript) — a live API client for when you need that instead of (or alongside) a bundled snapshot. Its **Migrating from the Local Packages** section has a side-by-side comparison. [Get a free API key](https://app.countrystatecity.in?source=sdk_docs\&campaign=sdk_api_migration). ## Packages | Package | Environment | Description | Bundle | | - | - | - | - | | [`@countrystatecity/countries`](https://www.npmjs.com/package/@countrystatecity/countries) | Node.js / Server | Countries, states, cities with lazy loading | \<10KB | | [`@countrystatecity/countries-browser`](https://www.npmjs.com/package/@countrystatecity/countries-browser) | Browser / Any | Same API as above, loads data via jsDelivr CDN | \<10KB | | [`@countrystatecity/timezones`](https://www.npmjs.com/package/@countrystatecity/timezones) | Node.js / Server | 392 IANA timezones with conversion utilities | \<20KB | | [`@countrystatecity/currencies`](https://www.npmjs.com/package/@countrystatecity/currencies) | Node.js / Browser | 155 ISO 4217 currencies with symbols and formatting | \<3KB | | [`@countrystatecity/translations`](https://www.npmjs.com/package/@countrystatecity/translations) | Node.js / Browser | Country name translations in 19 languages | \<3KB | | [`@countrystatecity/phonecodes`](https://www.npmjs.com/package/@countrystatecity/phonecodes) | Node.js / Browser | Country phone codes with lookup and formatting | \<3KB | | [`@countrystatecity/postalcodes`](https://www.npmjs.com/package/@countrystatecity/postalcodes) | Node.js / Server | Postal codes with locality search and validation | Lazy-loaded | | [`@countrystatecity/geojson`](https://www.npmjs.com/package/@countrystatecity/geojson) | Browser / Any | Countries, states, and cities as GeoJSON points | Lazy-loaded | | [`@countrystatecity/cli`](https://www.npmjs.com/package/@countrystatecity/cli) | Terminal | Search, explore, and generate code from geographic data | – | | [`@countrystatecity/sdk`](/api/sdks/typescript) | Node.js / Browser | Typed client for the live REST API | – | ## Installation Install only the packages you need: ```bash theme={null} # Countries, states, and cities (Node.js / server-side) npm install @countrystatecity/countries # Countries, states, and cities (browser / React / Vue / Svelte) npm install @countrystatecity/countries-browser # Timezones npm install @countrystatecity/timezones # Currencies npm install @countrystatecity/currencies # Translations npm install @countrystatecity/translations # Phone codes npm install @countrystatecity/phonecodes # Postal codes npm install @countrystatecity/postalcodes # GeoJSON points npm install @countrystatecity/geojson # CLI (global install) npm install -g @countrystatecity/cli # Live API SDK npm install @countrystatecity/sdk ``` Use `@countrystatecity/countries` in Node.js, Next.js API routes, or Express. Use `@countrystatecity/countries-browser` in client-side React, Vue, or Svelte apps — it loads data via CDN instead of bundling it. ## Usage ### Countries, States, and Cities ```typescript Node.js theme={null} import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries'; const countries = await getCountries(); // [{ id: 101, name: 'India', iso2: 'IN', emoji: '🇮🇳', ... }, ...] const states = await getStatesOfCountry('US'); // [{ id: 1, name: 'California', iso2: 'CA', ... }, ...] const cities = await getCitiesOfState('US', 'CA'); // [{ id: 110992, name: 'Los Angeles', latitude: '34.05', longitude: '-118.24', ... }, ...] ``` ```typescript Browser (React / Vue / Svelte) theme={null} import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries-browser'; const countries = await getCountries(); // [{ id: 101, name: 'India', iso2: 'IN', emoji: '🇮🇳', ... }, ...] const states = await getStatesOfCountry('IN'); // [{ id: 1, name: 'Maharashtra', iso2: 'MH', ... }, ...] const cities = await getCitiesOfState('IN', 'MH'); // [{ id: 1, name: 'Mumbai', ... }, ...] ``` ### Timezones ```typescript theme={null} import { getTimezonesByCountry, convertTime, getCurrentTime } from '@countrystatecity/timezones'; const timezones = await getTimezonesByCountry('US'); // [{ zoneName: 'America/New_York', abbreviation: 'EST', gmtOffset: -18000, ... }, ...] const result = await convertTime( '2025-10-18T12:00:00Z', 'America/New_York', 'Europe/London' ); // { originalTime: '2025-10-18T08:00:00', convertedTime: '2025-10-18T13:00:00', timeDifference: 5 } const now = await getCurrentTime('Asia/Kolkata'); // "2025-10-18T17:30:00.000Z" ``` ### Currencies ```typescript theme={null} import { getCurrencyByCode, formatCurrencyAmount, getCurrenciesByCountry } from '@countrystatecity/currencies'; const usd = await getCurrencyByCode('USD'); // { code: 'USD', name: 'US Dollar', symbol: '$', decimalDigits: 2, ... } const formatted = await formatCurrencyAmount(1234.5, 'USD'); // "$1,234.50" const currencies = await getCurrenciesByCountry('IN'); // [{ code: 'INR', name: 'Indian Rupee', symbol: '₹', ... }] ``` ### Translations ```typescript theme={null} import { getTranslation, getTranslationOrFallback, getCountryTranslations } from '@countrystatecity/translations'; const name = await getTranslation('DE', 'fr'); // "Allemagne" const entry = await getCountryTranslations('IN'); // { iso2: 'IN', name: 'India', translations: { fr: 'Inde', de: 'Indien', 'zh-CN': '印度', ... } } getTranslationOrFallback(entry, 'hi'); // "भारत" getTranslationOrFallback(entry, 'xx'); // "India" ← falls back to English ``` **Supported locales:** `ar`, `br`, `de`, `es`, `fa`, `fr`, `hi`, `hr`, `it`, `ja`, `ko`, `nl`, `pl`, `pt`, `pt-BR`, `ru`, `tr`, `uk`, `zh-CN` ### CLI ```bash theme={null} # Authenticate with your free API key csc auth login # Search countries, states, and cities csc search countries csc search states --country IN csc search cities --country IN --state MH # Get detailed info as JSON csc get country US --json # Interactive browser: pick country → state → view cities or generate code csc explore # Generate a React dropdown component (Supporter plan+) csc generate dropdown --entity countries --format react csc generate dropdown --entity states --format react --country IN # Generate a Prisma seed file (Supporter plan+) csc generate seed --entity countries --format prisma # Open the online bulk export tool csc export ``` Code generation (`csc generate`) requires a Supporter plan or above. Run `csc upgrade` to view plans and pricing. See the [CLI](/api/sdks/cli) page for the full command reference, including typo-tolerant fuzzy search and `--fields`/`--sort`. ## Data Updates All packages update automatically every week via a CI pipeline that fetches the latest data from the [countries-states-cities-database](https://github.com/dr5hn/countries-states-cities-database), runs tests, and publishes a patch release to npm. To get the latest data in your project, update the package: ```bash theme={null} npm update @countrystatecity/countries ``` ### Checking your installed data version Both `@countrystatecity/countries` and `@countrystatecity/countries-browser` export a `getDataVersion()` loader so you can confirm which release your installed package is bundling, without loading the full dataset: ```typescript theme={null} import { getDataVersion } from '@countrystatecity/countries'; const version = await getDataVersion(); // { // dataVersion: 'v3.2-export.7-2026.07.29', // sourceRelease: 'v3.2-export.7', // updatedAt: '2026-07-29T09:01:18Z', // recordCounts: { countries: 250, states: 5308, cities: 152970 } // } ``` The counts above are from one release and are illustrative — they change every time the data is refreshed. ### Comparing your package against the live API The [Get Data Version](/api/endpoints/get-data-version) endpoint returns the same three fields for the data the API is serving. **Compare `sourceRelease`, not `dataVersion`.** Both sides build `dataVersion` from the same release tag, but they append different dates: the package uses the date the release was **published**, while the API uses the date that release was **imported**. Because the import happens after the release is published, the two strings usually differ by a day or more even when both sides hold exactly the same data. `sourceRelease` is identical on both sides, so it is the reliable parity key. ```typescript theme={null} import { getDataVersion } from '@countrystatecity/countries'; async function isPackageInSyncWithApi(apiKey: string): Promise { const local = await getDataVersion(); const response = await fetch('https://api.countrystatecity.in/v1/meta/data-version', { headers: { 'X-CSCAPI-KEY': apiKey } }); if (!response.ok) { console.error(`Could not read the live data version (HTTP ${response.status})`); return null; } const live = await response.json(); // Compare sourceRelease — the dataVersion date suffixes differ by design. return local.sourceRelease === live.sourceRelease; } const inSync = await isPackageInSyncWithApi('YOUR_API_KEY'); console.log(inSync); // true ``` The API's version metadata is cached and can trail a fresh import by about 5 minutes (about 5.5 minutes for the `X-CSC-Data-*` response headers). If a comparison comes back out of sync right after a data refresh, retry a few minutes later before treating it as a real mismatch. The package's `recordCounts` is narrower than the API's — `countries`/`states`/`cities` only, no `regions`/`subregions`. ## Source View the monorepo source, open issues, or contribute. Browse all published packages on npm. # Python SDK & Packages Source: https://docs.countrystatecity.in/api/sdks/python Use the official countrystatecity-api client for live managed data, or install type-safe offline geographical data packages Python applications can use the live Country State City API or install versioned offline data snapshots: * **Live API SDK:** `countrystatecity-api` provides typed synchronous and asynchronous clients, structured errors, and current managed data. * **Offline packages:** the suffixed `countrystatecity-*` packages bundle data for applications that cannot use an API key or network connection. Install the live client with `pip install countrystatecity-api`, then import it as `countrystatecity`. There is no bare `countrystatecity` distribution on PyPI. ## Live API SDK Install the official API client and keep your key in a server-side environment variable: ```bash theme={null} pip install countrystatecity-api export CSC_API_KEY="YOUR_API_KEY" ``` [Create a free API key](https://app.countrystatecity.in/?source=sdk_docs\&campaign=sdk_api_migration\&package=countrystatecity-api\&utm_source=docs\&utm_medium=documentation\&utm_campaign=python_packages\&utm_content=python_sdk) and [compare plans](https://countrystatecity.in/pricing/?source=sdk_docs\&campaign=sdk_api_migration\&package=countrystatecity-api\&utm_source=docs\&utm_medium=documentation\&utm_campaign=python_packages\&utm_content=python_sdk). ```python theme={null} from countrystatecity import CountryStateCity, CountryStateCityError try: with CountryStateCity() as client: states = client.get_states_of_country("IN") for state in states[:3]: print(state["name"]) except CountryStateCityError as error: print(f"Country State City request failed: {error}") ``` Use `AsyncCountryStateCity` with `async with` for non-blocking applications. Both clients cover countries, states, cities, regions, subregions, timezones, currencies, dial codes, ISO conversion, phone parsing, and fuzzy search. Start on the free tier and create a server-side API key. Review quotas and plan-specific capabilities. ## Offline Packages The offline packages use full type hints, Pydantic models, and lazy loading. Their data is bundled directly, so they do not require an API key or network request. ### Available Packages | Package | Status | Description | | - | - | - | | `countrystatecity-countries` | Released | Countries, states, and cities with lazy loading | | `countrystatecity-timezones` | Released | IANA timezone data and time conversion utilities | | `countrystatecity-currencies` | Released | Currency codes, symbols, and country associations | | `countrystatecity-translations` | Released | Country name translations in 19 languages | | `countrystatecity-phonecodes` | Released | International dialing codes for 250+ countries | | `countrystatecity-regions` | Released | Region and subregion associations | | `countrystatecity-postal-codes` | Released | Postal code records and format validation | ### Installation Install only what you need: ```bash theme={null} pip install countrystatecity-countries pip install countrystatecity-timezones pip install countrystatecity-currencies pip install countrystatecity-translations pip install countrystatecity-phonecodes pip install countrystatecity-regions pip install countrystatecity-postal-codes ``` ### Usage #### Countries ```python theme={null} from countrystatecity_countries import get_countries, get_country_by_code countries = get_countries() print(f"Total countries: {len(countries)}") usa = get_country_by_code("US") print(f"{usa.emoji} {usa.name}") print(f"Capital: {usa.capital}") print(f"Currency: {usa.currency_symbol} {usa.currency_name}") ``` #### States ```python theme={null} from countrystatecity_countries import get_states_of_country states = get_states_of_country("IN") print(f"Total states in India: {len(states)}") for state in states[:3]: print(f"{state.name} ({state.iso2})") # Maharashtra (MH) # Karnataka (KA) # Tamil Nadu (TN) ``` #### Cities ```python theme={null} from countrystatecity_countries import get_cities_of_state cities = get_cities_of_state("US", "CA") print(f"Cities in California: {len(cities)}") for city in cities[:3]: print(f"{city.name} — {city.latitude}, {city.longitude}") # Los Angeles — 34.05223, -118.24368 # San Diego — 32.72541, -117.15726 # San Jose — 37.33939, -121.89496 ``` #### Country Details ```python theme={null} from countrystatecity_countries import get_country_by_code gb = get_country_by_code("GB") print(gb.name) # United Kingdom print(gb.iso2) # GB print(gb.iso3) # GBR print(gb.capital) # London print(gb.currency) # GBP print(gb.currency_name) # British Pound print(gb.region) # Europe print(gb.timezones) # [...] ``` #### Timezones ```python theme={null} from countrystatecity_timezones import ( get_all_timezones, get_timezones_by_country, get_timezone_by_zone_name, convert_time, ) # Timezones for a country timezones = get_timezones_by_country("US") print(f"US timezones: {len(timezones)}") # Lookup by zone name tz = get_timezone_by_zone_name("America/New_York") print(f"{tz.zone_name} — {tz.gmt_offset_name}") # Convert time between zones from datetime import datetime dt = datetime(2024, 1, 1, 12, 0, 0) converted = convert_time(dt, "America/New_York", "Asia/Kolkata") ``` #### Currencies ```python theme={null} from countrystatecity_currencies import ( get_all_currencies, get_currency_by_country, get_countries_by_currency, search_currencies, ) # Currency for a country currency = get_currency_by_country("US") print(f"{currency.symbol} {currency.name} ({currency.code})") # $ United States Dollar (USD) # All countries using a currency countries = get_countries_by_currency("EUR") print(f"Countries using EUR: {len(countries)}") # Search by name results = search_currencies("dollar") ``` #### Translations ```python theme={null} from countrystatecity_translations import ( get_all_translations, get_translations_by_country, get_translations_by_language, get_translation, ) # Country name in a specific language translation = get_translation("US", "fr") print(translation.translation) # États-Unis # All translations for a country (all 19 languages) translations = get_translations_by_country("IN") # All countries translated into Japanese japanese = get_translations_by_language("ja") print(f"Japanese translations: {len(japanese)}") ``` #### Phone Codes ```python theme={null} from countrystatecity_phonecodes import ( get_all_phonecodes, get_phonecode_by_country, get_countries_by_phonecode, search_phonecodes, ) # Phone code for a country us = get_phonecode_by_country("US") print(f"+{us.phoneCode} — {us.countryName}") # +1 — United States # All countries sharing a dialing code plus1 = get_countries_by_phonecode("1") print(f"{len(plus1)} countries use +1") # 25 countries use +1 # Works with or without + prefix plus44 = get_countries_by_phonecode("+44") # Search by country name, code, or phone code results = search_phonecodes("united") ``` ## Features Full type hints with Pydantic models and mypy support. Data loads on first access and caches with LRU cache for minimal memory footprint. Only requires Pydantic — no other external packages. 250+ countries, 5,299+ states, 153,765+ cities with coordinates and metadata. ## Offline Data Updates To get the latest data, upgrade any package: ```bash theme={null} pip install --upgrade countrystatecity-countries pip install --upgrade countrystatecity-timezones pip install --upgrade countrystatecity-currencies pip install --upgrade countrystatecity-translations pip install --upgrade countrystatecity-phonecodes pip install --upgrade countrystatecity-regions pip install --upgrade countrystatecity-postal-codes ``` ## Source View source, open issues, or contribute. Official live API client on PyPI. Countries, states, and cities on PyPI. Timezone data on PyPI. Currency data on PyPI. Translations data on PyPI. Phone codes data on PyPI. Regions and subregions on PyPI. Postal code records and validation on PyPI. # TypeScript SDK Source: https://docs.countrystatecity.in/api/sdks/typescript Official @countrystatecity/sdk — a live REST API client for Node.js and browser, with typed resources, structured errors, and automatic retries Official JS/TS client for the live Country State City REST API — countries, states, cities, regions, currencies, phone codes, timezones, fuzzy search, and account usage. Zero runtime dependencies, dual ESM/CJS builds, full TypeScript types. This is not the same thing as the [npm Packages](/api/sdks/npm) covered elsewhere on this site. Those embed the geographical data directly — no API key, no network call. `@countrystatecity/sdk` is the opposite: a **live REST API client**. It needs an API key, makes real HTTP requests, and is subject to your plan's usage quotas — in exchange, it's always current and covers endpoints the offline packages don't have (`search`, `usage`, and live currency/timezone/phone lookups). You can use both together — see **Migrating from the local packages** below. `@countrystatecity/sdk` is currently **0.1.0**, pre-1.0. The API surface may still shift before a stable 1.0 release. ## Installation ```bash theme={null} npm install @countrystatecity/sdk ``` Requires **Node.js 18+** (for native `fetch`) or any modern browser. Zero runtime dependencies — built entirely on native `fetch`, `URL`/`URLSearchParams`, and `AbortController`. You'll need an API key — [get one free](https://app.countrystatecity.in?source=sdk_docs\&campaign=sdk_api_migration\&package=%40countrystatecity%2Fsdk) and compare [plans and quotas](https://countrystatecity.in/pricing?source=sdk_docs\&campaign=sdk_api_migration\&package=%40countrystatecity%2Fsdk). ## Quick Start ```typescript theme={null} import { createCSCClient } from '@countrystatecity/sdk'; const csc = createCSCClient({ apiKey: process.env.CSC_API_KEY! }); const { data: countries } = await csc.countries.list(); const { data: states } = await csc.states.list({ country: 'IN' }); const { data: cities } = await csc.cities.list({ country: 'IN', state: 'MH' }); const { data: matches } = await csc.search.fuzzy({ query: 'Banglore', type: 'city', country: 'IN', limit: 10, }); ``` Every resource method returns `{ data, meta }` — `data` contains the typed result and `meta` carries request, usage, pagination, and cache information alongside it. See **Response Metadata** below. ## Configuration ```typescript theme={null} const csc = createCSCClient({ apiKey: 'your-api-key', // required — never read from env or persisted by the SDK baseUrl: 'https://api.countrystatecity.in/v1', // default timeout: 10_000, // ms, per request attempt, default 10000 fetch: myCustomFetch, // optional custom fetch implementation headers: { 'X-Trace-Id': 'x' }, // extra headers merged into every request retry: { retries: 2, baseDelayMs: 200, maxDelayMs: 2000 }, // or `false` to disable userAgent: 'my-app/1.0', // overrides the default countrystatecity-sdk-js/ }); ``` The SDK never reads `apiKey` from an environment variable and never writes it to disk — pass it explicitly at construction time. ## Resource Reference Every `list`/`get`/etc. method also accepts a trailing `{ signal?, timeout?, headers? }` for per-call overrides — see **Retries & Timeouts** below. | Resource | Methods | | - | - | | `csc.countries` | `list({ limit?, offset?, fields?, sort? })`, `get(iso2)` | | `csc.states` | `list({ country?, limit?, offset?, fields?, sort? })`, `get(country, stateCode)` | | `csc.cities` | `list({ country?, state?, kind?, limit?, offset?, fields?, sort? })`, `get(country, stateCode, cityId)` | | `csc.regions` | `list()`, `get(id)`, `subregions(id)` | | `csc.currencies` | `list()`, `get(code)`, `byCountry(iso2)` | | `csc.iso` | `lookup({ iso2? \| iso3? \| numeric? })` | | `csc.phone` | `list()`, `get(iso2)`, `byDialCode(dialCode)` | | `csc.timezones` | `list()`, `byCountry(iso2)`, `convert({ time, from, to })` | | `csc.search` | `fuzzy({ query, type?, country?, limit?, threshold? })` — `type` defaults to `city` | | `csc.usage` | `get()` — returns cached rate-limit usage from the last request when available, otherwise makes one lightweight request | `csc.cities.list({ state })` requires `country` to also be set — a `ValidationError` is thrown client-side otherwise, before any network call. ## Error Handling All errors extend `CSCError` (`message`, `statusCode`, `requestId`, `url`, `retryCount`): ```typescript theme={null} import { AuthenticationError, ForbiddenError, ValidationError, FeatureRestrictedError, RateLimitError, NotFoundError, NetworkError, TimeoutError, } from '@countrystatecity/sdk'; try { await csc.search.fuzzy({ query: 'Mumbai' }); } catch (err) { if (err instanceof ValidationError) { console.error(`Bad input: ${err.field} — ${err.reason}`); } else if (err instanceof AuthenticationError) { console.error('Invalid or missing API key.'); } else if (err instanceof ForbiddenError) { console.error('Request blocked by API-key domain or IP restrictions.'); } else if (err instanceof FeatureRestrictedError) { console.error(`"${err.feature}" needs the ${err.requiredPlan} plan (you're on ${err.currentPlan}).`); console.error(`Upgrade: ${err.upgradeUrl}`); } else if (err instanceof RateLimitError) { console.error(`Rate limited (${err.scope}). Retry after ${err.retryAfter}s.`); console.error(`Upgrade: ${err.upgradeUrl}`); } else if (err instanceof NotFoundError) { console.error(`${err.resource} "${err.identifier}" not found.`); } else if (err instanceof TimeoutError) { console.error(`Timed out after ${err.timeoutMs}ms.`); } else if (err instanceof NetworkError) { console.error('Network or server error:', err.message); } } ``` ### Error class reference | Class | When | Extra fields | | - | - | - | | `AuthenticationError` | 401 — missing or invalid API key | — | | `ForbiddenError` | 403 — blocked by an API-key domain or IP restriction | — | | `ValidationError` | Malformed input caught client-side, or a 4xx validation response | `field`, `value`, `reason` | | `FeatureRestrictedError` | 403 — current plan doesn't include this feature | `feature`, `currentPlan`, `requiredPlan`, `upgradeUrl` | | `RateLimitError` | 429 — daily or monthly quota exceeded | `scope` (`daily`/`monthly`), `limit`, `remaining`, `resetAt`, `retryAfter`, `tier`, `upgradeUrl` | | `NotFoundError` | 404 — resource doesn't exist | `resource`, `identifier` | | `NetworkError` | Connection failure, or a 5xx that exhausted all retries | — | | `TimeoutError` | A request (including retries) exceeded its configured timeout | `timeoutMs` | `ValidationError` can also be thrown synchronously — as a rejected promise, before any network call is made — for malformed input like bad ISO codes, out-of-range coordinates, or invalid limits. ## Retries & Timeouts GET requests are retried automatically on transient network errors, `429`, and `5xx` responses — never on `401`/`403`/`404`/`400`-class responses, and never on a caller-initiated `AbortSignal` cancellation. Defaults: 2 retries, full-jitter exponential backoff (200ms base, 2000ms cap). Any `Retry-After` response header takes precedence over the computed delay. ```typescript theme={null} const csc = createCSCClient({ apiKey: 'k', retry: false }); // disable entirely const csc2 = createCSCClient({ apiKey: 'k', retry: { retries: 5, baseDelayMs: 100, maxDelayMs: 5000 } }); ``` `timeout` applies **per attempt**, not as a total budget — worst-case latency for a call is roughly `timeout × attempts + sum(backoff delays)`. Override per call: ```typescript theme={null} const controller = new AbortController(); setTimeout(() => controller.abort(), 3000); await csc.countries.list(undefined, { signal: controller.signal, timeout: 5000 }); ``` ## Response Metadata ```typescript theme={null} const { data, meta } = await csc.countries.list(); meta.requestId; // string | undefined meta.rateLimit; // { dailyUsed, dailyLimit, monthlyUsed, monthlyLimit } | undefined meta.dataVersion; // string | undefined meta.cache; // 'HIT' | 'MISS' | 'DYNAMIC' | undefined meta.pagination; // { total, limit, offset, hasMore } | undefined meta.retryCount; // number of retries this call needed ``` `meta.dataVersion` is the SDK-side way to read the same value documented on [Get Data Version](/api/endpoints/get-data-version). When the response includes the data-version header, no extra request is needed. `csc.getLastResponseMeta()` returns the metadata from the most recent successful request on that client instance — useful for surfacing usage in a UI without an extra call. ## Browser Usage ```typescript theme={null} import { createCSCClient } from '@countrystatecity/sdk'; const csc = createCSCClient({ apiKey: PUBLISHABLE_RESTRICTED_KEY }); const { data } = await csc.countries.list(); ``` **A key embedded in browser JavaScript is public** — visible in your bundle and every outgoing request. Never use an unrestricted/server key here. Instead, create a key in your CSC dashboard that's **restricted to specific allowed origins** (your site's domain(s)); requests from any other origin are rejected server-side. The SDK adds no protection on top of this — origin restriction is an account/dashboard setting, not something the client enforces. ## Node.js and Next.js Use the SDK directly in any Node.js 18+ script or backend service. In Next.js, keep it **server-side** — in a Route Handler, Server Component, or Server Action — so your API key never reaches the client bundle. ## Bundle Size \~6.9KB gzipped (ESM build), against a 20KB budget enforced in CI — zero runtime dependencies. ## Migrating from the Local Packages If you're already using [`@countrystatecity/countries`](/api/sdks/npm) and want live, quota-aware data instead of the weekly-updated bundled snapshot: ```typescript theme={null} // Before — @countrystatecity/countries import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries'; const countries = await getCountries(); const states = await getStatesOfCountry('US'); const cities = await getCitiesOfState('US', 'CA'); ``` ```typescript theme={null} // After — @countrystatecity/sdk import { createCSCClient } from '@countrystatecity/sdk'; const csc = createCSCClient({ apiKey: process.env.CSC_API_KEY! }); const { data: countries } = await csc.countries.list(); const { data: states } = await csc.states.list({ country: 'US' }); const { data: cities } = await csc.cities.list({ country: 'US', state: 'CA' }); ``` ### Why migrate * You need data more current than the weekly-updated local snapshot. * You need `search` or `usage` — neither has a local-package equivalent. * You're already calling the live API elsewhere and want one typed client instead of hand-rolled `fetch` calls. ### Why not to migrate If you don't need any of the above, staying on the local package is usually the better choice — it's free, has no network latency, and isn't subject to plan quotas. ### What changes * **An API key is required.** The local package needs none. * **Every call is async and can fail with a network-shaped error** (`RateLimitError`, `NetworkError`, `TimeoutError`, and others) in addition to the local package's simpler failure modes. * **Results are quota-limited** per your plan (`RateLimitError`), not just rate-limited by your own code. * **The shape is `{ data, meta }`**, not the bare array/object the local package returns — destructure `data` at the call site. You don't have to choose one. Keep `@countrystatecity/countries` for offline/bundled lookups and add `@countrystatecity/sdk` only for what it uniquely provides — `search`, `usage`, and guaranteed-current data. They don't conflict. ## TypeScript Types ```typescript theme={null} import type { CSCClientOptions, CSCResponse, CSCResponseMeta, ICountry, IState, ICity, IRegion, ISubregion, ICurrency, IPhonecode, ITimezone, IConvertedTime, ISearchResult, IUsageSnapshot, } from '@countrystatecity/sdk'; ``` ## Source View the SDK source, open issues, or contribute. View published versions and changelog. # Changelog Source: https://docs.countrystatecity.in/changelog New endpoints, fixes and improvements to the Country State City documentation and API Follow along with what has changed across the CSC API docs. For changes to the geographical data itself, use the [Data Changes endpoint](/api/endpoints/get-data-changes). #### Changed * **Pricing tier realignment**: every endpoint page and guide now matches the current plan features. * **Starter** now includes All States (`/states`), Cities by Country, search filtering with `?q=` (including postcode listing and search), the Regions & Subregions API, phone dial codes, currency lookup, and [field filtering and sorting](/api/field-filtering-and-sorting) (`?fields=`, `?sort=`). Starter still returns the basic field set. * **Supporter** now has full data access, including `translations`, `wikiDataId`, and [localized names](/api/localization), plus Fuzzy Search, Location Autocomplete, and Nearby Search. These previously required Professional. * **Professional** now includes the [Data Change Feed](/api/endpoints/get-data-changes), previously Business-only. * Field tables now have two access levels, **Basic** and **Full**, because no plan uses the old Coordinates level. Endpoints that Community plans cannot call, such as All States and Cities by Country, list only Starter and Legacy under Basic. * Full-tier examples that show `translations` now include the `include_translations=true` request that returns them, and plan-gate `403` examples use the standard `status`/`message`/`details` response body. #### Added * **[GraphQL API](/api/graphql)** guide: documents the new `POST /v1/graphql` endpoint (Professional+), covering authentication, opening the GraphiQL explorer with an API key, nested queries, per-plan field access, the full query list including the GraphQL-only `city(id)` lookup, `limit`/`offset` pagination (root default 1,000, nested default 20 per parent), the 50-lookup / 25,000-row query budget, and how the HTTP status and error shape depend on where a request fails #### Changed * Updated `llms.txt` to surface the GraphQL endpoint for AI assistants #### Added * **Postcode Endpoints** group: exact postcode lookup (`GET /v1/countries/{cc}/postcodes/{code}`, free on every plan) and paginated listing/search (`GET /v1/countries/{cc}/postcodes`, requires Supporter+ even for a bare list with no filters) — documents duplicate-match handling, cursor pagination (including expiry and tamper detection), `q`/`state_code`/`city_id`/`type` filters, conditional-request (`ETag`/`If-None-Match`) support, and tier-based field availability * Non-deliverability disclaimer on postcode documentation: this is informational data, not an address verification or mail-deliverability service #### Fixed * Corrected the postcodes table's field name from `postcode` to the authoritative `code` throughout `database/postcodes.mdx` and `database/schema.mdx` (query examples, ER diagram, field reference, sample data) * Corrected `database/postcodes.mdx`'s export-format claim: the postcodes table **is** included in the CSV and YAML exports; only the GeoJSON export excludes it * Replaced the postcode type reference's fictional `full`/`outward`/`sector`/`district`/`area` list with the 5 values actually present in the current 844,248-row dataset (`full`, `area`, `fsa`, `po_box`, `street`), verified directly against the source data * Completed the postcodes table field reference in `database/schema.mdx` with the previously undocumented `country_id`, `state_id`, `state_code`, `city_id`, `latitude`, and `longitude` fields and their API access-level requirements #### Added * **Search Endpoints** group: fuzzy / typo-tolerant search (`GET /v1/search/fuzzy`) for cities, states, and countries using trigram similarity — documents `q`, `type`, `country`, `limit`, `threshold` parameters, the `match_score` / `matched_alias` response fields, native-script matching, and Professional+ tier gating #### Changed * Updated `llms.txt` to surface the fuzzy search capability for AI assistants #### Added * **Field Filtering & Sorting** cross-cutting guide — documents `?fields=` and `?sort=` syntax, tier gating (Supporter+), per-entity sortable fields, behaviour rules, and error responses * **ISO Code Endpoints** group: lookup country by alpha-2/alpha-3/numeric, lookup state by ISO 3166-2 subdivision code, convert between ISO formats * **Phone Endpoints** group: list dial codes with reverse lookup, get dial code by country, parse E.164 phone number into country + national parts * **Timezone Endpoints** group: timezone by country, state, and city — with DST-aware response (IANA name, abbreviation, std/DST offsets, current-DST flag) * Per-endpoint `?fields=` / `?sort=` callout on every geographic list and detail page (12 endpoints) linking back to the cross-cutting guide #### Changed * Updated `llms.txt` to surface the new endpoint groups for AI assistants * Standardised the "Related Endpoints" footer pattern across the new endpoint pages #### Added * Regions API documentation: 3 new endpoint pages (regions, subregions, countries by subregion) * Inline search filtering (`?q=`) parameter documented on list endpoints * Regions Endpoints navigation group #### Changed * Updated endpoint descriptions to mention search filtering availability (Supporter+) #### Added * API documentation covering endpoints, authentication, and usage * Changelog, donation, and database documentation pages * API FAQs section with key request instructions * Home link to the documentation navbar * Analytics integration for documentation site * Custom icons and metadata configuration * Mintlify-powered documentation site with dev and build scripts #### Changed * Updated free tier limits from 150 to 100 requests/day and 4,500 to 3,000 requests/month * Updated API request limits and trial credits across documentation * Updated export pricing documentation * Removed rate limit details from public documentation for clarity * Updated portal links to the new API key management site * Updated API key request instructions in FAQ * Improved overall documentation structure and content clarity * Removed welcome section from index page for cleaner layout #### Fixed * Corrected documentation URLs and broken links * Resolved miscellaneous documentation issues and formatting errors [1.0.0]: https://github.com/dr5hn/docs/releases/tag/v1.0.0 # City Types Source: https://docs.countrystatecity.in/database/city-types Reference for the 35 distinct values of the cities.type field, the derived kind classification, and how to filter for genuine settlements via the API or raw data Every city record carries an optional `type` field describing what kind of place it is. The value is inherited from the upstream [GeoNames](https://www.geonames.org/) feature classification. Building against the API? Filter server-side with `?kind=` on [Get Cities by Country](/api/endpoints/get-cities-by-country) or [Get Cities by State](/api/endpoints/get-cities-by-state) instead of applying a `type` exclusion list yourself. It is not a drop-in swap: `?kind=settlement` is stricter than the exclusion list further down this page, and `?kind=settlement,section,unknown` is the closest match. That manual filtering is for teams running their own copy of the raw database outside the API. The `type` field mixes two ideas — *what a place is* (`city`, `town`, `village`) and *what administrative role it fills* (`adm2`, `county`, `parish`). The same place can be tagged either way. For example, **Dallas** is `adm2` because it is the seat of Dallas County, and major Australian cities are `adm1` because they are seats of state-level governments. ## The `kind` Field [Get Cities by Country](/api/endpoints/get-cities-by-country) and [Get Cities by State](/api/endpoints/get-cities-by-state) also return a derived `kind` field — a simplified classification computed server-side from `type`, so you don't have to memorize which of the 35 raw values count as real places. Other endpoints that return city records do not include it. `kind` is one of four values: | `kind` | Meaning | | - | - | | `settlement` | An actual populated place — city, town, village, or similar | | `administrative` | A governed administrative division that is not itself a settlement (county, province, regency, and similar) | | `section` | A suburb or district of a larger city (the `section` type) | | `unknown` | Neither a live settlement nor a governed administrative area — defunct or unclassified entries, including `null` and any `type` value not yet in the mapping | The `administrative` vs. `unknown` split is this project's own interpretation layered on top of the source data — the upstream [countries-states-cities-database](https://github.com/dr5hn/countries-states-cities-database) project (and the GeoNames classification it inherits from) does not define a 4-way split like this itself. We separate genuine administrative divisions (`county`, `province`, `regency`, and similar) from defunct or non-place entries (`abandoned`, `historical`, `religious`, and similar) so that `administrative` only contains types you'd reasonably call a governed area — not a ghost town or a place of worship. `null` types and any `type` value not yet in our mapping default to `kind: unknown`. Read `unknown` as *not classified*, not as *not a place* — the bucket holds both defunct entries (`abandoned`, `historical`, `destroyed`, `religious`, `historical_capital`) and the 1,900 rows that carry no `type` at all. Use `?kind=settlement` (or any comma-separated combination, e.g. `?kind=settlement,section`) on [Get Cities by Country](/api/endpoints/get-cities-by-country) or [Get Cities by State](/api/endpoints/get-cities-by-state) to filter server-side. `kind` is available on every plan, including Community and Starter — no tier upgrade required. Raw `type` filtering (`?type=`) is also available but requires a Supporter+ plan, matching the existing `type` field's tier gate — on a lower plan the request is rejected with a `400` error rather than being ignored. ## All Type Values There are **35 distinct values**. The counts below come from the upstream [`TYPE_FIELD.md`](https://github.com/dr5hn/countries-states-cities-database/blob/master/TYPE_FIELD.md) reference, snapshotted on **2026-05-24**, and add up to **156,025** rows. Treat them as approximate: they drift as the data changes, so they do not match the **153,765+** city total quoted elsewhere in these docs, which is the upstream project's README figure counted at a different time. The **Kind** column shows the `kind` value derived from each `type` — see above for what each value means. | `type` | Count | Kind | Meaning | | - | -: | :-: | - | | `city` | 96,098 | `settlement` | Actual city | | `adm2` | 20,796 | `settlement` | 2nd-order admin division — usually a real city that is also a county/district seat | | `adm3` | 15,774 | `settlement` | Mostly real municipalities/communes tagged with their admin role | | `section` | 5,346 | `section` | Suburbs and districts of larger cities | | `county` | 4,409 | `administrative` | County-level admin unit — not a settlement | | `adm4` | 3,909 | `settlement` | Similar mix to `adm3` | | `adm1` | 3,505 | `settlement` | 1st-order admin division — usually a state/region seat (e.g. major Australian cities) | | `district` | 2,381 | `settlement` | District-level populated place | | *(null)* | 1,900 | `unknown` | No type assigned — see below | | `regency` | 390 | `administrative` | Indonesian *kabupaten* — admin division | | `prefecture` | 369 | `administrative` | Prefecture-level admin division | | `locality` | 319 | `settlement` | Named populated place | | `capital` | 281 | `settlement` | National or regional capital | | `municipality` | 209 | `settlement` | Municipality | | `parish` | 80 | `administrative` | Louisiana-style parish (= county) | | `banner` | 52 | `administrative` | Inner Mongolia *banner* — admin division | | `town` | 48 | `settlement` | Town | | `province` | 36 | `administrative` | Province-level admin division | | `adm5` | 16 | `settlement` | 5th-order admin division (populated) | | `abandoned` | 15 | `unknown` | Abandoned place | | `cities` | 13 | `settlement` | Data artifact of `city` | | `area` | 12 | `administrative` | Generic administrative area | | `village` | 10 | `settlement` | Village | | `historical` | 9 | `unknown` | Historical place — not a live settlement | | `settlement` | 9 | `settlement` | Settlement | | `oblast` | 8 | `administrative` | Oblast — admin division | | `gov_seat` | 6 | `settlement` | Government seat (populated) | | `special municipality` | 6 | `settlement` | Special municipality | | `administrative zone` | 5 | `administrative` | Administrative zone | | `region` | 4 | `administrative` | Region-level admin division | | `destroyed` | 3 | `unknown` | Destroyed place | | `township` | 3 | `settlement` | Township | | `religious` | 2 | `unknown` | Religious site — not a settlement | | `subdistrict` | 1 | `settlement` | Subdistrict (populated) | | `historical_capital` | 1 | `unknown` | Former capital — not a live settlement | ## Filtering via Raw SQL/JS/Python (Self-Hosted Data) Calling the API instead? Use `?kind=` — see [The `kind` Field](#the-kind-field) above. It is **not** a drop-in swap for the exclusion list below. `?kind=settlement` is stricter, because the list also keeps the 5,346 `section` rows and the 1,900 `null`-type rows. `?kind=settlement,section,unknown` is the closest match; it differs only by also returning the \~30 defunct rows the list drops (`abandoned`, `historical`, `destroyed`, `religious`, `historical_capital`), which map to `unknown` too. This section is for teams running their own copy of the [raw database](/database/overview) outside the API, where no `kind` field exists. For use cases like "find the nearest city to a location" or "populate a dropdown" against a self-hosted copy of the data, exclude admin-only and non-place types directly. Use an **exclusion list** rather than an inclusion list — any new settlement-style value added in the future is kept by default. **Exclude these types:** ``` county, regency, prefecture, parish, banner, province, area, oblast, administrative zone, region, abandoned, historical, destroyed, religious, historical_capital ``` ### SQL ```sql theme={null} SELECT * FROM cities WHERE ( type IS NULL OR type NOT IN ( 'county', 'regency', 'prefecture', 'parish', 'banner', 'province', 'area', 'oblast', 'administrative zone', 'region', 'abandoned', 'historical', 'destroyed', 'religious', 'historical_capital' ) ) AND latitude IS NOT NULL AND longitude IS NOT NULL; ``` `type NOT IN (...)` evaluates to `UNKNOWN` for rows where `type` is `NULL`, silently dropping them. The explicit `type IS NULL OR ...` keeps null-type rows. Remove the `type IS NULL` clause only if you want to exclude them. ### JavaScript ```javascript theme={null} const EXCLUDED_TYPES = new Set([ 'county', 'regency', 'prefecture', 'parish', 'banner', 'province', 'area', 'oblast', 'administrative zone', 'region', 'abandoned', 'historical', 'destroyed', 'religious', 'historical_capital', ]); const settlements = cities.filter( (c) => !EXCLUDED_TYPES.has(c.type) && c.latitude != null && c.longitude != null ); ``` ### Python ```python theme={null} EXCLUDED_TYPES = { 'county', 'regency', 'prefecture', 'parish', 'banner', 'province', 'area', 'oblast', 'administrative zone', 'region', 'abandoned', 'historical', 'destroyed', 'religious', 'historical_capital', } settlements = [ c for c in cities if c.get('type') not in EXCLUDED_TYPES and c.get('latitude') is not None and c.get('longitude') is not None ] ``` ## Records with No Type About **1,900** rows have a `null` type. They are a mix of legitimate places and unclassified entries. For strict settlement queries, the safest signal is the presence of valid coordinates (`latitude`/`longitude` not null), and — where available — a non-null `population`. Via the API, these rows surface with `kind: unknown`, same as other unclassified entries. ## Related Full cities table definition including the type field. How overseas territories and dual-modeled entities affect city queries. Filter cities by `kind` or `type` directly via the API. # Contributions Source: https://docs.countrystatecity.in/database/contributing How to contribute improvements, corrections, and new data to the Countries States Cities database ## Contributing Overview The Countries States Cities database thrives through community contributions. Whether you're fixing a small typo or proposing major data additions, your contributions help millions of developers access accurate geographical data. Our official web-based tool makes contributing easy - no technical setup required! ## Contribution Methods ### 1. CSC Update Tool (Recommended) The easiest way to contribute is through our dedicated web application: Easily explore regions, countries, states, and cities with powerful search capabilities. Simple forms to request corrections, additions, or updates with proper source validation. Monitor your submissions through our streamlined review process with real-time status updates. Contributors are recognized for their valuable additions to the database. Visit [manager.countrystatecity.in](https://manager.countrystatecity.in/) and sign in with GitHub or use the Magic Link option. No account required - we automatically create one when you first contribute. Use the search functionality to locate geographical entities that need updates: * Browse by region → country → state → city * Search for specific locations * Filter by data quality indicators * Review community-flagged issues Fill out the contribution form with: * **What needs to change**: Current incorrect data * **Correct information**: Your proposed updates * **Source documentation**: Links to authoritative sources * **Additional context**: Any relevant background information Always include authoritative sources for your changes. Government websites and official statistical offices are preferred. Monitor your contribution through the review process: * **Pending**: Under initial review * **In Review**: Being evaluated by our team * **Approved**: Accepted and queued for integration * **Rejected**: Not accepted (with feedback) * **Integrated**: Live in the database ### 2. GitHub Contributions (Advanced) For developers comfortable with Git workflows: Direct GitHub contributions require technical knowledge of SQL and database management. We recommend using the Update Tool for most contributions. ```bash theme={null} git clone https://github.com/dr5hn/countries-states-cities-database.git cd countries-states-cities-database git checkout -b feature/your-improvement-name ``` Edit the `sql/world.sql` file directly: ```sql theme={null} -- Example: Adding a new city INSERT INTO cities (name, state_id, state_code, state_name, country_id, country_code, country_name, latitude, longitude) VALUES ('New City', 1234, 'CA', 'California', 231, 'US', 'United States', 34.0522, -118.2437); -- Example: Correcting city coordinates UPDATE cities SET latitude = 40.7128, longitude = -74.0060 WHERE name = 'New York' AND state_code = 'NY' AND country_code = 'US'; ``` ```bash theme={null} git add sql/world.sql git commit -m "feat: add New City to California, US" git push origin feature/your-improvement-name ``` Then create a pull request on GitHub with: * Clear description of changes * Source documentation * Justification for the changes ## Contribution Guidelines ### Quality Standards **Tier 1 Sources (Preferred)**: * Government websites and official portals * National statistical offices * International organizations (UN, ISO, etc.) * Official geographical surveys **Tier 2 Sources (Acceptable)**: * Reputable geographical databases (GeoNames, etc.) * Academic institutions and research papers * Established reference works * Well-documented Wikipedia articles with citations **Unacceptable Sources**: * Personal blogs or opinions * Commercial directories without verification * Uncited crowd-sourced content * Outdated or deprecated sources **Coordinates**: * Must be in WGS84 decimal degrees format * Precision appropriate to entity type (cities: 6+ decimals) * Should represent actual administrative center when possible **Names**: * Use internationally recognized English names * Maintain consistency with established conventions * Include alternative names only when widely used **Administrative Data**: * Must reflect current official administrative structure * Include effective dates for recent changes * Maintain proper hierarchical relationships Every contribution must include: ``` Source: [Official source name and URL] Date Verified: [When you verified this information] Justification: [Why this change is needed] Impact: [What this change affects] Additional Context: [Any relevant background] ``` **Example**: ``` Source: Statistics Canada - https://www.statcan.gc.ca/ Date Verified: September 13, 2025 Justification: Nunavut coordinates incorrect by 2km Impact: Affects coordinate-based queries for Canadian territories Additional Context: Verified against official territorial government website ``` Have questions? Want to become a regional coordinator? Reach out to our team! Thank you for helping make geographical data more accurate and accessible for developers worldwide! 🌍 # DuckDB Installation Source: https://docs.countrystatecity.in/database/installation/duckdb Complete guide to installing the Countries States Cities database in DuckDB for analytical workloads and data science DuckDB is an analytical database designed for data science and OLAP workloads. It's optimized for fast analytical queries and integrates seamlessly with Python, R, and other data science tools. This guide covers installing the Countries States Cities database in DuckDB. DuckDB installation typically takes 1-2 minutes and requires approximately 60MB of disk space. ## Prerequisites Before starting, ensure you have: * Python 3.7+ (for the conversion script) * DuckDB installed (`pip install duckdb`) * At least 200MB free disk space * Downloaded the SQLite database files from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) DuckDB format is created by converting SQLite files using our conversion script, as DuckDB can directly read and import from SQLite databases. ## Installation Methods ### Method 1: Direct SQLite Import DuckDB can directly query SQLite databases without conversion: ```bash theme={null} pip install duckdb ``` ```python theme={null} import duckdb # Connect to DuckDB (creates file if doesn't exist) conn = duckdb.connect('world.duckdb') # Attach SQLite database conn.execute("ATTACH 'sqlite/world.sqlite3' AS sqlite_db") # Copy tables from SQLite to DuckDB conn.execute("CREATE TABLE countries AS SELECT * FROM sqlite_db.countries") conn.execute("CREATE TABLE states AS SELECT * FROM sqlite_db.states") conn.execute("CREATE TABLE cities AS SELECT * FROM sqlite_db.cities") # Detach SQLite database conn.execute("DETACH sqlite_db") print("Database imported successfully!") conn.close() ``` ```python theme={null} import duckdb conn = duckdb.connect('world.duckdb') # Check table counts result = conn.execute(""" SELECT 'countries' as table_name, COUNT(*) as count FROM countries UNION ALL SELECT 'states', COUNT(*) FROM states UNION ALL SELECT 'cities', COUNT(*) FROM cities """).fetchall() for row in result: print(f"{row[0]}: {row[1]}") conn.close() ``` Expected output: ``` countries: 250 states: 5299 cities: 153765 ``` ### Method 2: Using Conversion Script ```bash theme={null} wget https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/bin/import_duckdb.py ``` Or create your own script: ```python theme={null} import duckdb import sqlite3 import argparse import os def convert_sqlite_to_duckdb(sqlite_path, duckdb_path): """Convert SQLite database to DuckDB format""" # Connect to both databases sqlite_conn = sqlite3.connect(sqlite_path) duckdb_conn = duckdb.connect(duckdb_path) # Get all tables from SQLite sqlite_cursor = sqlite_conn.cursor() tables = sqlite_cursor.execute( "SELECT name FROM sqlite_master WHERE type='table'" ).fetchall() for table_name, in tables: print(f"Converting table: {table_name}") # Get table schema schema = sqlite_cursor.execute(f"PRAGMA table_info({table_name})").fetchall() # Create table in DuckDB columns = [] for col_info in schema: col_name = col_info[1] col_type = col_info[2] # Map SQLite types to DuckDB types if col_type.upper() == 'INTEGER': col_type = 'INTEGER' elif col_type.upper() in ['TEXT', 'VARCHAR']: col_type = 'VARCHAR' elif col_type.upper() in ['REAL', 'FLOAT', 'DOUBLE']: col_type = 'DOUBLE' columns.append(f"{col_name} {col_type}") create_sql = f"CREATE TABLE {table_name} ({', '.join(columns)})" duckdb_conn.execute(create_sql) # Copy data data = sqlite_cursor.execute(f"SELECT * FROM {table_name}").fetchall() if data: placeholders = ','.join(['?' for _ in data[0]]) insert_sql = f"INSERT INTO {table_name} VALUES ({placeholders})" duckdb_conn.executemany(insert_sql, data) print(f" Copied {len(data)} rows") # Close connections sqlite_conn.close() duckdb_conn.close() print(f"Conversion complete: {sqlite_path} -> {duckdb_path}") if __name__ == "__main__": parser = argparse.ArgumentParser(description='Convert SQLite to DuckDB') parser.add_argument('--input', required=True, help='Input SQLite file path') parser.add_argument('--output', required=True, help='Output DuckDB file path') args = parser.parse_args() convert_sqlite_to_duckdb(args.input, args.output) ``` ```bash theme={null} # Convert complete database python import_duckdb.py --input sqlite/world.sqlite3 --output duckdb/world.duckdb ``` ```bash theme={null} # Convert individual table databases if available python import_duckdb.py --input sqlite/countries.sqlite3 --output duckdb/countries.duckdb python import_duckdb.py --input sqlite/states.sqlite3 --output duckdb/states.duckdb python import_duckdb.py --input sqlite/cities.sqlite3 --output duckdb/cities.duckdb ``` ### Method 3: Command Line Interface ```bash theme={null} # On macOS brew install duckdb # On Ubuntu/Debian sudo apt install duckdb # Or download from https://duckdb.org/docs/installation/ ``` ```bash theme={null} # Start DuckDB CLI duckdb world.duckdb # Import from SQLite .mode csv .import sqlite/countries.csv countries .import sqlite/states.csv states .import sqlite/cities.csv cities # Or use SQL to attach SQLite ATTACH 'sqlite/world.sqlite3' AS sqlite_db; CREATE TABLE countries AS SELECT * FROM sqlite_db.countries; CREATE TABLE states AS SELECT * FROM sqlite_db.states; CREATE TABLE cities AS SELECT * FROM sqlite_db.cities; DETACH sqlite_db; ``` ## Performance Optimization ### Create Indexes and Views ```sql Indexes theme={null} -- Create indexes for better query performance CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); CREATE INDEX idx_countries_region ON countries(region); CREATE INDEX idx_states_country_id ON states(country_id); CREATE INDEX idx_states_name ON states(name); CREATE INDEX idx_cities_country_id ON cities(country_id); CREATE INDEX idx_cities_state_id ON cities(state_id); CREATE INDEX idx_cities_coordinates ON cities(latitude, longitude); CREATE INDEX idx_cities_name ON cities(name); ``` ```sql Analytical Views theme={null} -- Create views for common analytical queries CREATE VIEW countries_summary AS SELECT region, COUNT(*) as country_count, string_agg(DISTINCT currency, ', ') as currencies FROM countries WHERE region IS NOT NULL GROUP BY region; CREATE VIEW city_density_by_country AS SELECT country_name, COUNT(*) as city_count, AVG(TRY_CAST(latitude AS DOUBLE)) as avg_latitude, AVG(TRY_CAST(longitude AS DOUBLE)) as avg_longitude FROM cities GROUP BY country_name HAVING COUNT(*) > 10 ORDER BY city_count DESC; ``` ### DuckDB-Specific Optimizations ```sql Columnar Storage theme={null} -- DuckDB automatically uses columnar storage -- Create materialized views for frequently accessed data CREATE VIEW top_cities_by_country AS SELECT country_name, name as city_name, latitude, longitude, ROW_NUMBER() OVER (PARTITION BY country_name ORDER BY name) as rank FROM cities; ``` ```sql Parallel Processing theme={null} -- DuckDB automatically uses all CPU cores -- Configure thread count if needed SET threads = 4; -- Check current configuration SELECT * FROM duckdb_settings() WHERE name LIKE '%thread%'; ``` ## Connection Examples ```python theme={null} import duckdb import pandas as pd from typing import List, Dict, Any, Optional class WorldDatabase: def __init__(self, db_path: str = 'world.duckdb'): self.db_path = db_path self.conn = duckdb.connect(db_path) def query(self, sql: str, params: List = None) -> List[Dict]: """Execute query and return results as list of dictionaries""" if params: result = self.conn.execute(sql, params) else: result = self.conn.execute(sql) columns = [desc[0] for desc in result.description] return [dict(zip(columns, row)) for row in result.fetchall()] def query_df(self, sql: str, params: List = None) -> pd.DataFrame: """Execute query and return results as pandas DataFrame""" if params: return self.conn.execute(sql, params).df() else: return self.conn.execute(sql).df() def get_countries(self, region: Optional[str] = None, limit: Optional[int] = None) -> pd.DataFrame: sql = "SELECT * FROM countries" params = [] if region: sql += " WHERE region = ?" params.append(region) if limit: sql += f" LIMIT {limit}" return self.query_df(sql, params if params else None) def get_cities_by_country(self, country_name: str, limit: Optional[int] = None) -> pd.DataFrame: sql = "SELECT * FROM cities WHERE country_name = ?" params = [country_name] if limit: sql += f" LIMIT {limit}" return self.query_df(sql, params) def analyze_population_by_region(self) -> pd.DataFrame: """Analytical query example""" sql = """ SELECT region, COUNT(DISTINCT c.name) as countries, COUNT(DISTINCT s.name) as states, COUNT(DISTINCT cities.name) as cities, AVG(TRY_CAST(cities.latitude AS DOUBLE)) as avg_latitude, AVG(TRY_CAST(cities.longitude AS DOUBLE)) as avg_longitude FROM countries c LEFT JOIN states s ON c.id = s.country_id LEFT JOIN cities ON c.id = cities.country_id WHERE c.region IS NOT NULL GROUP BY c.region ORDER BY cities DESC """ return self.query_df(sql) def find_cities_near_coordinates(self, lat: float, lon: float, max_distance_km: float = 100) -> pd.DataFrame: """Find cities within a certain distance using Haversine formula""" sql = """ SELECT name, country_name, state_name, latitude, longitude, 6371 * acos( cos(radians(?)) * cos(radians(TRY_CAST(latitude AS DOUBLE))) * cos(radians(TRY_CAST(longitude AS DOUBLE)) - radians(?)) + sin(radians(?)) * sin(radians(TRY_CAST(latitude AS DOUBLE))) ) as distance_km FROM cities WHERE latitude IS NOT NULL AND longitude IS NOT NULL AND 6371 * acos( cos(radians(?)) * cos(radians(TRY_CAST(latitude AS DOUBLE))) * cos(radians(TRY_CAST(longitude AS DOUBLE)) - radians(?)) + sin(radians(?)) * sin(radians(TRY_CAST(latitude AS DOUBLE))) ) <= ? ORDER BY distance_km """ return self.query_df(sql, [lat, lon, lat, lat, lon, lat, max_distance_km]) def get_country_statistics(self) -> pd.DataFrame: """Get comprehensive country statistics""" sql = """ SELECT c.region, COUNT(DISTINCT c.name) as countries, COUNT(DISTINCT c.currency) as currencies, COUNT(DISTINCT s.name) as states, COUNT(cities.name) as cities, string_agg(DISTINCT c.currency, ', ') as currency_list FROM countries c LEFT JOIN states s ON c.id = s.country_id LEFT JOIN cities ON c.id = cities.country_id WHERE c.region IS NOT NULL GROUP BY c.region ORDER BY cities DESC """ return self.query_df(sql) def close(self): self.conn.close() # Usage example db = WorldDatabase() try: # Basic queries print("=== European Countries ===") europe_countries = db.get_countries(region="Europe", limit=5) print(europe_countries[['name', 'iso2', 'capital', 'currency']]) print("\n=== US Cities ===") us_cities = db.get_cities_by_country("United States", limit=5) print(us_cities[['name', 'state_name', 'latitude', 'longitude']]) # Analytical queries print("\n=== Population Analysis by Region ===") population_analysis = db.analyze_population_by_region() print(population_analysis) print("\n=== Cities near New York (within 100km) ===") nearby_cities = db.find_cities_near_coordinates(40.7128, -74.0060, 100) print(nearby_cities.head(10)) print("\n=== Country Statistics ===") stats = db.get_country_statistics() print(stats) finally: db.close() ``` ```r theme={null} library(duckdb) library(DBI) library(dplyr) # Connect to DuckDB con <- dbConnect(duckdb::duckdb(), dbdir = "world.duckdb", read_only = FALSE) # Helper function to query with dplyr query_db <- function(table_name) { tbl(con, table_name) } # Basic queries using dplyr syntax countries <- query_db("countries") %>% filter(region == "Europe") %>% select(name, iso2, iso3, capital, currency) %>% collect() print("European Countries:") print(countries) # Cities in United States us_cities <- query_db("cities") %>% filter(country_name == "United States") %>% select(name, state_name, latitude, longitude) %>% head(10) %>% collect() print("US Cities:") print(us_cities) # Analytical query - cities by region cities_by_region <- query_db("cities") %>% left_join(query_db("countries") %>% select(id, region), by = c("country_id" = "id")) %>% filter(!is.na(region)) %>% group_by(region) %>% summarise( city_count = n(), avg_latitude = mean(as.numeric(latitude), na.rm = TRUE), avg_longitude = mean(as.numeric(longitude), na.rm = TRUE) ) %>% arrange(desc(city_count)) %>% collect() print("Cities by Region:") print(cities_by_region) # Raw SQL query for complex analysis population_density <- dbGetQuery(con, " SELECT c.region, COUNT(DISTINCT c.name) as countries, COUNT(cities.name) as total_cities, ROUND(AVG(TRY_CAST(cities.latitude AS DOUBLE)), 2) as avg_lat, ROUND(AVG(TRY_CAST(cities.longitude AS DOUBLE)), 2) as avg_lon FROM countries c LEFT JOIN cities ON c.id = cities.country_id WHERE c.region IS NOT NULL GROUP BY c.region ORDER BY total_cities DESC ") print("Population Density by Region:") print(population_density) # Close connection dbDisconnect(con, shutdown = TRUE) ``` ```javascript theme={null} const duckdb = require('duckdb'); class WorldDatabase { constructor(dbPath = 'world.duckdb') { this.db = new duckdb.Database(dbPath); this.conn = this.db.connect(); } query(sql, params = []) { return new Promise((resolve, reject) => { if (params.length > 0) { this.conn.all(sql, ...params, (err, rows) => { if (err) reject(err); else resolve(rows); }); } else { this.conn.all(sql, (err, rows) => { if (err) reject(err); else resolve(rows); }); } }); } async getCountries(region = null, limit = null) { let sql = 'SELECT * FROM countries'; const params = []; if (region) { sql += ' WHERE region = ?'; params.push(region); } if (limit) { sql += ` LIMIT ${limit}`; } return await this.query(sql, params); } async getCitiesByCountry(countryName, limit = null) { let sql = 'SELECT * FROM cities WHERE country_name = ?'; const params = [countryName]; if (limit) { sql += ` LIMIT ${limit}`; } return await this.query(sql, params); } async analyzePopulationByRegion() { const sql = ` SELECT region, COUNT(DISTINCT c.name) as countries, COUNT(DISTINCT s.name) as states, COUNT(DISTINCT cities.name) as cities, ROUND(AVG(TRY_CAST(cities.latitude AS DOUBLE)), 2) as avg_latitude, ROUND(AVG(TRY_CAST(cities.longitude AS DOUBLE)), 2) as avg_longitude FROM countries c LEFT JOIN states s ON c.id = s.country_id LEFT JOIN cities ON c.id = cities.country_id WHERE c.region IS NOT NULL GROUP BY c.region ORDER BY cities DESC `; return await this.query(sql); } async findNearbyCities(lat, lon, maxDistanceKm = 100) { const sql = ` SELECT name, country_name, state_name, latitude, longitude, ROUND(6371 * acos( cos(radians(?)) * cos(radians(TRY_CAST(latitude AS DOUBLE))) * cos(radians(TRY_CAST(longitude AS DOUBLE)) - radians(?)) + sin(radians(?)) * sin(radians(TRY_CAST(latitude AS DOUBLE))) ), 2) as distance_km FROM cities WHERE latitude IS NOT NULL AND longitude IS NOT NULL AND 6371 * acos( cos(radians(?)) * cos(radians(TRY_CAST(latitude AS DOUBLE))) * cos(radians(TRY_CAST(longitude AS DOUBLE)) - radians(?)) + sin(radians(?)) * sin(radians(TRY_CAST(latitude AS DOUBLE))) ) <= ? ORDER BY distance_km LIMIT 20 `; return await this.query(sql, [lat, lon, lat, lat, lon, lat, maxDistanceKm]); } close() { this.conn.close(); this.db.close(); } } // Usage example async function example() { const db = new WorldDatabase(); try { console.log('=== European Countries ==='); const countries = await db.getCountries('Europe', 5); countries.forEach(country => { console.log(`${country.name} (${country.iso2}) - ${country.capital}`); }); console.log('\n=== US Cities ==='); const usCities = await db.getCitiesByCountry('United States', 5); usCities.forEach(city => { console.log(`${city.name}, ${city.state_name}`); }); console.log('\n=== Population Analysis ==='); const analysis = await db.analyzePopulationByRegion(); analysis.forEach(region => { console.log(`${region.region}: ${region.countries} countries, ${region.cities} cities`); }); console.log('\n=== Cities near New York ==='); const nearby = await db.findNearbyCity ies(40.7128, -74.0060, 100); nearby.slice(0, 10).forEach(city => { console.log(`${city.name}, ${city.state_name}: ${city.distance_km} km`); }); } catch (error) { console.error('Database error:', error); } finally { db.close(); } } example(); ``` ## Advanced Analytics Features ### Time Series Analysis ```python theme={null} # Example: Analyze city data distribution over time import duckdb import matplotlib.pyplot as plt conn = duckdb.connect('world.duckdb') # Create time-based analysis views conn.execute(""" CREATE OR REPLACE VIEW city_coordinates_analysis AS SELECT CASE WHEN TRY_CAST(latitude AS DOUBLE) >= 60 THEN 'Arctic' WHEN TRY_CAST(latitude AS DOUBLE) >= 23.5 THEN 'Northern Temperate' WHEN TRY_CAST(latitude AS DOUBLE) >= -23.5 THEN 'Tropical' WHEN TRY_CAST(latitude AS DOUBLE) >= -60 THEN 'Southern Temperate' ELSE 'Antarctic' END as climate_zone, COUNT(*) as city_count, AVG(TRY_CAST(latitude AS DOUBLE)) as avg_lat, AVG(TRY_CAST(longitude AS DOUBLE)) as avg_lon FROM cities WHERE latitude IS NOT NULL AND longitude IS NOT NULL GROUP BY climate_zone ORDER BY city_count DESC """) # Get results climate_data = conn.execute("SELECT * FROM city_coordinates_analysis").df() print(climate_data) ``` ### Geospatial Analytics ```python theme={null} # Advanced geospatial analysis def analyze_city_clusters(conn): """Find city clusters using spatial density""" sql = """ WITH city_grid AS ( SELECT name, country_name, FLOOR(TRY_CAST(latitude AS DOUBLE) / 5) * 5 as lat_grid, FLOOR(TRY_CAST(longitude AS DOUBLE) / 5) * 5 as lon_grid FROM cities WHERE latitude IS NOT NULL AND longitude IS NOT NULL ), grid_density AS ( SELECT lat_grid, lon_grid, COUNT(*) as city_count, string_agg(DISTINCT country_name, ', ') as countries FROM city_grid GROUP BY lat_grid, lon_grid HAVING COUNT(*) >= 5 ) SELECT * FROM grid_density ORDER BY city_count DESC LIMIT 20 """ return conn.execute(sql).df() # Usage conn = duckdb.connect('world.duckdb') clusters = analyze_city_clusters(conn) print("Top City Clusters:") print(clusters) ``` ## Data Science Integration ### Jupyter Notebook Integration ```python theme={null} # Install in Jupyter # !pip install duckdb pandas matplotlib seaborn import duckdb import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # Connect and create data for visualization conn = duckdb.connect('world.duckdb') # Create summary statistics summary_stats = conn.execute(""" SELECT region, COUNT(DISTINCT c.name) as countries, COUNT(cities.name) as cities, ROUND(AVG(TRY_CAST(cities.latitude AS DOUBLE)), 2) as avg_latitude, ROUND(AVG(TRY_CAST(cities.longitude AS DOUBLE)), 2) as avg_longitude FROM countries c LEFT JOIN cities ON c.id = cities.country_id WHERE c.region IS NOT NULL GROUP BY c.region ORDER BY cities DESC """).df() # Create visualizations plt.figure(figsize=(12, 8)) plt.subplot(2, 2, 1) sns.barplot(data=summary_stats, x='cities', y='region', orient='h') plt.title('Cities by Region') plt.subplot(2, 2, 2) sns.scatterplot(data=summary_stats, x='avg_longitude', y='avg_latitude', size='cities', hue='region') plt.title('Geographic Distribution') plt.tight_layout() plt.show() ``` ## Troubleshooting **Problem**: DuckDB installation fails **Solutions**: 1. Update pip: `pip install --upgrade pip` 2. Install specific version: `pip install duckdb==0.9.0` 3. Check Python version compatibility (3.7+) 4. Try installing from conda: `conda install -c conda-forge duckdb` 5. Verify system requirements and available memory **Problem**: Cannot import from SQLite database **Solutions**: 1. Verify SQLite file exists and is readable 2. Check SQLite file integrity: `sqlite3 world.sqlite3 "PRAGMA integrity_check;"` 3. Ensure DuckDB has read permissions 4. Try importing individual tables 5. Use absolute file paths **Problem**: Queries are running slowly **Solutions**: 1. Create appropriate indexes (see Performance section) 2. Increase thread count: `SET threads = 8` 3. Use columnar operations instead of row-by-row processing 4. Optimize SQL queries with `EXPLAIN` command 5. Consider using views for complex repeated queries **Problem**: Out of memory errors during processing **Solutions**: 1. Reduce memory usage: `SET memory_limit = '1GB'` 2. Process data in chunks using LIMIT and OFFSET 3. Use streaming operations instead of loading all data 4. Close connections when not needed 5. Monitor memory usage during operations ## Backup and Maintenance ### Backup Strategies ```bash File Copy Backup theme={null} # DuckDB files can be backed up by copying cp world.duckdb world_backup_$(date +%Y%m%d).duckdb # Compress backup gzip world_backup_$(date +%Y%m%d).duckdb ``` ```python Export to Other Formats theme={null} import duckdb conn = duckdb.connect('world.duckdb') # Export to CSV conn.execute("COPY countries TO 'backup_countries.csv' (FORMAT CSV, HEADER)") conn.execute("COPY cities TO 'backup_cities.csv' (FORMAT CSV, HEADER)") # Export to Parquet (efficient columnar format) conn.execute("COPY countries TO 'backup_countries.parquet' (FORMAT PARQUET)") conn.execute("COPY cities TO 'backup_cities.parquet' (FORMAT PARQUET)") # Export entire database to SQL conn.execute("EXPORT DATABASE 'backup_sql_dump'") ``` ### Maintenance Commands ```python theme={null} import duckdb conn = duckdb.connect('world.duckdb') # Check database size and statistics stats = conn.execute(""" SELECT table_name, estimated_size, column_count, row_count FROM duckdb_tables() WHERE table_name IN ('countries', 'states', 'cities') """).df() print("Table Statistics:") print(stats) # Optimize database (checkpoint and compress) conn.execute("CHECKPOINT") # Check query performance conn.execute("PRAGMA enable_profiling") result = conn.execute("SELECT COUNT(*) FROM cities WHERE country_name = 'United States'") conn.execute("PRAGMA disable_profiling") conn.close() ``` DuckDB automatically optimizes storage and doesn't require frequent maintenance like traditional databases. Regular backups and occasional checkpoints are usually sufficient. ## Integration with Data Science Stack ### Apache Arrow Integration ```python theme={null} # DuckDB integrates seamlessly with Apache Arrow import duckdb import pyarrow as pa conn = duckdb.connect('world.duckdb') # Query to Arrow Table arrow_table = conn.execute("SELECT * FROM countries WHERE region = 'Europe'").arrow() print(f"Arrow table with {arrow_table.num_rows} rows") # Convert back to pandas if needed df = arrow_table.to_pandas() ``` ### Polars Integration ```python theme={null} # DuckDB works great with Polars for fast data processing import duckdb import polars as pl conn = duckdb.connect('world.duckdb') # Query with Polars df = pl.read_database("SELECT * FROM cities WHERE country_name = 'United States'", conn) print(df.head()) ``` ## Next Steps After successful installation: 1. **Explore analytical queries** using DuckDB's powerful SQL extensions 2. **Integrate with your data science workflow** using pandas, R, or other tools 3. **Set up automated backups** using the provided scripts 4. **Experiment with advanced features** like spatial functions and time series analysis 5. **Consider scaling** to larger datasets using DuckDB's columnar storage Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for DuckDB-specific questions and data science integration tips. # Installation Overview Source: https://docs.countrystatecity.in/database/installation/index Choose your preferred database platform and follow the step-by-step installation guide The Countries States Cities database is available in multiple formats to support different technology stacks and use cases. Choose your preferred database platform and follow the corresponding installation guide. Get the latest database files from our GitHub releases page. ## Available Database Formats Most popular open-source relational database **Best for**: Web applications, high-traffic sites Advanced open-source relational database **Best for**: Complex queries, analytics, spatial data Lightweight file-based database **Best for**: Mobile apps, prototypes, embedded systems Document-oriented NoSQL database **Best for**: Flexible schemas, JSON-based applications Microsoft SQL Server compatible format **Best for**: Enterprise applications, Windows environments Analytical database for data science **Best for**: Analytics, data science, in-process OLAP ## Quick Comparison | Database | File Size | Installation Time | Best Use Case | | :- | :- | :- | :- | | SQLite | \~50MB | \< 1 minute | Development, mobile apps | | MySQL | \~200MB | 2-5 minutes | Web applications | | PostgreSQL | \~220MB | 3-6 minutes | Complex applications | | MongoDB | \~180MB | 2-4 minutes | Document-based apps | | SQL Server | \~250MB | 5-10 minutes | Enterprise environments | | DuckDB | \~60MB | 1-2 minutes | Analytics workloads | ## Prerequisites Before installing any database format: Download the appropriate database files from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database). ```bash Git Clone theme={null} git clone https://github.com/dr5hn/countries-states-cities-database.git cd countries-states-cities-database ``` ```bash Direct Download theme={null} # Download specific format files wget https://github.com/dr5hn/countries-states-cities-database/raw/master/mysql/world.sql ``` Ensure your target database system is installed and running. Each installation guide includes specific version requirements and compatibility information. ## What You'll Get After installation, your database will contain: * **250 Countries** with ISO codes, regions, and metadata * **5,299 States/Provinces** with administrative codes * **153,765 Cities** with coordinates and timezone data All data is regularly updated and includes comprehensive geographical information for worldwide coverage. ## Need Help? Check minimum hardware and software requirements Common installation issues and solutions Optimize your database for better performance Set up automated backups and disaster recovery *** ## System Requirements ### Minimum Hardware Requirements

Development Environment

  • • RAM: 2GB minimum, 4GB recommended
  • • Disk: 500MB free space
  • • CPU: Any modern processor

Production Environment

  • • RAM: 8GB minimum, 16GB+ recommended
  • • Disk: 2GB free space + growth capacity
  • • CPU: Multi-core processor recommended
### Software Version Compatibility * **MySQL**: 5.7+ (8.0+ recommended) * **PostgreSQL**: 10+ (13+ recommended) * **SQLite**: 3.25+ (3.35+ recommended) * **SQL Server**: 2016+ (2019+ recommended) * **MongoDB**: 4.0+ (5.0+ recommended) * **DuckDB**: 0.8+ (latest recommended) * **Python**: 3.7+ for DuckDB conversion * **Node.js**: 14+ for JavaScript examples * **PHP**: 7.4+ for web applications ## Performance Optimization ### Recommended Indexes ```sql theme={null} -- Performance indexes for common queries CREATE INDEX idx_cities_coordinates ON cities(latitude, longitude); CREATE INDEX idx_countries_region ON countries(region); CREATE INDEX idx_states_name ON states(name); CREATE INDEX idx_cities_name ON cities(name); ``` ### Query Optimization Tips * Enable query cache if available * Configure appropriate connection pooling * Set up read replicas for high-traffic applications * Consider partitioning large tables by region/country ## Backup Configuration Always implement a backup strategy before using the database in production. ```bash MySQL Backup theme={null} mysqldump -u root -p world > world_backup_$(date +%Y%m%d).sql ``` ```bash PostgreSQL Backup theme={null} pg_dump -U postgres world > world_backup_$(date +%Y%m%d).sql ``` ```bash SQLite Backup theme={null} # SQLite files can be backed up by copying the database file cp world.db world_backup_$(date +%Y%m%d).db ``` ```bash MongoDB Backup theme={null} mongodump --db world --out world_backup_$(date +%Y%m%d) ``` ## Troubleshooting ### Common Issues **Problem**: Special characters or emojis display incorrectly **Solution**: Ensure proper UTF-8 encoding ```sql theme={null} -- MySQL ALTER DATABASE world CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- PostgreSQL CREATE DATABASE world WITH ENCODING 'UTF8'; ``` **Problem**: Import process times out or fails **Solution**: Increase timeout limits and disable checks temporarily ```sql theme={null} -- MySQL SET session sql_mode = ''; SET foreign_key_checks = 0; SET autocommit = 0; -- Run import -- COMMIT; ``` **Problem**: Out of memory errors during import **Solution**: * Increase database memory limits * Import tables individually * Use smaller batch sizes # MongoDB Installation Source: https://docs.countrystatecity.in/database/installation/mongodb Complete guide to installing the Countries States Cities database in MongoDB with document-oriented data structure MongoDB is a popular document-oriented NoSQL database that stores data in flexible, JSON-like documents. This guide covers installing the Countries States Cities database in MongoDB using the provided BSON dump files. MongoDB installation typically takes 2-4 minutes and requires approximately 180MB of disk space. ## Prerequisites Before starting, ensure you have: * MongoDB 4.0+ installed (MongoDB 5.0+ recommended) * MongoDB server running on your system * At least 300MB free disk space * Downloaded the `mongodb-dump/world-mongodb-dump.tar.gz` file from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) MongoDB uses BSON (Binary JSON) format rather than SQL, so the installation process differs from relational databases. ## Installation Steps ```bash theme={null} tar -xzvf mongodb-dump/world-mongodb-dump.tar.gz ``` This extracts the MongoDB dump files to a `mongodb-dump/world` directory containing: * `regions.bson` and `regions.metadata.json` * `countries.bson` and `countries.metadata.json` * `states.bson` and `states.metadata.json` * `cities.bson` and `cities.metadata.json` ```bash theme={null} mongorestore --host localhost:27017 --db world mongodb-dump/world ``` Adjust the host and port parameters according to your MongoDB configuration. Default is usually localhost:27017. ```bash theme={null} mongo world --eval " printjson({ regions: db.regions.count(), countries: db.countries.count(), states: db.states.count(), cities: db.cities.count() }) " ``` Expected output: ```json theme={null} { "regions": 5, "countries": 250, "states": 5299, "cities": 153765 } ``` ## Alternative Installation Methods ### Using MongoDB Compass Open MongoDB Compass and connect to your MongoDB server. * Click "Create Database" * Database Name: "world" * Collection Name: "countries" (initial collection) For each collection (regions, countries, states, cities): * Select the collection * Click "ADD DATA" → "Import File" * Choose the corresponding `.json` file * Select JSON format and import ### Using Docker ```bash theme={null} docker run -d \ --name mongodb-csc \ -p 27017:27017 \ -v $(pwd)/mongodb-data:/data/db \ mongo:5.0 ``` ```bash theme={null} docker cp mongodb-dump/world mongodb-csc:/tmp/world ``` ```bash theme={null} docker exec mongodb-csc mongorestore --db world /tmp/world ``` ## Database Structure MongoDB stores the data in four main collections: ```javascript Regions Collection theme={null} // Sample region document { "_id": ObjectId("..."), "id": 1, "name": "Africa", "subregions": [ "Northern Africa", "Western Africa", "Eastern Africa", "Southern Africa", "Middle Africa" ] } ``` ```javascript Countries Collection theme={null} // Sample country document { "_id": ObjectId("..."), "id": 1, "name": "Afghanistan", "iso3": "AFG", "iso2": "AF", "numeric_code": "004", "phone_code": "93", "capital": "Kabul", "currency": "AFN", "currency_name": "Afghan afghani", "currency_symbol": "؋", "tld": ".af", "native": "افغانستان", "region": "Asia", "subregion": "Southern Asia", "timezones": [ { "zoneName": "Asia/Kabul", "gmtOffset": 16200, "gmtOffsetName": "UTC+04:30" } ], "latitude": "33.00000000", "longitude": "65.00000000" } ``` ```javascript Cities Collection theme={null} // Sample city document { "_id": ObjectId("..."), "id": 1, "name": "New York", "state_id": 3176, "state_code": "NY", "state_name": "New York", "country_id": 233, "country_code": "US", "country_name": "United States", "latitude": "40.71427000", "longitude": "-74.00597000", "wikiDataId": "Q60" } ``` ## MongoDB-Specific Operations ### Basic Queries ```javascript Find Operations theme={null} // Connect to the database use world // Find all countries in Europe db.countries.find({"region": "Europe"}).limit(5) // Find cities in California db.cities.find({ "country_code": "US", "state_code": "CA" }).limit(10) // Find countries with specific currency db.countries.find({"currency": "USD"}) // Find cities by coordinates (nearby locations) db.cities.find({ "latitude": {$gte: "40", $lte: "41"}, "longitude": {$gte: "-74", $lte: "-73"} }) ``` ```javascript Aggregation Queries theme={null} // Count cities by country db.cities.aggregate([ { $group: { _id: "$country_name", count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $limit: 10 } ]) // Average coordinates by region db.cities.aggregate([ { $lookup: { from: "countries", localField: "country_code", foreignField: "iso2", as: "country_info" } }, { $unwind: "$country_info" }, { $group: { _id: "$country_info.region", avgLat: { $avg: { $toDouble: "$latitude" } }, avgLng: { $avg: { $toDouble: "$longitude" } }, count: { $sum: 1 } } } ]) // Cities with timezone information db.cities.aggregate([ { $lookup: { from: "countries", localField: "country_code", foreignField: "iso2", as: "country" } }, { $unwind: "$country" }, { $unwind: "$country.timezones" }, { $project: { name: 1, country_name: 1, timezone: "$country.timezones.zoneName", gmtOffset: "$country.timezones.gmtOffset" } }, { $limit: 10 } ]) ``` ### Create Indexes ```javascript theme={null} // Essential indexes for query performance db.countries.createIndex({"iso2": 1}) db.countries.createIndex({"iso3": 1}) db.countries.createIndex({"region": 1}) db.countries.createIndex({"subregion": 1}) db.states.createIndex({"country_id": 1}) db.states.createIndex({"country_code": 1}) db.cities.createIndex({"state_id": 1}) db.cities.createIndex({"country_id": 1}) db.cities.createIndex({"country_code": 1}) db.cities.createIndex({"state_code": 1}) ``` ```javascript theme={null} // Create 2dsphere index for geospatial queries db.cities.createIndex({ "location": "2dsphere" }) // Add location field to existing documents db.cities.updateMany( {}, [{ $set: { location: { type: "Point", coordinates: [ { $toDouble: "$longitude" }, { $toDouble: "$latitude" } ] } } }] ) ``` ```javascript theme={null} // Create text indexes for search functionality db.countries.createIndex({ "name": "text", "native": "text", "capital": "text" }) db.cities.createIndex({ "name": "text", "state_name": "text", "country_name": "text" }) ``` ## Connection Examples ```python theme={null} from pymongo import MongoClient from pprint import pprint import json class WorldDatabase: def __init__(self, connection_string="mongodb://localhost:27017/"): self.client = MongoClient(connection_string) self.db = self.client.world def get_countries(self, region=None, limit=None): query = {} if region: query["region"] = region cursor = self.db.countries.find(query) if limit: cursor = cursor.limit(limit) return list(cursor) def get_cities_by_country(self, country_code, limit=None): query = {"country_code": country_code} cursor = self.db.cities.find(query) if limit: cursor = cursor.limit(limit) return list(cursor) def search_cities(self, search_term, limit=10): # Using text search return list(self.db.cities.find( {"$text": {"$search": search_term}}, {"score": {"$meta": "textScore"}} ).sort([("score", {"$meta": "textScore"})]).limit(limit)) def find_nearby_cities(self, longitude, latitude, max_distance=100000): # Geospatial query (requires 2dsphere index on location field) return list(self.db.cities.find({ "location": { "$near": { "$geometry": { "type": "Point", "coordinates": [longitude, latitude] }, "$maxDistance": max_distance } } })) def get_country_statistics(self): pipeline = [ { "$group": { "_id": "$region", "countries": {"$sum": 1}, "currencies": {"$addToSet": "$currency"} } }, {"$sort": {"countries": -1}} ] return list(self.db.countries.aggregate(pipeline)) def close(self): self.client.close() # Usage example db = WorldDatabase() try: # Get European countries europe_countries = db.get_countries(region="Europe", limit=5) print("European Countries:") for country in europe_countries: print(f" {country['name']} ({country['iso2']})") # Get US cities us_cities = db.get_cities_by_country("US", limit=5) print(f"\nUS Cities:") for city in us_cities: print(f" {city['name']}, {city['state_name']}") # Search cities search_results = db.search_cities("paris", limit=5) print(f"\nCities matching 'paris':") for city in search_results: print(f" {city['name']}, {city['country_name']}") # Country statistics stats = db.get_country_statistics() print(f"\nCountries by region:") for stat in stats: print(f" {stat['_id']}: {stat['countries']} countries") finally: db.close() ``` ```javascript theme={null} const { MongoClient } = require('mongodb'); class WorldDatabase { constructor(connectionString = 'mongodb://localhost:27017') { this.connectionString = connectionString; this.client = null; this.db = null; } async connect() { this.client = new MongoClient(this.connectionString); await this.client.connect(); this.db = this.client.db('world'); } async getCountries(region = null, limit = null) { const query = region ? { region } : {}; let cursor = this.db.collection('countries').find(query); if (limit) cursor = cursor.limit(limit); return await cursor.toArray(); } async getCitiesByCountry(countryCode, limit = null) { const query = { country_code: countryCode }; let cursor = this.db.collection('cities').find(query); if (limit) cursor = cursor.limit(limit); return await cursor.toArray(); } async searchCities(searchTerm, limit = 10) { return await this.db.collection('cities') .find( { $text: { $search: searchTerm } }, { projection: { score: { $meta: 'textScore' } } } ) .sort({ score: { $meta: 'textScore' } }) .limit(limit) .toArray(); } async findNearbyCities(longitude, latitude, maxDistance = 100000) { return await this.db.collection('cities').find({ location: { $near: { $geometry: { type: "Point", coordinates: [longitude, latitude] }, $maxDistance: maxDistance } } }).toArray(); } async getCountryStatistics() { const pipeline = [ { $group: { _id: "$region", countries: { $sum: 1 }, currencies: { $addToSet: "$currency" } } }, { $sort: { countries: -1 } } ]; return await this.db.collection('countries').aggregate(pipeline).toArray(); } async close() { if (this.client) { await this.client.close(); } } } // Usage example async function example() { const db = new WorldDatabase(); try { await db.connect(); // Get European countries const europeCountries = await db.getCountries('Europe', 5); console.log('European Countries:'); europeCountries.forEach(country => { console.log(` ${country.name} (${country.iso2})`); }); // Get US cities const usCities = await db.getCitiesByCountry('US', 5); console.log('\nUS Cities:'); usCities.forEach(city => { console.log(` ${city.name}, ${city.state_name}`); }); // Search cities const searchResults = await db.searchCities('paris', 5); console.log('\nCities matching "paris":'); searchResults.forEach(city => { console.log(` ${city.name}, ${city.country_name}`); }); // Country statistics const stats = await db.getCountryStatistics(); console.log('\nCountries by region:'); stats.forEach(stat => { console.log(` ${stat._id}: ${stat.countries} countries`); }); } catch (error) { console.error('Database error:', error); } finally { await db.close(); } } example(); ``` ```java theme={null} import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.mongodb.core.query.TextQuery; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.query.NearQuery; import java.util.List; public class WorldDatabase { private final MongoTemplate mongoTemplate; public WorldDatabase(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List getCountries(String region, Integer limit) { Query query = new Query(); if (region != null) { query.addCriteria(Criteria.where("region").is(region)); } if (limit != null) { query.limit(limit); } return mongoTemplate.find(query, Country.class, "countries"); } public List getCitiesByCountry(String countryCode, Integer limit) { Query query = new Query(Criteria.where("country_code").is(countryCode)); if (limit != null) { query.limit(limit); } return mongoTemplate.find(query, City.class, "cities"); } public List searchCities(String searchTerm, int limit) { TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny(searchTerm); Query query = TextQuery.queryText(criteria).sortByScore().limit(limit); return mongoTemplate.find(query, City.class, "cities"); } public List findNearbyCities(double longitude, double latitude, double maxDistanceKm) { Point location = new Point(longitude, latitude); Distance distance = new Distance(maxDistanceKm, Metrics.KILOMETERS); Query query = new Query(Criteria.where("location").near(location).maxDistance(distance.getNormalizedValue())); return mongoTemplate.find(query, City.class, "cities"); } } // Entity classes @Document(collection = "countries") public class Country { @Id private String id; private String name; private String iso2; private String iso3; private String region; private String subregion; private String capital; private String currency; private String phone_code; // Getters and setters... } @Document(collection = "cities") public class City { @Id private String id; private String name; private String state_name; private String country_name; private String country_code; private String state_code; private String latitude; private String longitude; // Getters and setters... } ``` ## Advanced Features ### Geospatial Queries ```javascript Setup Geospatial Data theme={null} // Add GeoJSON location field to all cities db.cities.updateMany( {}, [{ $set: { location: { type: "Point", coordinates: [ { $toDouble: "$longitude" }, { $toDouble: "$latitude" } ] } } }] ) // Create 2dsphere index db.cities.createIndex({ "location": "2dsphere" }) ``` ```javascript Geospatial Queries theme={null} // Find cities within 50km of New York db.cities.find({ location: { $near: { $geometry: { type: "Point", coordinates: [-74.006, 40.7128] }, $maxDistance: 50000 } } }) // Find cities within a polygon (e.g., a country boundary) db.cities.find({ location: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[ [-74.0, 40.7], [-74.0, 40.8], [-73.9, 40.8], [-73.9, 40.7], [-74.0, 40.7] ]] } } } }) ``` ### Data Validation ```javascript theme={null} // Add validation rules db.createCollection("countries", { validator: { $jsonSchema: { bsonType: "object", required: ["name", "iso2", "iso3"], properties: { name: { bsonType: "string", description: "must be a string and is required" }, iso2: { bsonType: "string", minLength: 2, maxLength: 2, description: "must be a 2-character string" }, iso3: { bsonType: "string", minLength: 3, maxLength: 3, description: "must be a 3-character string" } } } } }) ``` ## Troubleshooting **Problem**: `MongoNetworkError: connect ECONNREFUSED` **Solutions**: 1. Check if MongoDB service is running: `sudo systemctl status mongod` 2. Start MongoDB: `sudo systemctl start mongod` 3. Check MongoDB port: `netstat -an | grep 27017` 4. Verify connection string format 5. Check firewall settings **Problem**: `MongoServerError: Authentication failed` **Solutions**: 1. Check username/password in connection string 2. Verify user exists: `db.getUsers()` 3. Check user roles: `db.getUser("username")` 4. Create user with proper permissions: ```javascript theme={null} use admin db.createUser({ user: "worlduser", pwd: "password", roles: [{ role: "readWrite", db: "world" }] }) ``` **Problem**: `mongorestore` fails with various errors **Solutions**: 1. Check MongoDB version compatibility 2. Ensure sufficient disk space 3. Verify dump file integrity: `tar -tzf world-mongodb-dump.tar.gz` 4. Try importing individual collections: ```bash theme={null} mongorestore --db world --collection countries mongodb-dump/world/countries.bson ``` 5. Use `--drop` flag to overwrite existing data **Problem**: Queries are running slowly **Solutions**: 1. Create appropriate indexes (see Performance section) 2. Use `explain()` to analyze query performance 3. Optimize aggregation pipelines 4. Increase MongoDB cache size 5. Consider sharding for very large datasets ## Backup and Maintenance ### Backup Strategies ```bash Complete Database Backup theme={null} #!/bin/bash # backup_world_mongodb.sh DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/path/to/backups" DB_NAME="world" # Create dump mongodump --host localhost:27017 --db $DB_NAME --out "$BACKUP_DIR/mongodb_backup_$DATE" # Compress backup tar -czf "$BACKUP_DIR/world_mongodb_backup_$DATE.tar.gz" -C "$BACKUP_DIR" "mongodb_backup_$DATE" # Remove uncompressed files rm -rf "$BACKUP_DIR/mongodb_backup_$DATE" # Keep only last 7 backups find $BACKUP_DIR -name "world_mongodb_backup_*.tar.gz" -type f -mtime +7 -delete echo "Backup completed: world_mongodb_backup_$DATE.tar.gz" ``` ```bash Collection-Specific Backup theme={null} # Backup specific collections mongodump --db world --collection countries --out backup_countries mongodump --db world --collection cities --out backup_cities # Restore specific collection mongorestore --db world --collection countries backup_countries/world/countries.bson ``` ### Maintenance Tasks ```javascript theme={null} // Check collection statistics db.countries.stats() db.cities.stats() // Rebuild indexes db.countries.reIndex() db.cities.reIndex() // Check index usage db.cities.aggregate([{ $indexStats: {} }]) // Compact collections (offline operation) db.runCommand({ compact: "cities" }) ``` MongoDB automatically manages most maintenance tasks, but periodic index optimization and backup verification are recommended. ## Next Steps After successful installation: 1. **Create appropriate indexes** for your query patterns 2. **Set up replica sets** for high availability 3. **Configure sharding** if handling very large datasets 4. **Implement monitoring** with MongoDB Compass or third-party tools 5. **Set up automated backups** using the provided scripts Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for MongoDB-specific questions and NoSQL optimization tips. # MySQL Installation Source: https://docs.countrystatecity.in/database/installation/mysql Complete guide to installing the Countries States Cities database in MySQL with step-by-step instructions MySQL is one of the most popular open-source relational database management systems. This guide walks you through installing the Countries States Cities database in MySQL. MySQL installation typically takes 2-5 minutes and requires approximately 200MB of disk space. ## Prerequisites Before starting, ensure you have: * MySQL 5.7+ installed (MySQL 8.0+ recommended) * Administrative access to your MySQL server * At least 500MB free disk space * Downloaded the `mysql/world.sql` file from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) ## Method 1: Command Line Import ```bash theme={null} mysql -u root -p -e "CREATE DATABASE world CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" ``` We use utf8mb4 charset to properly handle Unicode characters including emojis and special characters. ```bash theme={null} mysql -u root -p world < mysql/world.sql ``` The import process typically takes 2-5 minutes depending on your system performance. ```sql theme={null} mysql -u root -p world -e " SELECT COUNT(*) as countries FROM countries; SELECT COUNT(*) as states FROM states; SELECT COUNT(*) as cities FROM cities; " ``` Expected output: ``` | countries | | 250 | | states | | 5299 | | cities | | 153765 | ``` ## Method 2: MySQL Workbench For users who prefer a graphical interface: Connect to your MySQL server instance. * Right-click in the Navigator panel * Select "Create Schema" * Name it "world" * Set charset to "utf8mb4" * Set collation to "utf8mb4\_unicode\_ci" * Go to Server → Data Import * Select "Import from Self-Contained File" * Choose the `mysql/world.sql` file * Select "world" as target schema * Click "Start Import" ## Advanced Configuration ### Performance Tuning for Large Imports ```sql Optimize Import Performance theme={null} -- Disable checks during import for better performance SET foreign_key_checks = 0; SET unique_checks = 0; SET autocommit = 0; -- Import the database here -- -- mysql -u root -p world < mysql/world.sql -- Re-enable checks after import SET foreign_key_checks = 1; SET unique_checks = 1; COMMIT; ``` ```sql Memory Configuration theme={null} -- Increase buffer pool size for better performance SET GLOBAL innodb_buffer_pool_size = 1073741824; -- 1GB -- Increase log file size for large imports SET GLOBAL innodb_log_file_size = 268435456; -- 256MB ``` ### Create Application User ```sql theme={null} CREATE USER 'csc_user'@'localhost' IDENTIFIED BY 'secure_password_here'; ``` Replace 'secure\_password\_here' with a strong, unique password. ```sql theme={null} -- For read-only access GRANT SELECT ON world.* TO 'csc_user'@'localhost'; -- For read-write access (if needed) GRANT SELECT, INSERT, UPDATE, DELETE ON world.* TO 'csc_user'@'localhost'; FLUSH PRIVILEGES; ``` ## Database Structure Verification After installation, verify the database structure: ```sql Table Information theme={null} -- Check all tables are created SHOW TABLES; -- Check table structures DESCRIBE countries; DESCRIBE states; DESCRIBE cities; ``` ```sql Sample Data Queries theme={null} -- View sample countries SELECT id, name, iso2, iso3, region FROM countries LIMIT 10; -- View states for a specific country SELECT id, name, state_code, country_id FROM states WHERE country_id = (SELECT id FROM countries WHERE iso2 = 'US') LIMIT 10; -- View cities with coordinates SELECT id, name, latitude, longitude, country_name, state_name FROM cities WHERE country_name = 'United States' LIMIT 10; ``` ## Performance Optimization ### Create Additional Indexes ```sql theme={null} -- Performance indexes for common queries CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); CREATE INDEX idx_countries_region ON countries(region); CREATE INDEX idx_states_country_id ON states(country_id); CREATE INDEX idx_states_name ON states(name); CREATE INDEX idx_cities_country_id ON cities(country_id); CREATE INDEX idx_cities_state_id ON cities(state_id); CREATE INDEX idx_cities_coordinates ON cities(latitude, longitude); CREATE INDEX idx_cities_name ON cities(name); ``` ### Update Table Statistics ```sql theme={null} -- Update statistics for query optimizer ANALYZE TABLE countries, states, cities; ``` ## Connection Examples ```php theme={null} setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Test query $stmt = $pdo->query("SELECT COUNT(*) as count FROM countries"); $result = $stmt->fetch(PDO::FETCH_ASSOC); echo "Countries in database: " . $result['count']; } catch(PDOException $e) { echo "Connection failed: " . $e->getMessage(); } ?> ``` ```python theme={null} import mysql.connector config = { 'user': 'csc_user', 'password': 'your_password', 'host': 'localhost', 'database': 'world', 'charset': 'utf8mb4' } try: connection = mysql.connector.connect(**config) cursor = connection.cursor() # Test query cursor.execute("SELECT COUNT(*) FROM countries") count = cursor.fetchone()[0] print(f"Countries in database: {count}") except mysql.connector.Error as err: print(f"Database error: {err}") finally: if connection.is_connected(): cursor.close() connection.close() ``` ```javascript theme={null} const mysql = require('mysql2/promise'); const config = { host: 'localhost', user: 'csc_user', password: 'your_password', database: 'world', charset: 'utf8mb4' }; async function testConnection() { try { const connection = await mysql.createConnection(config); // Test query const [rows] = await connection.execute('SELECT COUNT(*) as count FROM countries'); console.log(`Countries in database: ${rows[0].count}`); await connection.end(); } catch (error) { console.error('Database error:', error.message); } } testConnection(); ``` ## Troubleshooting ### Common Issues **Problem**: `ERROR 1045 (28000): Access denied for user` **Solutions**: 1. Verify username and password 2. Check user permissions: `SHOW GRANTS FOR 'csc_user'@'localhost';` 3. Ensure user can connect from your host 4. Try connecting as root first to verify server is running **Problem**: Special characters display as question marks or boxes **Solutions**: 1. Ensure database uses utf8mb4 charset 2. Set connection charset: `SET NAMES utf8mb4;` 3. Check client application charset settings 4. Verify file encoding when importing **Problem**: `ERROR 2006 (HY000): MySQL server has gone away` **Solutions**: 1. Increase max\_allowed\_packet: `SET GLOBAL max_allowed_packet=1073741824;` 2. Increase wait\_timeout: `SET GLOBAL wait_timeout=3600;` 3. Import in smaller batches 4. Use `--single-transaction` flag for mysqldump imports **Problem**: Queries are running slowly **Solutions**: 1. Add appropriate indexes (see Performance Optimization section) 2. Increase innodb\_buffer\_pool\_size 3. Update table statistics with ANALYZE TABLE 4. Use EXPLAIN to identify slow queries 5. Consider query optimization techniques ## Backup and Maintenance ### Create Automated Backup ```bash theme={null} #!/bin/bash # backup_world_db.sh DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/path/to/backups" DB_NAME="world" # Create backup mysqldump -u root -p --single-transaction --routines --triggers \ $DB_NAME > "$BACKUP_DIR/world_backup_$DATE.sql" # Compress backup gzip "$BACKUP_DIR/world_backup_$DATE.sql" # Keep only last 7 backups find $BACKUP_DIR -name "world_backup_*.sql.gz" -type f -mtime +7 -delete echo "Backup completed: world_backup_$DATE.sql.gz" ``` ### Regular Maintenance ```sql theme={null} -- Check table integrity CHECK TABLE countries, states, cities; -- Optimize tables OPTIMIZE TABLE countries, states, cities; -- Update statistics ANALYZE TABLE countries, states, cities; ``` Set up automated maintenance scripts to run during off-peak hours for optimal performance. ## Next Steps After successful installation: 1. **Set up regular backups** using the script above 2. **Configure monitoring** for database performance 3. **Implement connection pooling** for production applications 4. **Review security settings** and user permissions 5. **Test your application** connectivity and queries Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for MySQL-specific questions and optimization tips. # PostgreSQL Installation Source: https://docs.countrystatecity.in/database/installation/postgresql Complete guide to installing the Countries States Cities database in PostgreSQL with advanced features and spatial queries PostgreSQL is a powerful, advanced open-source relational database with excellent support for complex queries, JSON data, and spatial operations. This guide covers the complete installation process for the Countries States Cities database. PostgreSQL installation typically takes 3-6 minutes and requires approximately 220MB of disk space. ## Prerequisites Before starting, ensure you have: * PostgreSQL 10+ installed (PostgreSQL 13+ recommended) * Administrative access to your PostgreSQL server * At least 500MB free disk space * Downloaded the `pgsql/world.sql` file from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) ## Method 1: Command Line Import ```bash theme={null} createdb -U postgres world ``` Or using psql: ```sql theme={null} psql -U postgres -c "CREATE DATABASE world WITH ENCODING='UTF8';" ``` PostgreSQL uses UTF-8 encoding by default, which properly handles all international characters. ```bash theme={null} psql -U postgres -d world -f pgsql/world.sql ``` PostgreSQL import includes automatic index creation for optimal performance. ```bash theme={null} psql -U postgres -d world -c " SELECT 'countries' as table_name, COUNT(*) as count FROM countries UNION ALL SELECT 'states', COUNT(*) FROM states UNION ALL SELECT 'cities', COUNT(*) FROM cities; " ``` Expected output: ``` table_name | count ------------|-------- countries | 250 states | 5299 cities | 153765 ``` ## Method 2: pgAdmin GUI For users who prefer a graphical interface: Open pgAdmin and connect to your PostgreSQL server. * Right-click on "Databases" * Select "Create" → "Database" * Name: "world" * Encoding: "UTF8" * Template: "template0" * Right-click on the "world" database * Select "Query Tool" * Click the folder icon to open file * Select `pgsql/world.sql` * Execute the script (F5) ## PostgreSQL-Specific Features ### Spatial Queries with PostGIS ```sql theme={null} -- Enable PostGIS for advanced spatial queries (optional) CREATE EXTENSION IF NOT EXISTS postgis; ``` ```sql theme={null} -- Add geometry columns for spatial operations ALTER TABLE cities ADD COLUMN geom geometry(POINT, 4326); UPDATE cities SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326); -- Create spatial index CREATE INDEX idx_cities_geom ON cities USING GIST(geom); ``` ```sql theme={null} -- Find cities within 100km of New York City SELECT name, country_name, latitude, longitude FROM cities WHERE ST_DWithin( geom, ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326)::geography, 100000 -- 100km in meters ); -- Find nearest cities to a point SELECT name, country_name, ST_Distance(geom, ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326)::geography) as distance FROM cities ORDER BY geom <-> ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326) LIMIT 10; ``` ### Full-Text Search PostgreSQL provides powerful full-text search capabilities: ```sql Create Search Indexes theme={null} -- Create full-text search indexes CREATE INDEX idx_countries_name_fts ON countries USING gin(to_tsvector('english', name)); CREATE INDEX idx_cities_name_fts ON cities USING gin(to_tsvector('english', name)); CREATE INDEX idx_states_name_fts ON states USING gin(to_tsvector('english', name)); ``` ```sql Search Queries theme={null} -- Search for countries containing "united" SELECT name, region FROM countries WHERE to_tsvector('english', name) @@ to_tsquery('english', 'united'); -- Search cities with complex queries SELECT name, country_name, state_name FROM cities WHERE to_tsvector('english', name || ' ' || country_name) @@ to_tsquery('english', 'Los & Angeles | York & New'); -- Ranked search results SELECT name, country_name, ts_rank(to_tsvector('english', name), to_tsquery('english', 'francisco')) as rank FROM cities WHERE to_tsvector('english', name) @@ to_tsquery('english', 'francisco') ORDER BY rank DESC; ``` ## Advanced Configuration ### Create Application User and Database ```sql theme={null} CREATE ROLE csc_user WITH LOGIN PASSWORD 'secure_password_here'; ``` Replace 'secure\_password\_here' with a strong, unique password. ```sql theme={null} -- Grant database access GRANT CONNECT ON DATABASE world TO csc_user; -- Grant schema usage GRANT USAGE ON SCHEMA public TO csc_user; -- Grant table permissions GRANT SELECT ON ALL TABLES IN SCHEMA public TO csc_user; -- Grant permissions on future tables ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO csc_user; ``` ### Performance Optimization ```sql Additional Indexes theme={null} -- Performance indexes for common queries CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); CREATE INDEX idx_countries_region ON countries(region); CREATE INDEX idx_countries_subregion ON countries(subregion); CREATE INDEX idx_states_country_id ON states(country_id); CREATE INDEX idx_states_name ON states(name); CREATE INDEX idx_states_country_name ON states(country_name); CREATE INDEX idx_cities_country_id ON cities(country_id); CREATE INDEX idx_cities_state_id ON cities(state_id); CREATE INDEX idx_cities_name ON cities(name); CREATE INDEX idx_cities_coordinates ON cities(latitude, longitude); CREATE INDEX idx_cities_country_name ON cities(country_name); ``` ```sql Update Statistics theme={null} -- Update table statistics for query planner ANALYZE countries, states, cities; -- Check index usage SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes WHERE schemaname = 'public' ORDER BY idx_scan DESC; ``` ## Database Structure Verification ```sql Table Information theme={null} -- List all tables \dt -- Describe table structures \d countries \d states \d cities -- Check table sizes SELECT schemaname as schema, tablename as table, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size, pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) as table_size, pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename)) as index_size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; ``` ```sql Sample Data theme={null} -- View sample data from each table SELECT id, name, iso2, iso3, region, subregion FROM countries LIMIT 5; SELECT id, name, state_code, country_id, country_name FROM states WHERE country_name = 'United States' LIMIT 5; SELECT id, name, latitude, longitude, country_name, state_name FROM cities WHERE country_name = 'United States' AND state_name = 'California' LIMIT 5; ``` ## Connection Examples ```python theme={null} import psycopg2 from psycopg2.extras import RealDictCursor config = { 'host': 'localhost', 'database': 'world', 'user': 'csc_user', 'password': 'your_password', 'port': 5432 } try: connection = psycopg2.connect(**config) cursor = connection.cursor(cursor_factory=RealDictCursor) # Test query cursor.execute("SELECT COUNT(*) as count FROM countries") result = cursor.fetchone() print(f"Countries in database: {result['count']}") # Example query with parameters cursor.execute(""" SELECT name, latitude, longitude FROM cities WHERE country_name = %s LIMIT 5 """, ('United States',)) cities = cursor.fetchall() for city in cities: print(f"{city['name']}: {city['latitude']}, {city['longitude']}") except psycopg2.Error as error: print(f"Database error: {error}") finally: if connection: cursor.close() connection.close() ``` ```javascript theme={null} const { Pool } = require('pg'); const pool = new Pool({ user: 'csc_user', host: 'localhost', database: 'world', password: 'your_password', port: 5432, }); async function testConnection() { const client = await pool.connect(); try { // Test query const result = await client.query('SELECT COUNT(*) as count FROM countries'); console.log(`Countries in database: ${result.rows[0].count}`); // Example parameterized query const cities = await client.query( 'SELECT name, latitude, longitude FROM cities WHERE country_name = $1 LIMIT 5', ['United States'] ); cities.rows.forEach(city => { console.log(`${city.name}: ${city.latitude}, ${city.longitude}`); }); } catch (err) { console.error('Database error:', err.message); } finally { client.release(); } } testConnection(); ``` ```java theme={null} import java.sql.*; import java.util.Properties; public class PostgreSQLConnection { public static void main(String[] args) { String url = "jdbc:postgresql://localhost:5432/world"; Properties props = new Properties(); props.setProperty("user", "csc_user"); props.setProperty("password", "your_password"); try (Connection conn = DriverManager.getConnection(url, props)) { // Test query try (PreparedStatement stmt = conn.prepareStatement("SELECT COUNT(*) FROM countries")) { ResultSet rs = stmt.executeQuery(); if (rs.next()) { System.out.println("Countries in database: " + rs.getInt(1)); } } // Example query with parameters String query = "SELECT name, latitude, longitude FROM cities WHERE country_name = ? LIMIT 5"; try (PreparedStatement stmt = conn.prepareStatement(query)) { stmt.setString(1, "United States"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { System.out.printf("%s: %f, %f%n", rs.getString("name"), rs.getDouble("latitude"), rs.getDouble("longitude")); } } } catch (SQLException e) { System.err.println("Database error: " + e.getMessage()); } } } ``` ## JSON Support PostgreSQL has excellent JSON support. Here are some examples: ```sql JSON Aggregation theme={null} -- Aggregate cities by country as JSON SELECT country_name, json_agg( json_build_object( 'name', name, 'latitude', latitude, 'longitude', longitude ) ) as cities FROM cities WHERE country_name = 'Switzerland' GROUP BY country_name; ``` ```sql JSON Queries theme={null} -- If you have JSON columns, you can query them efficiently -- Example: Adding metadata as JSON ALTER TABLE countries ADD COLUMN metadata jsonb; -- Update with sample JSON data UPDATE countries SET metadata = jsonb_build_object( 'continent', region, 'area_km2', NULL, 'timezone_count', 1 ) WHERE region IS NOT NULL; -- Query JSON data SELECT name, metadata->>'continent' as continent FROM countries WHERE metadata->>'continent' = 'Europe'; ``` ## Troubleshooting **Problem**: `could not connect to server: Connection refused` **Solutions**: 1. Check if PostgreSQL service is running: `sudo systemctl status postgresql` 2. Verify PostgreSQL is listening on correct port: `netstat -an | grep 5432` 3. Check pg\_hba.conf for connection permissions 4. Ensure firewall allows connections on port 5432 **Problem**: `FATAL: password authentication failed` **Solutions**: 1. Verify username and password are correct 2. Check pg\_hba.conf authentication method 3. Reset password: `ALTER USER username PASSWORD 'newpassword';` 4. Ensure user has login privileges: `ALTER USER username LOGIN;` **Problem**: `ERROR: permission denied for table countries` **Solutions**: 1. Grant table permissions: `GRANT SELECT ON ALL TABLES IN SCHEMA public TO username;` 2. Check current permissions: `\dp` in psql 3. Ensure user has schema usage: `GRANT USAGE ON SCHEMA public TO username;` 4. Grant database connection: `GRANT CONNECT ON DATABASE world TO username;` **Problem**: Queries are running slowly **Solutions**: 1. Add appropriate indexes (see Performance Optimization section) 2. Update table statistics: `ANALYZE;` 3. Check query plan: `EXPLAIN ANALYZE your_query;` 4. Increase shared\_buffers in postgresql.conf 5. Consider using connection pooling (pgbouncer) ## Backup and Maintenance ### Create Backup Script ```bash theme={null} #!/bin/bash # backup_world_db.sh DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/path/to/backups" DB_NAME="world" # Create compressed backup pg_dump -U postgres -h localhost $DB_NAME | gzip > "$BACKUP_DIR/world_backup_$DATE.sql.gz" # Create custom format backup (faster restore) pg_dump -U postgres -h localhost -Fc $DB_NAME > "$BACKUP_DIR/world_backup_$DATE.dump" # Keep only last 7 backups find $BACKUP_DIR -name "world_backup_*.sql.gz" -type f -mtime +7 -delete find $BACKUP_DIR -name "world_backup_*.dump" -type f -mtime +7 -delete echo "Backup completed: world_backup_$DATE" ``` ### Regular Maintenance ```sql theme={null} -- Vacuum and analyze tables VACUUM ANALYZE countries, states, cities; -- Check database statistics SELECT schemaname, tablename, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE schemaname = 'public'; -- Check index usage SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read FROM pg_stat_user_indexes WHERE schemaname = 'public' ORDER BY idx_scan DESC; ``` PostgreSQL's autovacuum process handles most maintenance automatically, but manual VACUUM ANALYZE can help after large data changes. ## Next Steps After successful installation: 1. **Configure connection pooling** with pgbouncer for production 2. **Set up monitoring** with pg\_stat\_monitor or similar tools 3. **Implement backup strategy** using the provided script 4. **Explore spatial features** with PostGIS extension 5. **Optimize queries** using EXPLAIN ANALYZE Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for PostgreSQL-specific questions and advanced optimization tips. # SQL Server Installation Source: https://docs.countrystatecity.in/database/installation/sql-server Complete guide to installing the Countries States Cities database in Microsoft SQL Server with enterprise features Microsoft SQL Server is a powerful enterprise-grade relational database management system. This guide covers installing the Countries States Cities database in SQL Server using the provided SQL scripts. SQL Server installation typically takes 5-10 minutes and requires approximately 250MB of disk space. ## Prerequisites Before starting, ensure you have: * SQL Server 2016+ (SQL Server 2019+ recommended) * SQL Server Management Studio (SSMS) or Azure Data Studio * Administrative access to your SQL Server instance * At least 500MB free disk space * Downloaded the `sqlserver/world.sql` file from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) SQL Server provides advanced enterprise features like partitioning, columnstore indexes, and in-memory processing that can significantly improve performance for large datasets. ## Method 1: SQL Server Management Studio (SSMS) ```sql theme={null} CREATE DATABASE [world] ON ( NAME = 'world_data', FILENAME = 'C:\Data\world.mdf', SIZE = 100MB, MAXSIZE = 1GB, FILEGROWTH = 10MB ) LOG ON ( NAME = 'world_log', FILENAME = 'C:\Data\world.ldf', SIZE = 10MB, MAXSIZE = 100MB, FILEGROWTH = 5MB ); ``` Adjust the file paths according to your SQL Server data directory. Default is usually `C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\`. * Open SSMS and connect to your server * Open a new query window * Set the database context: `USE [world]` * Open the `sqlserver/world.sql` file * Execute the script (F5) The script includes table creation, data insertion, and index creation optimized for SQL Server. ```sql theme={null} USE [world]; SELECT 'countries' as table_name, COUNT(*) as record_count FROM countries UNION ALL SELECT 'states', COUNT(*) FROM states UNION ALL SELECT 'cities', COUNT(*) FROM cities; ``` Expected output: ``` table_name record_count countries 250 states 5299 cities 153765 ``` ## Method 2: Command Line (sqlcmd) ```bash theme={null} sqlcmd -S localhost -E -Q "CREATE DATABASE world" ``` Or for SQL Server Authentication: ```bash theme={null} sqlcmd -S localhost -U sa -P your_password -Q "CREATE DATABASE world" ``` ```bash theme={null} sqlcmd -S localhost -E -d world -i sqlserver/world.sql ``` Use `-U username -P password` instead of `-E` if not using Windows Authentication. ```bash theme={null} sqlcmd -S localhost -E -d world -Q " SELECT 'countries' as table_name, COUNT(*) as count FROM countries UNION ALL SELECT 'states', COUNT(*) FROM states UNION ALL SELECT 'cities', COUNT(*) FROM cities" ``` ## Method 3: Import/Export Wizard * Right-click on the database in SSMS * Select "Tasks" → "Import Data" * Choose "SQL Server Native Client" as data source * Server name: your SQL Server instance * Authentication: Windows or SQL Server * Database: select source if importing from another SQL Server * Choose your target `world` database * Select tables to import * Configure any data type mappings ## Advanced Configuration ### Create Application User and Login ```sql theme={null} USE master; CREATE LOGIN [csc_user] WITH PASSWORD = 'SecurePassword123!', DEFAULT_DATABASE = [world], CHECK_POLICY = ON, CHECK_EXPIRATION = OFF; ``` Use a strong password that meets your organization's security requirements. ```sql theme={null} USE [world]; CREATE USER [csc_user] FOR LOGIN [csc_user]; -- Grant permissions ALTER ROLE [db_datareader] ADD MEMBER [csc_user]; -- Add db_datawriter if write access needed -- ALTER ROLE [db_datawriter] ADD MEMBER [csc_user]; ``` ### Performance Optimization ```sql Additional Indexes theme={null} -- Performance indexes optimized for SQL Server CREATE NONCLUSTERED INDEX IX_Countries_ISO2 ON countries (iso2) INCLUDE (name, region); CREATE NONCLUSTERED INDEX IX_Countries_Region ON countries (region) INCLUDE (name, iso2, iso3); CREATE NONCLUSTERED INDEX IX_States_CountryID ON states (country_id) INCLUDE (name, state_code); CREATE NONCLUSTERED INDEX IX_Cities_CountryID ON cities (country_id) INCLUDE (name, state_id, latitude, longitude); CREATE NONCLUSTERED INDEX IX_Cities_StateID ON cities (state_id) INCLUDE (name, latitude, longitude); CREATE NONCLUSTERED INDEX IX_Cities_Coordinates ON cities (latitude, longitude) INCLUDE (name, country_name, state_name); ``` ```sql Columnstore Index theme={null} -- Create columnstore index for analytical queries CREATE NONCLUSTERED COLUMNSTORE INDEX NCCI_Cities_Analytics ON cities ( country_id, state_id, name, country_name, state_name, latitude, longitude ); ``` ```sql Partitioning theme={null} -- Partition large cities table by region (requires Enterprise edition) CREATE PARTITION FUNCTION PF_Region (NVARCHAR(100)) AS RANGE LEFT FOR VALUES ('Africa', 'Asia', 'Europe', 'North America', 'Oceania'); CREATE PARTITION SCHEME PS_Region AS PARTITION PF_Region TO (PRIMARY, PRIMARY, PRIMARY, PRIMARY, PRIMARY, PRIMARY); -- Apply partitioning when creating the table CREATE TABLE cities_partitioned ( -- columns... ) ON PS_Region (region); ``` ### Memory-Optimized Tables ```sql theme={null} -- Create memory-optimized filegroup (SQL Server 2014+) ALTER DATABASE [world] ADD FILEGROUP [world_memory] CONTAINS MEMORY_OPTIMIZED_DATA; ALTER DATABASE [world] ADD FILE ( name='world_memory', filename='C:\Data\world_memory' ) TO FILEGROUP [world_memory]; -- Create memory-optimized table for frequently accessed countries CREATE TABLE countries_memory ( id INT NOT NULL PRIMARY KEY NONCLUSTERED, name NVARCHAR(100) NOT NULL, iso2 NCHAR(2) NOT NULL, iso3 NCHAR(3) NOT NULL, region NVARCHAR(100), INDEX IX_Countries_Memory_ISO2 NONCLUSTERED (iso2), INDEX IX_Countries_Memory_Region NONCLUSTERED (region) ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA); ``` ## Database Structure Verification ```sql Table Information theme={null} -- Get table information SELECT t.name AS TableName, s.name AS SchemaName, p.rows AS RecordCount, CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS TotalSpaceMB, CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS UsedSpaceMB FROM sys.tables t INNER JOIN sys.indexes i ON t.object_id = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id INNER JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE t.name IN ('countries', 'states', 'cities') AND i.index_id <= 1 GROUP BY t.name, s.name, p.rows ORDER BY t.name; ``` ```sql Index Analysis theme={null} -- Check index usage statistics SELECT OBJECT_NAME(s.object_id) AS TableName, i.name AS IndexName, i.type_desc, s.user_seeks, s.user_scans, s.user_lookups, s.user_updates FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id WHERE OBJECT_NAME(s.object_id) IN ('countries', 'states', 'cities') ORDER BY TableName, IndexName; ``` ```sql Sample Data theme={null} -- View sample data with SQL Server specific functions SELECT TOP 5 id, name, iso2, iso3, region, LEN(name) as name_length, UPPER(iso2) as iso2_upper FROM countries; SELECT TOP 5 c.name, c.latitude, c.longitude, GEOGRAPHY::Point(c.latitude, c.longitude, 4326) as geo_point, c.country_name, c.state_name FROM cities c WHERE c.country_name = 'United States'; ``` ## Connection Examples ```csharp theme={null} using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; public class WorldDatabase : IDisposable { private readonly string _connectionString; private SqlConnection _connection; public WorldDatabase(string connectionString) { _connectionString = connectionString; _connection = new SqlConnection(_connectionString); _connection.Open(); } public List GetCountries(string region = null, int? limit = null) { var countries = new List(); var query = "SELECT id, name, iso2, iso3, region FROM countries"; var parameters = new List(); if (!string.IsNullOrEmpty(region)) { query += " WHERE region = @region"; parameters.Add(new SqlParameter("@region", region)); } if (limit.HasValue) { query += $" ORDER BY name OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY"; } using var command = new SqlCommand(query, _connection); command.Parameters.AddRange(parameters.ToArray()); using var reader = command.ExecuteReader(); while (reader.Read()) { countries.Add(new Country { Id = reader.GetInt32("id"), Name = reader.GetString("name"), Iso2 = reader.GetString("iso2"), Iso3 = reader.GetString("iso3"), Region = reader.IsDBNull("region") ? null : reader.GetString("region") }); } return countries; } public List GetCitiesByCountry(string countryName, int? limit = null) { var cities = new List(); var query = @" SELECT name, latitude, longitude, state_name, country_name FROM cities WHERE country_name = @countryName"; if (limit.HasValue) { query += $" ORDER BY name OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY"; } using var command = new SqlCommand(query, _connection); command.Parameters.AddWithValue("@countryName", countryName); using var reader = command.ExecuteReader(); while (reader.Read()) { cities.Add(new City { Name = reader.GetString("name"), Latitude = reader.IsDBNull("latitude") ? null : reader.GetDouble("latitude"), Longitude = reader.IsDBNull("longitude") ? null : reader.GetDouble("longitude"), StateName = reader.IsDBNull("state_name") ? null : reader.GetString("state_name"), CountryName = reader.GetString("country_name") }); } return cities; } public void Dispose() { _connection?.Dispose(); } } // Usage string connectionString = "Server=localhost;Database=world;Trusted_Connection=true;"; using var db = new WorldDatabase(connectionString); var countries = db.GetCountries("Europe", 5); foreach (var country in countries) { Console.WriteLine($"{country.Name} ({country.Iso2})"); } ``` ```python theme={null} import pyodbc from typing import List, Optional, Dict, Any class WorldDatabase: def __init__(self, connection_string: str): self.connection_string = connection_string self.connection = pyodbc.connect(connection_string) def get_countries(self, region: Optional[str] = None, limit: Optional[int] = None) -> List[Dict[str, Any]]: query = "SELECT id, name, iso2, iso3, region FROM countries" params = [] if region: query += " WHERE region = ?" params.append(region) if limit: query += f" ORDER BY name OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY" cursor = self.connection.cursor() cursor.execute(query, params) columns = [column[0] for column in cursor.description] return [dict(zip(columns, row)) for row in cursor.fetchall()] def get_cities_by_country(self, country_name: str, limit: Optional[int] = None) -> List[Dict[str, Any]]: query = """ SELECT name, latitude, longitude, state_name, country_name FROM cities WHERE country_name = ? """ if limit: query += f" ORDER BY name OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY" cursor = self.connection.cursor() cursor.execute(query, [country_name]) columns = [column[0] for column in cursor.description] return [dict(zip(columns, row)) for row in cursor.fetchall()] def search_cities_fulltext(self, search_term: str, limit: int = 10) -> List[Dict[str, Any]]: # Using SQL Server full-text search (requires full-text indexes) query = """ SELECT name, country_name, state_name, latitude, longitude FROM cities WHERE CONTAINS(name, ?) ORDER BY name OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY """ cursor = self.connection.cursor() cursor.execute(query, [search_term, limit]) columns = [column[0] for column in cursor.description] return [dict(zip(columns, row)) for row in cursor.fetchall()] def get_nearby_cities(self, latitude: float, longitude: float, radius_km: float = 100) -> List[Dict[str, Any]]: # Using SQL Server spatial functions query = """ SELECT name, country_name, state_name, latitude, longitude, geography::Point(?, ?, 4326).STDistance(geography::Point(latitude, longitude, 4326))/1000 as distance_km FROM cities WHERE geography::Point(?, ?, 4326).STDistance(geography::Point(latitude, longitude, 4326))/1000 <= ? ORDER BY distance_km """ cursor = self.connection.cursor() cursor.execute(query, [latitude, longitude, latitude, longitude, radius_km]) columns = [column[0] for column in cursor.description] return [dict(zip(columns, row)) for row in cursor.fetchall()] def close(self): self.connection.close() # Usage example connection_string = ( "DRIVER={ODBC Driver 17 for SQL Server};" "SERVER=localhost;" "DATABASE=world;" "Trusted_Connection=yes;" ) db = WorldDatabase(connection_string) try: # Get European countries countries = db.get_countries(region="Europe", limit=5) print("European Countries:") for country in countries: print(f" {country['name']} ({country['iso2']})") # Get US cities us_cities = db.get_cities_by_country("United States", limit=5) print(f"\nUS Cities:") for city in us_cities: print(f" {city['name']}, {city['state_name']}") # Find nearby cities (example: near New York) nearby = db.get_nearby_cities(40.7128, -74.0060, radius_km=50) print(f"\nCities near NYC:") for city in nearby[:5]: print(f" {city['name']}: {city['distance_km']:.1f} km") finally: db.close() ``` ```java theme={null} import java.sql.*; import java.util.*; public class WorldDatabase implements AutoCloseable { private final Connection connection; public WorldDatabase(String connectionString) throws SQLException { this.connection = DriverManager.getConnection(connectionString); } public List> getCountries(String region, Integer limit) throws SQLException { StringBuilder query = new StringBuilder("SELECT id, name, iso2, iso3, region FROM countries"); List params = new ArrayList<>(); if (region != null) { query.append(" WHERE region = ?"); params.add(region); } if (limit != null) { query.append(" ORDER BY name OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); params.add(limit); } try (PreparedStatement stmt = connection.prepareStatement(query.toString())) { for (int i = 0; i < params.size(); i++) { stmt.setObject(i + 1, params.get(i)); } ResultSet rs = stmt.executeQuery(); List> results = new ArrayList<>(); while (rs.next()) { Map row = new HashMap<>(); row.put("id", rs.getInt("id")); row.put("name", rs.getString("name")); row.put("iso2", rs.getString("iso2")); row.put("iso3", rs.getString("iso3")); row.put("region", rs.getString("region")); results.add(row); } return results; } } public List> getCitiesByCountry(String countryName, Integer limit) throws SQLException { StringBuilder query = new StringBuilder( "SELECT name, latitude, longitude, state_name, country_name FROM cities WHERE country_name = ?" ); if (limit != null) { query.append(" ORDER BY name OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); } try (PreparedStatement stmt = connection.prepareStatement(query.toString())) { stmt.setString(1, countryName); if (limit != null) { stmt.setInt(2, limit); } ResultSet rs = stmt.executeQuery(); List> results = new ArrayList<>(); while (rs.next()) { Map row = new HashMap<>(); row.put("name", rs.getString("name")); row.put("latitude", rs.getDouble("latitude")); row.put("longitude", rs.getDouble("longitude")); row.put("state_name", rs.getString("state_name")); row.put("country_name", rs.getString("country_name")); results.add(row); } return results; } } @Override public void close() throws SQLException { if (connection != null) { connection.close(); } } public static void main(String[] args) { String connectionString = "jdbc:sqlserver://localhost:1433;databaseName=world;integratedSecurity=true;"; try (WorldDatabase db = new WorldDatabase(connectionString)) { // Get European countries List> countries = db.getCountries("Europe", 5); System.out.println("European Countries:"); for (Map country : countries) { System.out.printf(" %s (%s)%n", country.get("name"), country.get("iso2")); } // Get US cities List> cities = db.getCitiesByCountry("United States", 5); System.out.println("\nUS Cities:"); for (Map city : cities) { System.out.printf(" %s, %s%n", city.get("name"), city.get("state_name")); } } catch (SQLException e) { System.err.println("Database error: " + e.getMessage()); } } } ``` ## Enterprise Features ### Spatial Data and Geography ```sql Spatial Data Types theme={null} -- Add geography column for precise spatial operations ALTER TABLE cities ADD geolocation GEOGRAPHY; -- Update geography data UPDATE cities SET geolocation = geography::Point(latitude, longitude, 4326) WHERE latitude IS NOT NULL AND longitude IS NOT NULL; -- Create spatial index CREATE SPATIAL INDEX IX_Cities_Geolocation ON cities (geolocation); ``` ```sql Spatial Queries theme={null} -- Find cities within 100km of New York DECLARE @center GEOGRAPHY = geography::Point(40.7128, -74.0060, 4326); SELECT name, country_name, state_name, @center.STDistance(geolocation)/1000 as distance_km FROM cities WHERE @center.STDistance(geolocation) <= 100000 ORDER BY distance_km; -- Find cities within a polygon DECLARE @polygon GEOGRAPHY = geography::STGeomFromText( 'POLYGON((-74.1 40.6, -74.1 40.8, -73.9 40.8, -73.9 40.6, -74.1 40.6))', 4326 ); SELECT * FROM cities WHERE @polygon.STContains(geolocation) = 1; ``` ### Full-Text Search ```sql Setup Full-Text Search theme={null} -- Create full-text catalog CREATE FULLTEXT CATALOG ft_world_catalog; -- Create full-text indexes CREATE FULLTEXT INDEX ON countries (name) KEY INDEX PK_Countries; CREATE FULLTEXT INDEX ON cities (name) KEY INDEX PK_Cities; ``` ```sql Full-Text Queries theme={null} -- Search for countries SELECT name, region FROM countries WHERE CONTAINS(name, '"United*"'); -- Search cities with ranking SELECT name, country_name, KEY_TBL.RANK FROM cities INNER JOIN CONTAINSTABLE(cities, name, 'paris OR london') AS KEY_TBL ON cities.id = KEY_TBL.[KEY] ORDER BY KEY_TBL.RANK DESC; ``` ## Troubleshooting **Problem**: `A network-related or instance-specific error occurred` **Solutions**: 1. Check if SQL Server service is running 2. Verify SQL Server Browser service is running 3. Check firewall settings (default port 1433) 4. Enable TCP/IP protocol in SQL Server Configuration Manager 5. Verify connection string format **Problem**: `Login failed for user` **Solutions**: 1. Check username and password 2. Verify SQL Server authentication mode (Mixed Mode vs Windows) 3. Check user permissions: `SELECT * FROM sys.server_principals` 4. Ensure user is mapped to database: `SELECT * FROM sys.database_principals` 5. Check if account is locked or disabled **Problem**: Import fails with memory errors **Solutions**: 1. Increase SQL Server max memory setting 2. Import data in smaller batches 3. Temporarily disable indexes during import 4. Check available disk space for tempdb 5. Monitor memory usage during import **Problem**: Queries are running slowly **Solutions**: 1. Check execution plans using `SET STATISTICS IO ON` 2. Create appropriate indexes (see Performance section) 3. Update statistics: `UPDATE STATISTICS table_name` 4. Check for missing indexes: `sys.dm_db_missing_index_details` 5. Consider columnstore indexes for analytical queries ## Backup and Maintenance ### Backup Strategy ```sql Full Database Backup theme={null} -- Create full backup BACKUP DATABASE [world] TO DISK = 'C:\Backups\world_full_backup.bak' WITH FORMAT, COMPRESSION, CHECKSUM; -- Verify backup RESTORE VERIFYONLY FROM DISK = 'C:\Backups\world_full_backup.bak'; ``` ```sql Automated Backup Script theme={null} -- Create backup job (requires SQL Server Agent) DECLARE @BackupPath NVARCHAR(500); SET @BackupPath = 'C:\Backups\world_' + FORMAT(GETDATE(), 'yyyyMMdd_HHmmss') + '.bak'; BACKUP DATABASE [world] TO DISK = @BackupPath WITH FORMAT, COMPRESSION, CHECKSUM, DESCRIPTION = 'Automated full backup of world database'; -- Clean up old backups (keep last 7) DECLARE @CleanupDate DATETIME = DATEADD(day, -7, GETDATE()); EXEC xp_delete_file 0, 'C:\Backups\', 'bak', @CleanupDate; ``` ### Maintenance Tasks ```sql Index Maintenance theme={null} -- Check index fragmentation SELECT OBJECT_NAME(i.object_id) AS TableName, i.name AS IndexName, s.avg_fragmentation_in_percent, s.page_count FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') s INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id WHERE s.avg_fragmentation_in_percent > 10 ORDER BY s.avg_fragmentation_in_percent DESC; -- Rebuild fragmented indexes ALTER INDEX ALL ON countries REBUILD WITH (ONLINE = ON); ALTER INDEX ALL ON cities REBUILD WITH (ONLINE = ON); ``` ```sql Statistics Maintenance theme={null} -- Update all statistics UPDATE STATISTICS countries WITH FULLSCAN; UPDATE STATISTICS states WITH FULLSCAN; UPDATE STATISTICS cities WITH FULLSCAN; -- Check statistics information SELECT t.name AS TableName, s.name AS StatName, STATS_DATE(s.object_id, s.stats_id) AS LastUpdated, s.auto_created, s.user_created FROM sys.stats s INNER JOIN sys.tables t ON s.object_id = t.object_id WHERE t.name IN ('countries', 'states', 'cities') ORDER BY LastUpdated; ``` Use SQL Server Maintenance Plans to automate backup, index maintenance, and statistics updates for production environments. ## Next Steps After successful installation: 1. **Configure security** with proper user roles and permissions 2. **Set up monitoring** with SQL Server Management Studio or third-party tools 3. **Implement backup strategy** including full, differential, and log backups 4. **Optimize for your workload** using columnstore indexes or partitioning 5. **Configure Always On** availability groups for high availability Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for SQL Server-specific questions and enterprise optimization tips. # SQLite Installation Source: https://docs.countrystatecity.in/database/installation/sqlite Complete guide to installing the Countries States Cities database in SQLite for lightweight, embedded applications SQLite is a lightweight, file-based database that requires no separate server process. It's perfect for mobile applications, prototypes, and embedded systems. This guide covers installing the Countries States Cities database in SQLite. SQLite installation is the fastest option, typically taking less than 1 minute with approximately 50MB file size. ## Prerequisites Before starting, ensure you have: * SQLite 3.25+ installed (SQLite 3.35+ recommended) * At least 100MB free disk space * Downloaded the `sqlite/world.sql` file from our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database) SQLite databases are self-contained files that don't require a separate server installation, making them ideal for development and embedded applications. ## Method 1: SQLite Command Line ```bash theme={null} sqlite3 world.db ``` This creates a new SQLite database file named `world.db` in your current directory. ```sql theme={null} .read sqlite/world.sql ``` SQLite import is typically the fastest due to its lightweight nature and single-file architecture. ```sql theme={null} -- List all tables .tables -- Check table schema .schema countries -- Count records in each table SELECT 'countries' as table_name, COUNT(*) as count FROM countries UNION ALL SELECT 'states', COUNT(*) FROM states UNION ALL SELECT 'cities', COUNT(*) FROM cities; ``` Expected output: ``` table_name|count countries|250 states|5299 cities|153765 ``` ## Method 2: Using Programming Languages ### Python Implementation ```bash theme={null} # SQLite3 is included in Python standard library # No additional installation needed ``` ```python theme={null} import sqlite3 import os def install_world_database(): # Connect to database (creates file if doesn't exist) conn = sqlite3.connect('world.db') cursor = conn.cursor() # Read and execute SQL file try: with open('sqlite/world.sql', 'r', encoding='utf-8') as f: sql_script = f.read() cursor.executescript(sql_script) print("Database installation completed successfully!") # Verify installation cursor.execute("SELECT COUNT(*) FROM countries") country_count = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM cities") city_count = cursor.fetchone()[0] print(f"Installed {country_count} countries and {city_count} cities") except FileNotFoundError: print("Error: sqlite/world.sql file not found") except Exception as e: print(f"Error during installation: {e}") finally: conn.close() if __name__ == "__main__": install_world_database() ``` ```bash theme={null} python install_world_db.py ``` ### Node.js Implementation ```bash theme={null} npm install sqlite3 ``` ```javascript theme={null} const sqlite3 = require('sqlite3').verbose(); const fs = require('fs'); const path = require('path'); function installWorldDatabase() { // Open database const db = new sqlite3.Database('world.db'); // Read SQL file const sqlPath = path.join(__dirname, 'sqlite', 'world.sql'); try { const sql = fs.readFileSync(sqlPath, 'utf8'); // Execute SQL db.exec(sql, (err) => { if (err) { console.error('Installation failed:', err); return; } console.log('Database installation completed successfully!'); // Verify installation db.get("SELECT COUNT(*) as count FROM countries", (err, row) => { if (!err) { console.log(`Installed ${row.count} countries`); } }); db.get("SELECT COUNT(*) as count FROM cities", (err, row) => { if (!err) { console.log(`Installed ${row.count} cities`); } db.close(); }); }); } catch (err) { console.error('Error reading SQL file:', err.message); } } installWorldDatabase(); ``` ```bash theme={null} node install_world_db.js ``` ## Database Structure Verification ```sql Table Information theme={null} -- List all tables .tables -- Show table schemas .schema countries .schema states .schema cities -- Get table info PRAGMA table_info(countries); PRAGMA table_info(states); PRAGMA table_info(cities); ``` ```sql Sample Queries theme={null} -- View sample countries SELECT id, name, iso2, iso3, region FROM countries LIMIT 10; -- View states for specific country SELECT id, name, state_code, country_name FROM states WHERE country_name = 'United States' LIMIT 10; -- View cities with coordinates SELECT id, name, latitude, longitude, country_name, state_name FROM cities WHERE country_name = 'Canada' LIMIT 10; ``` ## Performance Optimization ### Create Indexes ```sql theme={null} -- Essential indexes for query performance CREATE INDEX IF NOT EXISTS idx_countries_iso2 ON countries(iso2); CREATE INDEX IF NOT EXISTS idx_countries_iso3 ON countries(iso3); CREATE INDEX IF NOT EXISTS idx_countries_region ON countries(region); CREATE INDEX IF NOT EXISTS idx_states_country_id ON states(country_id); CREATE INDEX IF NOT EXISTS idx_states_name ON states(name); CREATE INDEX IF NOT EXISTS idx_states_country_name ON states(country_name); CREATE INDEX IF NOT EXISTS idx_cities_country_id ON cities(country_id); CREATE INDEX IF NOT EXISTS idx_cities_state_id ON cities(state_id); CREATE INDEX IF NOT EXISTS idx_cities_name ON cities(name); CREATE INDEX IF NOT EXISTS idx_cities_coordinates ON cities(latitude, longitude); CREATE INDEX IF NOT EXISTS idx_cities_country_name ON cities(country_name); ``` ### SQLite-Specific Optimizations ```sql PRAGMA Settings theme={null} -- Optimize SQLite for better performance PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA cache_size = 10000; PRAGMA temp_store = memory; PRAGMA mmap_size = 268435456; -- 256MB ``` ```sql Analyze Statistics theme={null} -- Update query planner statistics ANALYZE; -- Check index usage .eqp on SELECT * FROM cities WHERE country_name = 'United States' LIMIT 10; ``` ## Connection Examples ```python theme={null} import sqlite3 from contextlib import contextmanager class WorldDatabase: def __init__(self, db_path='world.db'): self.db_path = db_path @contextmanager def get_connection(self): conn = sqlite3.connect(self.db_path) conn.row_factory = sqlite3.Row # Enable column access by name try: yield conn finally: conn.close() def get_countries(self, limit=None): with self.get_connection() as conn: cursor = conn.cursor() query = "SELECT * FROM countries" if limit: query += f" LIMIT {limit}" return cursor.execute(query).fetchall() def get_cities_by_country(self, country_name, limit=None): with self.get_connection() as conn: cursor = conn.cursor() query = """ SELECT name, latitude, longitude, state_name FROM cities WHERE country_name = ? """ if limit: query += f" LIMIT {limit}" return cursor.execute(query, (country_name,)).fetchall() def search_cities(self, search_term, limit=10): with self.get_connection() as conn: cursor = conn.cursor() query = """ SELECT name, country_name, state_name, latitude, longitude FROM cities WHERE name LIKE ? ORDER BY name LIMIT ? """ return cursor.execute(query, (f'%{search_term}%', limit)).fetchall() # Usage example db = WorldDatabase() # Get all countries countries = db.get_countries(limit=10) for country in countries: print(f"{country['name']} ({country['iso2']})") # Get cities in a specific country us_cities = db.get_cities_by_country('United States', limit=5) for city in us_cities: print(f"{city['name']}, {city['state_name']}") # Search for cities london_cities = db.search_cities('London') for city in london_cities: print(f"{city['name']}, {city['country_name']}") ``` ```javascript theme={null} const sqlite3 = require('sqlite3').verbose(); class WorldDatabase { constructor(dbPath = 'world.db') { this.db = new sqlite3.Database(dbPath); } getCountries(limit = null) { return new Promise((resolve, reject) => { let query = "SELECT * FROM countries"; if (limit) query += ` LIMIT ${limit}`; this.db.all(query, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } getCitiesByCountry(countryName, limit = null) { return new Promise((resolve, reject) => { let query = ` SELECT name, latitude, longitude, state_name FROM cities WHERE country_name = ? `; if (limit) query += ` LIMIT ${limit}`; this.db.all(query, [countryName], (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } searchCities(searchTerm, limit = 10) { return new Promise((resolve, reject) => { const query = ` SELECT name, country_name, state_name, latitude, longitude FROM cities WHERE name LIKE ? ORDER BY name LIMIT ? `; this.db.all(query, [`%${searchTerm}%`, limit], (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } close() { this.db.close(); } } // Usage example async function example() { const db = new WorldDatabase(); try { // Get countries const countries = await db.getCountries(5); console.log('Countries:', countries); // Get US cities const usCities = await db.getCitiesByCountry('United States', 5); console.log('US Cities:', usCities); // Search for cities const londonCities = await db.searchCities('London'); console.log('Cities named London:', londonCities); } catch (error) { console.error('Database error:', error); } finally { db.close(); } } example(); ``` ```csharp theme={null} using Microsoft.Data.Sqlite; using System; using System.Collections.Generic; using System.Data; public class WorldDatabase : IDisposable { private readonly SqliteConnection _connection; public WorldDatabase(string dbPath = "world.db") { string connectionString = $"Data Source={dbPath}"; _connection = new SqliteConnection(connectionString); _connection.Open(); } public List GetCountries(int? limit = null) { var countries = new List(); string query = "SELECT id, name, iso2, iso3, region FROM countries"; if (limit.HasValue) query += $" LIMIT {limit}"; using var command = new SqliteCommand(query, _connection); using var reader = command.ExecuteReader(); while (reader.Read()) { countries.Add(new Country { Id = reader.GetInt32("id"), Name = reader.GetString("name"), Iso2 = reader.GetString("iso2"), Iso3 = reader.GetString("iso3"), Region = reader.IsDBNull("region") ? null : reader.GetString("region") }); } return countries; } public List GetCitiesByCountry(string countryName, int? limit = null) { var cities = new List(); string query = @" SELECT name, latitude, longitude, state_name FROM cities WHERE country_name = @countryName"; if (limit.HasValue) query += $" LIMIT {limit}"; using var command = new SqliteCommand(query, _connection); command.Parameters.AddWithValue("@countryName", countryName); using var reader = command.ExecuteReader(); while (reader.Read()) { cities.Add(new City { Name = reader.GetString("name"), Latitude = reader.IsDBNull("latitude") ? null : reader.GetDouble("latitude"), Longitude = reader.IsDBNull("longitude") ? null : reader.GetDouble("longitude"), StateName = reader.IsDBNull("state_name") ? null : reader.GetString("state_name") }); } return cities; } public void Dispose() { _connection?.Dispose(); } } public class Country { public int Id { get; set; } public string Name { get; set; } public string Iso2 { get; set; } public string Iso3 { get; set; } public string Region { get; set; } } public class City { public string Name { get; set; } public double? Latitude { get; set; } public double? Longitude { get; set; } public string StateName { get; set; } } // Usage example using var db = new WorldDatabase(); var countries = db.GetCountries(5); foreach (var country in countries) { Console.WriteLine($"{country.Name} ({country.Iso2})"); } var usCities = db.GetCitiesByCountry("United States", 5); foreach (var city in usCities) { Console.WriteLine($"{city.Name}, {city.StateName}"); } ``` ## Advanced Features ### Full-Text Search ```sql FTS Setup theme={null} -- Create FTS virtual table for cities CREATE VIRTUAL TABLE cities_fts USING fts5( name, country_name, state_name, content='cities' ); -- Populate FTS table INSERT INTO cities_fts SELECT name, country_name, state_name FROM cities; ``` ```sql FTS Queries theme={null} -- Search cities with full-text search SELECT c.name, c.country_name, c.state_name, c.latitude, c.longitude FROM cities_fts f JOIN cities c ON c.name = f.name AND c.country_name = f.country_name WHERE cities_fts MATCH 'paris OR london' ORDER BY rank; -- Phrase search SELECT * FROM cities_fts WHERE cities_fts MATCH '"new york"'; ``` ### JSON Support ```sql theme={null} -- SQLite 3.38+ supports JSON functions SELECT name, json_object( 'country', country_name, 'coordinates', json_array(latitude, longitude), 'timezone', timezone ) as city_json FROM cities WHERE country_name = 'Japan' LIMIT 5; ``` ## Troubleshooting **Problem**: `database is locked` error **Solutions**: 1. Close all connections to the database 2. Check for zombie processes: `fuser world.db` 3. Remove WAL files: `rm world.db-wal world.db-shm` 4. Use connection timeout: `sqlite3 -timeout 10000 world.db` **Problem**: `database disk image is malformed` **Solutions**: 1. Run integrity check: `.integrity_check` 2. Try to repair: `.recover world_recovered.db` 3. Restore from backup if available 4. Re-import from original SQL file **Problem**: Queries are slow **Solutions**: 1. Create appropriate indexes (see Performance Optimization) 2. Enable WAL mode: `PRAGMA journal_mode = WAL;` 3. Increase cache size: `PRAGMA cache_size = 10000;` 4. Use prepared statements 5. Consider using FTS for text searches **Problem**: High memory consumption **Solutions**: 1. Reduce cache size: `PRAGMA cache_size = 2000;` 2. Disable memory mapping: `PRAGMA mmap_size = 0;` 3. Use disk-based temp storage: `PRAGMA temp_store = file;` 4. Close connections promptly ## Backup and Maintenance ### Backup Strategies ```bash Simple Copy theme={null} # SQLite databases can be backed up by copying the file cp world.db world_backup_$(date +%Y%m%d).db # Compress backup gzip world_backup_$(date +%Y%m%d).db ``` ```bash Online Backup theme={null} # Backup while database is in use sqlite3 world.db ".backup world_backup_$(date +%Y%m%d).db" # Or using Python python -c " import sqlite3 import shutil from datetime import datetime src = sqlite3.connect('world.db') dst = sqlite3.connect(f'world_backup_{datetime.now().strftime(\"%Y%m%d\")}.db') src.backup(dst) dst.close() src.close() " ``` ### Maintenance Commands ```sql theme={null} -- Analyze for better query plans ANALYZE; -- Vacuum to reclaim space (offline operation) VACUUM; -- Check database integrity PRAGMA integrity_check; -- Get database info PRAGMA database_list; PRAGMA table_list; ``` SQLite databases are highly reliable but benefit from regular ANALYZE commands to keep query performance optimal. ## Next Steps After successful installation: 1. **Create indexes** for your specific use cases 2. **Implement connection pooling** for web applications 3. **Set up backup strategy** using the provided scripts 4. **Consider FTS** for text search functionality 5. **Monitor database size** and performance Join our [community discussions](https://github.com/dr5hn/countries-states-cities-database/discussions) for SQLite-specific questions and mobile development tips. # Database Overview Source: https://docs.countrystatecity.in/database/overview Complete geographical database with 250+ countries, 5,299+ states, and 153,765+ cities in multiple formats ## Introduction The Countries States Cities Database is the world's most comprehensive open-source geographical database, providing structured data for countries, states/provinces, and cities worldwide. With regular updates and multiple export formats, it serves as the foundation for countless applications requiring geographical data. Explore the complete database online with search and filtering capabilities. Access the latest database files in all supported formats from GitHub. ## Database Statistics Our database contains comprehensive geographical data covering the entire world:
6
Regions
22
Subregions
250
Countries
5,299
States
153,765
Cities
Last Updated: 27th May 2026 ## Available Formats The database is available in multiple formats to suit different technical requirements and use cases: ### Database Formats
* **MySQL** (.sql) - Popular open-source relational database * **PostgreSQL** (.sql) - Advanced open-source relational database * **SQLite** (.sqlite3) - Lightweight file-based database * **SQL Server** (.sql) - Microsoft SQL Server compatible format
* **MongoDB** (.tar.gz) - Document-oriented NoSQL database * **JSON** (.json) - Structured data format for APIs and applications
* **CSV** (.csv) - Comma-separated values for spreadsheets * **XML** (.xml) - Structured markup format * **YAML** (.yaml) - Human-readable data serialization * **GeoJSON** (.geojson) - Standard geospatial format for mapping libraries * **TOON** (.toon) - Token-Oriented Object Notation, \~40% fewer LLM tokens than JSON
* **DuckDB** - Analytical database (via conversion from SQLite) * **Parquet** (.parquet) - Columnar, Snappy-compressed format for analytics & OLAP tooling
## File Distribution Matrix Different data combinations are available depending on your needs: | File Type | JSON | MySQL | PostgreSQL | SQLite | SQL Server | MongoDB | XML | YAML | CSV | GeoJSON | TOON | Parquet | | - | - | - | - | - | - | - | - | - | - | - | - | - | | **Regions** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | | **Subregions** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | | **Countries** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **States** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **Cities** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **Country+States** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **Country+Cities** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **Complete World** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Hierarchical combinations (Country+States, Country+Cities) are primarily available in JSON format for optimal nested data representation. ## Key Features ### Comprehensive Coverage * **Complete geographical hierarchy** from regions down to cities * **Standardized naming** using official sources and international standards * **ISO codes** for countries (ISO 3166-1) and states where available * **Coordinate data** with latitude and longitude for cities * **Timezone information** for accurate time-based applications ### Data Quality * **Regular updates** with community contributions and automated monitoring * **Multiple validation layers** ensuring data consistency and accuracy * **Source attribution** for transparency and verification * **Version control** with detailed changelog tracking ### Developer-Friendly * **Multiple export formats** supporting different technology stacks * **Consistent schema** across all formats * **Well-documented** structure with clear relationships * **API integration** for real-time access alongside offline database usage ## Common Use Cases Address forms, shipping calculations, tax regions, and location-based services. Geographic data analysis, regional reporting, and location-based insights. Location selection, offline maps, regional content delivery. Census data integration, administrative boundaries, public service delivery. ## Quick Start Options Choose the approach that best fits your project: Download pre-built database files from [GitHub](https://github.com/dr5hn/countries-states-cities-database) in your preferred format. Perfect for offline applications or when you need complete control over the data. Use our [REST API](/api/introduction) for real-time data access without managing database files. Ideal for applications that need always up-to-date data and don't want to handle updates. Use our [Export Tool](/tools/export-tool) to create customized datasets with specific filters and formats. Great for specific regional focus or when you only need subset of the data. ## Next Steps Understand the database structure, tables, and relationships. Step-by-step instructions for setting up the database in different environments. While we strive for accuracy, geographical data can change due to political developments, administrative reorganization, or other factors. Always verify critical data against official sources when needed. # Postcodes Source: https://docs.countrystatecity.in/database/postcodes 844,248 postal codes across 125 countries with type classification, source tracking, and query examples The postcodes table contains **844,248 records** covering **125 countries**. Each record includes the postcode / ZIP code string, country, granularity type, source attribution, and locality name. This is informational postcode data, not an address verification or mail-deliverability service. A record confirms a code exists in the dataset — it does not confirm a delivery point, standardize an address, or guarantee mail will arrive. ## Coverage | Stat | Value | | - | - | | Total postcodes | 844,248 | | Countries covered | 125 | | Distinct `type` values in the current dataset | 5 (`full`, `area`, `fsa`, `po_box`, `street`) | `type` is a source-provided classification, not a fixed enum on either the database column or the API. It reflects whatever granularity each country's source data actually distinguishes, and can gain new values as more sources are imported — don't hardcode a switch over a closed list. ## Postcode Types The five values present in the current dataset, most to least common: | Type | Description | Countries | Example | | - | - | - | - | | `full` | Complete postcode identifying a specific delivery point or small area — the default for most countries | 113, e.g. Portugal, Mexico, Japan, US, Malta | `90210` (US) | | `area` | A broader geographic area code, used where the source data doesn't resolve to a specific delivery point | 11, e.g. Netherlands, Iran, UK, Isle of Man, Bermuda | `98714` (French Polynesia) | | `fsa` | Forward Sortation Area — the first 3 characters of a Canadian postal code, identifying a wide geographic area | Canada only | `A0A` | | `po_box` | Postal delivery via PO Box, distinct from a street-delivery code for the same area | South Africa only | `0001` (Pretoria) | | `street` | Street/physical-delivery code, distinct from a PO Box code for the same area | South Africa only | `0002` (Pretoria) | ## Query Examples ### Look up postcodes for a country ```sql MySQL / PostgreSQL theme={null} SELECT code, locality_name, type FROM postcodes WHERE country_code = 'IN' ORDER BY code LIMIT 20; ``` ```javascript Node.js theme={null} const response = await fetch('https://api.countrystatecity.in/v1/countries/IN/postcodes?limit=20', { headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' } }); const page = await response.json(); console.log(page.data); // array of postcode records console.log(page.pagination); // { limit, next_cursor, has_more } ``` ```python Python theme={null} import requests response = requests.get( 'https://api.countrystatecity.in/v1/countries/IN/postcodes', params={'limit': 20}, headers={'X-CSCAPI-KEY': 'YOUR_API_KEY'} ) page = response.json() postcodes = page['data'] ``` This listing endpoint requires a Starter+ plan — including a bare request with no `?q=` at all, to stop lower-tier keys from scraping the full dataset. See [Get Postcodes by Country](/api/endpoints/get-postcodes-by-country) for full query parameters, pagination, and tier-based field availability. For a single known code, [Get Postcode by Code](/api/endpoints/get-postcode-by-code) is free on every plan. ### Validate a postcode format Use the `postal_code_regex` field on the countries table to validate a postcode before querying: ```sql theme={null} SELECT c.name, c.postal_code_format, c.postal_code_regex FROM countries c WHERE c.iso2 = 'GB'; -- postal_code_format: "@# #@@" or "@## #@@" etc. -- postal_code_regex: GIR[ ]?0AA|... ``` ### Find postcodes by locality ```sql theme={null} SELECT code, country_code, type, locality_name FROM postcodes WHERE country_code = 'GB' AND locality_name ILIKE '%London%' ORDER BY type, code; ``` ### Count postcodes per country ```sql theme={null} SELECT country_code, COUNT(*) AS total, COUNT(DISTINCT type) AS types FROM postcodes GROUP BY country_code ORDER BY total DESC LIMIT 10; ``` ## Availability by Format The postcodes table is included in all SQL-based exports (MySQL, PostgreSQL, SQLite, SQL Server), the JSON export, and the CSV and YAML exports. It is **not** included in the GeoJSON export. Use the [Export Tool](https://export.countrystatecity.in) to download a postcodes-only dataset for specific countries, which is significantly smaller than the full dump. ## Related Exact lookup via the hosted API, free on every plan. Paginated listing and search via the hosted API. Requires Starter+. Full table definitions including the postcodes table structure. Export postcodes data in your preferred format. # Database Schema Source: https://docs.countrystatecity.in/database/schema Complete database structure, table definitions, relationships, and field specifications for the Countries States Cities database ## Database Architecture The Countries States Cities database follows a hierarchical structure with clear relationships between geographical entities. The schema is designed to be normalized, efficient, and consistent across all supported database formats.
```mermaid theme={null} erDiagram regions { int id PK string name string wikiDataId timestamp created_at timestamp updated_at } subregions { int id PK string name int region_id FK string wikiDataId timestamp created_at timestamp updated_at } countries { int id PK string name string iso3 string iso2 string numeric_code string phone_code string capital string currency string currency_name string currency_symbol string tld string native string region string subregion int region_id FK int subregion_id FK string nationality int population decimal gdp decimal area_sq_km string postal_code_format string postal_code_regex json timezones json translations decimal latitude decimal longitude string emoji string emojiU string wikiDataId timestamp created_at timestamp updated_at } states { int id PK string name int country_id FK string country_code string country_name string state_code string fips_code string iso3166_2 string type int level int parent_id FK string native string timezone json translations int population decimal latitude decimal longitude string wikiDataId timestamp created_at timestamp updated_at } cities { int id PK string name int state_id FK string state_code string state_name int country_id FK string country_code string country_name string type int level int parent_id FK string native int population string timezone json translations decimal latitude decimal longitude string wikiDataId timestamp created_at timestamp updated_at } postcodes { int id PK string code int country_id FK string country_code FK int state_id FK string state_code int city_id FK string locality_name string type decimal latitude decimal longitude string source string wikiDataId timestamp created_at timestamp updated_at } regions ||--o{ subregions : contains subregions ||--o{ countries : contains regions ||--o{ countries : contains countries ||--o{ states : contains states ||--o{ cities : contains countries ||--o{ postcodes : contains states ||--o{ postcodes : links cities ||--o{ postcodes : links ```
## Core Tables ### 1. Regions Table The top-level geographical divisions based on UN regional classifications. Unique identifier for the region (Primary Key) Official name of the region (e.g., "Europe", "Asia", "Africa") Wikidata identifier for additional reference data Record creation timestamp Last modification timestamp **Sample Data:** ```json theme={null} { "id": 3, "name": "Europe", "wikiDataId": "Q46", "created_at": "2023-01-15T10:00:00Z", "updated_at": "2023-01-15T10:00:00Z" } ``` ### 2. Subregions Table Subdivisions of regions providing more granular geographical classification. Unique identifier for the subregion (Primary Key) Official name of the subregion (e.g., "Western Europe", "Southern Asia") Foreign key reference to the parent region Wikidata identifier for additional reference data Record creation timestamp Last modification timestamp **Sample Data:** ```json theme={null} { "id": 155, "name": "Western Europe", "region_id": 3, "wikiDataId": "Q27496", "created_at": "2023-01-15T10:00:00Z", "updated_at": "2023-01-15T10:00:00Z" } ``` ### 3. Countries Table Complete country information with ISO codes, economic data, and geographical coordinates. Unique identifier for the country (Primary Key) Official English name of the country ISO 3166-1 alpha-3 code (e.g., "USA", "IND", "GBR") ISO 3166-1 alpha-2 code (e.g., "US", "IN", "GB") ISO 3166-1 numeric code International dialing code (e.g., "+1", "+91", "+44") Name of the country's capital city ISO 4217 currency code (e.g., "USD", "EUR", "INR") Full name of the currency Currency symbol (e.g., "\$", "€", "₹") Top-level domain (e.g., ".us", ".in", ".uk") Native name of the country in local language/script Region name (denormalized for quick access) Subregion name (denormalized for quick access) Foreign key reference to the regions table Foreign key reference to the subregions table Demonym for country citizens (e.g., "American", "Indian", "British") Geographical latitude coordinate of country center Geographical longitude coordinate of country center Unicode emoji flag representation Unicode code points for the flag emoji Country population (may be null for territories with no dedicated census) Gross domestic product in USD billions (approximate) Total area in square kilometres Human-readable postal code format pattern (e.g., `#####` for US zip codes) Regular expression for validating postal codes for this country JSON array of timezone objects for the country. Each object contains `zoneName`, `gmtOffset`, `gmtOffsetName`, `abbreviation`, and `tzName`. JSON object of country name translations keyed by locale code (e.g., `"fr": "États-Unis"`, `"de": "Vereinigte Staaten"`). 19 languages supported. Wikidata identifier for cross-referencing additional metadata **Sample Data:** ```json theme={null} { "id": 231, "name": "United States", "iso3": "USA", "iso2": "US", "numeric_code": "840", "phone_code": "+1", "capital": "Washington", "currency": "USD", "currency_name": "US Dollar", "currency_symbol": "$", "tld": ".us", "native": "United States", "region": "Americas", "subregion": "Northern America", "region_id": 2, "subregion_id": 21, "nationality": "American", "population": 331000000, "gdp": 23000.00, "area_sq_km": 9833517.00, "postal_code_format": "#####-####", "postal_code_regex": "^\\d{5}(-\\d{4})?$", "timezones": [ { "zoneName": "America/New_York", "gmtOffset": -18000, "gmtOffsetName": "UTC-05:00", "abbreviation": "EST", "tzName": "Eastern Standard Time" } ], "translations": { "fr": "États-Unis", "de": "Vereinigte Staaten", "hi": "संयुक्त राज्य", "zh-CN": "美国" }, "latitude": 37.09024000, "longitude": -95.71289100, "emoji": "🇺🇸", "emojiU": "U+1F1FA U+1F1F8", "wikiDataId": "Q30" } ``` ### 4. States Table Administrative divisions within countries (states, provinces, regions, etc.). Unique identifier for the state (Primary Key) Official name of the state/province/region Foreign key reference to the parent country ISO 2-letter country code for quick reference Country name (denormalized for performance) Official state/province code when available Type of administrative division (state, province, region, municipality, etc.) FIPS (Federal Information Processing Standards) code, primarily used for US states ISO 3166-2 subdivision code (e.g., `US-CA`, `IN-MH`, `GB-ENG`) Hierarchy level within the country. `1` = top-level region/state, `2` = department/province. Used in countries with multi-tier administration (France, Italy). Null for countries with a flat structure. Foreign key to the parent state row for level-2 subdivisions (e.g., a French department points to its region). Null for level-1 states. State name in the local/native language and script Primary IANA timezone identifier for the state (e.g., `America/Los_Angeles`) JSON object of state name translations keyed by locale code State population estimate Wikidata identifier for cross-referencing additional metadata Geographical latitude coordinate of state center Geographical longitude coordinate of state center **Sample Data:** ```json theme={null} { "id": 1416, "name": "California", "country_id": 231, "country_code": "US", "country_name": "United States", "state_code": "CA", "fips_code": "06", "iso3166_2": "US-CA", "type": "state", "level": 1, "parent_id": null, "native": "California", "timezone": "America/Los_Angeles", "translations": { "fr": "Californie", "de": "Kalifornien", "hi": "कैलिफ़ोर्निया" }, "population": 39538223, "wikiDataId": "Q99", "latitude": 36.77826100, "longitude": -119.41793240 } ``` ### 5. Cities Table Cities, towns, and other populated places within states/countries. Unique identifier for the city (Primary Key) Official name of the city/town Foreign key reference to the parent state State code for quick reference State name (denormalized for performance) Foreign key reference to the country ISO 2-letter country code for quick reference Country name (denormalized for performance) Place classification inherited from GeoNames (e.g., `city`, `town`, `adm2`, `county`). See the [city types reference](/database/city-types) for all 35 values and filtering guidance. Administrative hierarchy level within the state, mirroring the state-level structure where applicable Foreign key to the parent city row for nested subdivisions (e.g., a district within a city) City name in the local/native language and script City population estimate IANA timezone identifier for the city (e.g., `America/Los_Angeles`) JSON object of city name translations keyed by locale code Geographical latitude coordinate Geographical longitude coordinate Wikidata identifier for additional reference data **Sample Data:** ```json theme={null} { "id": 111968, "name": "Los Angeles", "state_id": 1416, "state_code": "CA", "state_name": "California", "country_id": 231, "country_code": "US", "country_name": "United States", "type": "adm2", "level": null, "parent_id": null, "native": "Los Angeles", "population": 3898747, "timezone": "America/Los_Angeles", "translations": { "fr": "Los Angeles", "de": "Los Angeles", "hi": "लॉस एंजेलेस" }, "latitude": 34.05223000, "longitude": -118.24368000, "wikiDataId": "Q65" } ``` ### 6. Postcodes Table 844,248 postal codes across 125 countries with type classification and source tracking. The authoritative column and API property is `code` — not `postcode`. Unique identifier for the postcode record (Primary Key) The postal / ZIP code string, as stored (e.g., `110001`, `GY1 1AA`, `11104`) ISO 3166-1 alpha-2 country code linking to the countries table State/province code, when the postcode is linked to one Name of the locality or area associated with this postcode A source-provided classification, not a fixed database enum — new values can appear as more sources are imported. The current dataset uses `full` (complete postcode, most countries), `area` (broader geographic area, e.g. Netherlands, Iran, UK), `fsa` (Canadian Forward Sortation Area), `po_box`, and `street` (South Africa's distinct PO Box vs. street-delivery codes). See [Postcode Types](/database/postcodes#postcode-types) for the full breakdown. Foreign key reference to the parent country Foreign key reference to the linked state, when applicable Foreign key reference to the linked city, when applicable Geographical latitude coordinate of the postcode area Geographical longitude coordinate of the postcode area Data source attribution for the postcode record (e.g. `openplz`, `wikidata`, `census`) Wikidata identifier for cross-referencing **Sample Data:** ```json theme={null} [ { "id": 131878, "code": "110001", "country_code": "IN", "state_code": "DL", "locality_name": "Sansad Marg", "type": "full", "country_id": 101, "state_id": 4021, "city_id": null, "latitude": null, "longitude": null, "source": "india-post", "wikiDataId": null }, { "id": 670115, "code": "11104", "country_code": "LK", "state_code": "12", "locality_name": "Mabola", "type": "full", "country_id": 208, "state_id": 2804, "city_id": null, "latitude": 7.00628330, "longitude": 79.89248280, "source": "sri-lanka-post-via-madurapa", "wikiDataId": null } ] ``` The postcodes table is available in the self-hosted database exports and via the hosted API — see [Get Postcode by Code](/api/endpoints/get-postcode-by-code) and [Get Postcodes by Country](/api/endpoints/get-postcodes-by-country). See the [postcodes reference](/database/postcodes) for coverage details and self-hosted query examples. **API access levels.** The hosted API tiers postcode fields the same way it tiers cities and countries: `id`, `code`, `country_code`, `state_code`, `locality_name`, and `type` are on every plan (**Basic**). `country_id`, `state_id`, `city_id`, `latitude`, `longitude`, `source`, and `wikiDataId` require **Supporter+**. `created_at`, `updated_at`, and `flag` are not exposed publicly. Self-hosted database exports are not access-tiered — all columns are present. ## Indexes and Performance ### Primary Indexes All tables include clustered primary key indexes on the `id` field for optimal performance. ### Foreign Key Indexes ```sql MySQL theme={null} -- States table indexes CREATE INDEX idx_states_country_id ON states(country_id); CREATE INDEX idx_states_country_code ON states(country_code); -- Cities table indexes CREATE INDEX idx_cities_state_id ON cities(state_id); CREATE INDEX idx_cities_country_id ON cities(country_id); CREATE INDEX idx_cities_country_code ON cities(country_code); -- Countries table indexes CREATE INDEX idx_countries_region_id ON countries(region_id); CREATE INDEX idx_countries_subregion_id ON countries(subregion_id); CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); -- Subregions table indexes CREATE INDEX idx_subregions_region_id ON subregions(region_id); ``` ```sql PostgreSQL theme={null} -- States table indexes CREATE INDEX idx_states_country_id ON states(country_id); CREATE INDEX idx_states_country_code ON states(country_code); -- Cities table indexes CREATE INDEX idx_cities_state_id ON cities(state_id); CREATE INDEX idx_cities_country_id ON cities(country_id); CREATE INDEX idx_cities_country_code ON cities(country_code); -- Countries table indexes CREATE INDEX idx_countries_region_id ON countries(region_id); CREATE INDEX idx_countries_subregion_id ON countries(subregion_id); CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); -- Subregions table indexes CREATE INDEX idx_subregions_region_id ON subregions(region_id); ``` ### Search Indexes For full-text search capabilities, consider adding text search indexes on name fields based on your database platform's capabilities. ## Data Types by Platform Different database platforms use slightly different data type specifications: ```sql theme={null} -- Example table definition for MySQL CREATE TABLE countries ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, iso2 CHAR(2) NOT NULL, iso3 CHAR(3) NOT NULL, latitude DECIMAL(10,8), longitude DECIMAL(11,8), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` ```sql theme={null} -- Example table definition for PostgreSQL CREATE TABLE countries ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, iso2 CHAR(2) NOT NULL, iso3 CHAR(3) NOT NULL, latitude DECIMAL(10,8), longitude DECIMAL(11,8), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` ```sql theme={null} -- Example table definition for SQLite CREATE TABLE countries ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, iso2 TEXT NOT NULL, iso3 TEXT NOT NULL, latitude REAL, longitude REAL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); ``` ## Relationships and Constraints ### Foreign Key Constraints Foreign key constraints are enforced in most SQL databases but may need to be handled at the application level in some NoSQL implementations. ```sql theme={null} -- Foreign key relationships ALTER TABLE subregions ADD CONSTRAINT fk_subregions_region FOREIGN KEY (region_id) REFERENCES regions(id); ALTER TABLE countries ADD CONSTRAINT fk_countries_region FOREIGN KEY (region_id) REFERENCES regions(id); ALTER TABLE countries ADD CONSTRAINT fk_countries_subregion FOREIGN KEY (subregion_id) REFERENCES subregions(id); ALTER TABLE states ADD CONSTRAINT fk_states_country FOREIGN KEY (country_id) REFERENCES countries(id); ALTER TABLE cities ADD CONSTRAINT fk_cities_state FOREIGN KEY (state_id) REFERENCES states(id); ALTER TABLE cities ADD CONSTRAINT fk_cities_country FOREIGN KEY (country_id) REFERENCES countries(id); ``` ### Data Integrity Rules * All primary keys must be non-null and unique * Country names, ISO codes are required * State and city names are required * Geographic coordinates are required for cities * ISO2 codes must be exactly 2 characters * ISO3 codes must be exactly 3 characters * Phone codes must start with "+" * Latitude must be between -90 and 90 * Longitude must be between -180 and 180 * States must belong to valid countries * Cities must belong to valid states and countries * Countries must belong to valid regions and subregions ## Common Queries Here are some frequently used query patterns: ```sql Get Country with States theme={null} SELECT c.name AS country_name, c.iso2, s.name AS state_name, s.state_code FROM countries c LEFT JOIN states s ON c.id = s.country_id WHERE c.iso2 = 'US' ORDER BY s.name; ``` ```sql Get Cities by State theme={null} SELECT ci.name AS city_name, ci.latitude, ci.longitude, s.name AS state_name, c.name AS country_name FROM cities ci JOIN states s ON ci.state_id = s.id JOIN countries c ON s.country_id = c.id WHERE s.country_code = 'US' AND s.state_code = 'CA' ORDER BY ci.name; ``` ```sql Regional Statistics theme={null} SELECT r.name AS region_name, COUNT(DISTINCT c.id) AS country_count, COUNT(DISTINCT s.id) AS state_count, COUNT(ci.id) AS city_count FROM regions r LEFT JOIN countries c ON r.id = c.region_id LEFT JOIN states s ON c.id = s.country_id LEFT JOIN cities ci ON s.id = ci.state_id GROUP BY r.id, r.name ORDER BY country_count DESC; ``` ## Next Steps Learn how to set up the database in your preferred environment. # Self-hosting and extending CSC data Source: https://docs.countrystatecity.in/database/self-hosting-and-custom-data How to run CSC data in your own database, add private records, apply updates safely, and keep proprietary data separate under ODbL You can download Country State City data, import it into a privately hosted database, and maintain your own records and relationships alongside it. Commercial use is permitted under the [Open Database License (ODbL) v1.0](https://opendatacommons.org/licenses/odbl/1-0/). The main design rule is simple: keep the CSC reference tables replaceable, and keep your application-owned data in separate tables. This page explains the CSC distribution model and our recommended technical architecture. It is not legal advice. If the licensing boundary is important to your business, ask qualified counsel to review your specific design. ## Recommended architecture Treat a CSC download or export as an upstream reference snapshot, not as the place where your application stores custom data. ```mermaid theme={null} flowchart LR release[CSC release or Export Tool snapshot] --> mirror[Read-only CSC reference tables] mirror --> links[Application mapping tables] custom[Custom places and destinations] --> app[Application-owned tables] links --> app app --> groups[Custom groups and relationships] ``` A typical MySQL deployment uses either two databases on the same server or two clearly separated sets of tables: | Layer | Example tables | Ownership | Update policy | | - | - | - | - | | CSC reference | `csc_countries`, `csc_states`, `csc_cities` | CSC snapshot | Replace only from a verified CSC release or export | | Application data | `destinations`, `app_places`, `place_groups` | Your application | Changed by your application | | Mapping | `destination_csc_links`, `place_group_members` | Your application | Links your records to CSC IDs | | Overrides | `place_overrides` | Your application | Optional display or business-specific values without editing CSC rows | This separation has three practical benefits: * A CSC refresh cannot delete your custom records. * You can compare or roll back CSC releases independently of application data. * The boundary between the ODbL reference database and proprietary application data remains easier to understand and document. ### Adding a missing place Do not insert a custom city into `csc_cities` or allocate an ID from the CSC ID space. Store it in an application-owned table with your own primary key. If useful, link it to a CSC country or state through a mapping column or table. For example, an application record can have: ```sql theme={null} CREATE TABLE app_places ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL, place_type VARCHAR(50) NOT NULL, csc_entity_type ENUM('country', 'state', 'city') DEFAULT NULL, csc_entity_id BIGINT UNSIGNED DEFAULT NULL, csc_parent_type ENUM('country', 'state', 'city') DEFAULT NULL, csc_parent_id BIGINT UNSIGNED DEFAULT NULL, latitude DECIMAL(10, 8) DEFAULT NULL, longitude DECIMAL(11, 8) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY app_places_csc_reference (csc_entity_type, csc_entity_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` * A CSC-backed place uses `csc_entity_type` and `csc_entity_id`. * A missing or application-specific place leaves those fields empty and can use `csc_parent_type` and `csc_parent_id` to identify its CSC parent. * Groups and parent/child relationships should reference `app_places.id`, so their IDs remain under your control. If a missing or incorrect place should benefit all CSC users, submit it through the [Update Tool](/tools/update-tool). Keep your private record until the change appears in a published CSC release, then link or reconcile it explicitly. ## Preserve identifiers without depending on them blindly Keep CSC's original `id` values unchanged inside the reference layer. They are required for relationships such as `states.country_id` and `cities.state_id`. Do not assume that a numeric ID can never change in a future release. Before switching releases, validate important mappings using additional identifiers where available: * Countries: `iso2` or `iso3` * States: `iso3166_2`, country code, and state code * Cities: `wikiDataId`, parent IDs, and country/state codes Store the CSC release or export version used by your application so an issue can be reproduced against the same snapshot. ## Download and Export Tool options ### Complete database downloads The [GitHub releases page](https://github.com/dr5hn/countries-states-cities-database/releases) publishes versioned, complete replacement assets in multiple formats. For MySQL, use the `sql-world.sql.gz` release asset. Import a release into a new or staging database rather than over tables containing custom data: ```bash theme={null} gunzip -c sql-world.sql.gz | mysql --default-character-set=utf8mb4 -u YOUR_USER -p csc_next ``` Inspect the SQL before importing it. Complete database dumps may create, drop, or replace CSC tables. Never run a replacement dump against tables that also contain your custom records. ### Custom Export Tool downloads The [Export Tool](https://export.countrystatecity.in) can produce a smaller snapshot for selected countries, datasets, fields, and formats. Choose the SQL format for a MySQL-ready export. An Export Tool file can be imported into your own MySQL database and maintained independently. It is a point-in-time export: it does not remain connected to CSC and does not automatically update your server. When exporting related datasets, include their relationship fields: * Countries: `id`, `iso2`, and `iso3` * States: `id`, `country_id`, `country_code`, `iso2`, and `iso3166_2` * Cities: `id`, `country_id`, `state_id`, `country_code`, and `state_code` ## Applying future updates Self-hosted downloads currently use a snapshot workflow: | Capability | Complete GitHub download | Export Tool | | - | - | - | | Versioned snapshot | Yes, by release tag | Yes, the export records its database version | | Complete replacement | Yes | Yes, for the selected scope and fields | | Incremental or delta file | No | No | | Automatic merge into your database | No | No | | Fresh data | Download a newer release | Renew or create a new export | The Professional and Business API plans also provide [`GET /v1/changes`](/api/endpoints/get-data-changes) for customers who need an incremental change feed. That API feature is separate from self-hosted download and Export Tool files. ### Safe refresh workflow Save the CSC release tag or Export Tool version, selected scope, fields, row counts, and file checksum. Back up both the CSC reference layer and your application-owned tables before changing production data. Load the new snapshot into a separate database such as `csc_next`. Do not overwrite the live CSC tables in place. Check row counts, required fields, parent references, and the CSC IDs used by your mapping tables. Review additions, removals, renamed records, and changed parents. Update only application mapping or override rows that no longer resolve cleanly. Your custom places, groups, and relationships remain untouched. Point the application to the validated snapshot or perform a controlled table swap. Keep the previous snapshot until production verification is complete. This process works for both complete releases and repeated Export Tool configurations. Export Tool renewal generates a fresh snapshot with the latest available version; it does not patch the previously downloaded file. ## Field availability API pricing controls which fields an API plan returns. It does not make the underlying ODbL fields exclusive to the API. The complete database downloads include the source schema. The Export Tool exposes selectable fields for countries, states, and cities, including the fields commonly requested for multi-level administrative data: * `type` * `level` * `parent_id` * `iso3166_2` on states * `fips_code` on states * `latitude` and `longitude` * `timezone` * `native` * `population` * `translations` * `wikiDataId` Field availability varies by entity, and some records legitimately contain null values. See the [database schema](/database/schema) and the live Export Tool field selector for the exact current list. Some API response fields are computed services rather than stored database columns. Examples include search relevance, distance from a query point, and current timezone offset or daylight-saving status. ## ODbL considerations ### Which terms apply The standard delivery routes do not change the licence on the underlying CSC data: | Delivery route | Data licence | Additional terms | | - | - | - | | GitHub release | ODbL v1.0 and the Database Contents License (DbCL) | GitHub's platform terms may apply to use of GitHub | | Export Tool | ODbL v1.0 and DbCL | Export Tool terms govern the account, credits, export service, and hosted download | | REST API | ODbL v1.0 and DbCL | API terms govern the account, API key, quotas, rate limits, and hosted service | Paying for Export Tool credits or an API plan does not currently grant a separate OEM licence or waive ODbL. It pays for the hosted service, selected scope and fields, generated formats, capacity, and support. The Export Tool uses the public CSC repository and release assets as its upstream geographic corpus. An export may not be byte-for-byte identical to a named GitHub release because it reflects the tool's recorded synchronization time, your field and country selections, output format, sorting, and optional name translation. ### Private and internal use ODbL permits commercial use. Section 4.5(c) also states that internal use of a derivative database is not public use, so you can privately modify and extend a self-hosted copy without publishing those internal changes. ### Public applications If an application publicly displays or otherwise uses a Produced Work based on a substantial part of CSC data, ODbL section 4.3 requires a notice that makes users aware of the source and licence. Use the CSC attribution: ```text theme={null} Data by Countries States Cities Database https://github.com/dr5hn/countries-states-cities-database | ODbL v1.0 ``` Also link to the [ODbL v1.0 licence](https://opendatacommons.org/licenses/odbl/1-0/). Network access without transferring a copy of the database is not **Conveying** under ODbL. That does not mean every SaaS use is exempt from share-alike: section 4.4(c) treats a derivative database as publicly used when a Produced Work from that derivative database is publicly used. ### Proprietary data and derivative databases ODbL distinguishes a **Derivative Database** from a **Collective Database**: * Public use of a derivative CSC database triggers the share-alike and machine-readable access requirements in sections 4.4 and 4.6. This can also apply when a public Produced Work is generated from that derivative database. * A collection of independent databases does not require the entire collection to use ODbL under section 4.5(a), although the CSC component remains under ODbL. * Creating a Produced Work from an unmodified CSC database does not by itself create a derivative database under section 4.5(b), but public attribution can still be required. Keeping CSC reference tables unmodified and keeping proprietary destination, tourism, grouping, and relationship data in separately owned tables makes this distinction clearer. A technical separation does not by itself determine the legal result; the substance of how the data is combined and publicly used still matters. ### What share-alike requires If you publicly use a derivative CSC database or a Produced Work from it, section 4.6 requires you to offer recipients one of the following in machine-readable form: * the entire derivative database; or * all alterations, or the method used to make them, including additional contents needed to recreate every difference from the CSC database. This does not require you to publish your application code, customer records, or unrelated proprietary tables. Whether a mapping or joined dataset is an independent component of a Collective Database or part of the derivative database depends on how it is combined and used, not only on whether it has a separate table name. ### Content sources and provenance CSC is a community-maintained database. Contributions may use official government data, ISO material, Wikidata, Wikipedia, GeoNames, OpenStreetMap, and other documented sources. The repository's [Database Contents License](https://github.com/dr5hn/countries-states-cities-database/blob/master/.github/CONTENT_LICENSE) grants the rights CSC can grant in the individual contents and requires compliance with ODbL. ODbL governs database rights, but section 2.4 notes that independent rights in individual contents can exist. CSC does not currently publish a complete row-by-row or field-by-field provenance and third-party licence register for every country, state, and city field. Source information for newer contributions is generally available in the pull request history, but is not embedded in every exported row. For legal, tax, regulatory, emergency, navigation, or official-boundary uses, obtain the controlling identifiers from the responsible authority, retain their source and version, and validate your mappings independently. The standard downloads, Export Tool, and API do not include a contractual field-by-field provenance warranty. ## Quick answers Yes. Import a complete MySQL release or a custom SQL export into a database you control. Yes. Use application-owned tables and IDs rather than inserting custom rows into the replaceable CSC tables. No. Download a new release or renew your Export Tool configuration, import it into staging, validate it, and switch it into service yourself. No. GitHub downloads and Export Tool files are snapshots. The [data change feed](/api/endpoints/get-data-changes) on the Professional and Business API plans is the incremental option. No. API plans govern API responses. The source fields remain available through complete database downloads, and the Export Tool exposes selectable fields according to its current field list. Internal use does not trigger ODbL public share-alike. For a public application, keep proprietary data separate from the CSC reference database and obtain legal advice if you need a definitive classification of your architecture. No. Standard paid products provide delivery, selection, format, capacity, updates, and support. They do not currently provide an OEM licence or waive ODbL. Displaying output from an unmodified CSC reference database does not itself create a derivative database, although public attribution can be required. If you transform a substantial part of CSC into a new database and publicly serve output from it, ODbL section 4.4(c) can trigger share-alike and section 4.6 access requirements. # Multi-Level Territories Source: https://docs.countrystatecity.in/database/territories How overseas territories and autonomous regions are dual-modeled as both countries and state subdivisions, and how to query them correctly Some geographical entities appear in this database at **two levels simultaneously** — as an independent ISO 3166-1 country *and* as an ISO 3166-2 subdivision of a parent country. This is not a bug; it reflects how the ISO standard itself models these territories. ## Why Dual Modeling Exists A handful of overseas or autonomous territories are listed by ISO 3166 at both levels: * **ISO 3166-1** assigns them their own two-letter country code (e.g. `MQ` for Martinique). * **ISO 3166-2** also lists them as subdivisions of a parent state (e.g. `FR-MQ` as a subdivision of France). Martinique *is* an integral region of the French Republic — its residents vote in French national elections, use the euro, and are EU citizens — but it also has its own TLD (`.mq`), its own ISO codes, and independent representation in certain international contexts. **Both representations are kept in sync. Neither is canonical; both are first-class.** ## The 12 French Overseas Territories | ISO 3166-1 | ISO 3166-2 | Name | State type | | :- | :- | :- | :- | | `GF` | `FR-GF` | French Guiana | overseas region | | `PF` | `FR-PF` | French Polynesia | overseas collectivity | | `TF` | `FR-TF` | French Southern and Antarctic Lands | overseas territory | | `GP` | `FR-GP` | Guadeloupe | overseas region | | `MQ` | `FR-MQ` | Martinique | overseas region | | `YT` | `FR-YT` | Mayotte | overseas region | | `NC` | `FR-NC` | New Caledonia | overseas collectivity | | `RE` | `FR-RE` | Réunion | overseas region | | `PM` | `FR-PM` | Saint Pierre and Miquelon | overseas collectivity | | `BL` | `FR-BL` | Saint-Barthélemy | overseas collectivity | | `MF` | `FR-MF` | Saint-Martin (French part) | overseas collectivity | | `WF` | `FR-WF` | Wallis and Futuna | overseas collectivity | ## Other Dual-Modeled Territories The same pattern applies to territories of other countries: | Parent | Territory | `countries.iso2` | Notes | | :- | :- | :- | :- | | `CN` | Hong Kong SAR | `HK` | special administrative region | | `CN` | Macau SAR | `MO` | special administrative region | | `US` | Puerto Rico | `PR` | outlying area | | `US` | Guam | `GU` | outlying area | | `US` | American Samoa | `AS` | outlying area | | `US` | Northern Mariana Islands | `MP` | outlying area | | `US` | U.S. Virgin Islands | `VI` | outlying area | | `NO` | Svalbard | `SJ` | arctic region | ## How to Query Pick the model that matches the question being asked. ### Everything in France (metropolitan + all overseas territories) Use the `FR` country and traverse via `state_id`: ```sql theme={null} SELECT c.* FROM cities c JOIN states s ON c.state_id = s.id WHERE s.country_code = 'FR'; -- includes all 12 overseas territories ``` ### Only Martinique (the territory in isolation) Filter by the territory's own ISO 3166-1 code: ```sql theme={null} SELECT * FROM cities WHERE country_code = 'MQ'; ``` ### Metropolitan France only (exclude overseas) Exclude the 12 overseas codes explicitly: ```sql theme={null} SELECT * FROM cities WHERE country_code = 'FR' AND state_code NOT IN ( 'GF','PF','TF','GP','MQ','YT','NC','RE','PM','BL','MF','WF', '971','972','973','974','976' -- INSEE codes for DROM ); ``` The five DROM territories (French Guiana, Guadeloupe, Martinique, Réunion, Mayotte) currently use INSEE numeric codes (`971`–`976`) as their `state_code`, while the overseas collectivities use ISO 3166-2 alphabetic codes. A future update will align all to ISO 3166-2 alphabetic format. ### JavaScript ```javascript theme={null} import { getCitiesOfCountry } from '@countrystatecity/countries'; // All French cities including overseas territories const allFrench = await getCitiesOfCountry('FR'); // Martinique only const martinique = await getCitiesOfCountry('MQ'); // Metropolitan France only const OVERSEAS = new Set(['GF','PF','TF','GP','MQ','YT','NC','RE','PM','BL','MF','WF']); const metropolitan = allFrench.filter(c => !OVERSEAS.has(c.country_code)); ``` ## Impact on API Responses When you query `/v1/countries`, both `MQ` (Martinique) and `FR` (France) appear as separate entries. Both return valid states and cities. This is intentional — it ensures that code doing `country_code === 'MQ'` continues to work correctly. If you are building country pickers or dropdowns, you may want to filter out the overseas territories from the top-level country list to avoid showing both "France" and "Martinique" as separate country options. Use the `region`/`subregion` fields or an explicit exclusion list. ## Related How the cities.type field works and how to filter to genuine settlements. Full table definitions including level and parent\_id fields on states and cities. # Country State City Source: https://docs.countrystatecity.in/index Complete geographical data ecosystem with API, database tools, and export utilities for developers worldwide The most comprehensive geographical data platform providing accurate, up-to-date information for **250 countries**, **5,299+ states/provinces**, and **153,765+ cities** worldwide. Our ecosystem includes a powerful REST API, database tools, and export utilities designed for developers building location-aware applications. All data includes ISO codes, coordinates, timezones, phone codes, currencies, and administrative hierarchies to support any geographical use case. ## Quick Start Get up and running with Country State City data in minutes. Register for free API access with 100 requests per day Learn the basics and make your first API call Browse and explore geographical data interactively ## Core Products ### 🌐 REST API Production-ready API serving geographical data with sub-100ms response times globally. Access data for 250 countries with ISO codes, phone codes, and currencies Get states, provinces, and regions for any country Access 153,765+ cities with coordinates and timezone data Secure API access with API keys ### 🗄️ Database Tools Self-hosted database solutions with installation guides for popular database systems. Complete MySQL setup and optimization guide PostgreSQL installation with performance tuning NoSQL setup for flexible geographical queries Lightweight database for mobile and desktop apps Analytics-focused in-process SQL database Enterprise-grade Microsoft SQL Server setup ### 🔧 Management Tools Keep your geographical data current with our update and export tools. Submit and track geographical data updates Export data in multiple formats (JSON, CSV, SQL, XML) ## Developer Resources ### 📦 Official Packages Use our offline npm and Python packages for bundled data with no API key or network requests, or the TypeScript SDK to call the live API in code for always-current data and search. Offline data packages plus an API-backed CLI and TypeScript SDK Type-safe Pydantic models with lazy loading for countries, states, and cities Live API client for Node.js and browser — search, usage tracking, and always-current data ### SDKs & Integration Build against live managed data with typed sync and async clients JavaScript/TypeScript and Python packages shipped today, more languages planned Real-world examples and integration patterns ## Use Cases Build location-aware checkout flows, shipping calculators, and address validation systems with accurate geographical data and timezone support. Enhance your analytics dashboards with geographical insights, regional performance metrics, and location-based user segmentation. Create location pickers, store locators, weather apps, and travel applications with comprehensive city and coordinate data. Support policy analysis, demographic studies, and administrative systems with standardized geographical hierarchies and ISO codes. ## Getting Started Decide whether you need API access, self-hosted database, or data exports based on your use case and requirements. [Register for a free API key](https://app.countrystatecity.in?source=sdk_docs\&campaign=sdk_api_migration) to start with 100 requests per day, or explore database installation guides for self-hosted solutions. Review our [API documentation](/api/introduction) or [database schema](/database/schema) to understand the available data structure and relationships. Follow our [code examples](/api/examples) or use one of our [official SDKs](/api/sdks) to integrate geographical data into your application. ## Support the Project Help improve geographical data accuracy *** **Need help?** Reach out to our team at [api@countrystatecity.in](mailto:api@countrystatecity.in). # Quickstart Source: https://docs.countrystatecity.in/quickstart Make your first request and build a country, state and city picker in under five minutes This guide takes you from zero to a working cascading location picker, the most common way developers use the CSC API. Sign up at [app.countrystatecity.in](https://app.countrystatecity.in) and copy your key from the dashboard. The free Community plan is enough for this guide. Keep your key on the server. See [Authentication](/api/authentication) for safe usage patterns. Every request needs the `X-CSCAPI-KEY` header. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` You will get back an array of countries, each with an `iso2` code such as `IN` or `US`. Pass the country's `iso2` code in the path. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/states' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` Pass both the country and state `iso2` codes. ```bash theme={null} curl 'https://api.countrystatecity.in/v1/countries/IN/states/MH/cities' \ -H 'X-CSCAPI-KEY: YOUR_API_KEY' ``` ## Put it together ```javascript Node.js theme={null} const BASE = 'https://api.countrystatecity.in/v1'; const headers = { 'X-CSCAPI-KEY': process.env.CSC_API_KEY }; const get = (path) => fetch(`${BASE}${path}`, { headers }).then((r) => r.json()); const countries = await get('/countries'); const states = await get('/countries/IN/states'); const cities = await get('/countries/IN/states/MH/cities'); console.log(countries.length, states.length, cities.length); ``` ```python Python theme={null} import os import requests BASE = "https://api.countrystatecity.in/v1" HEADERS = {"X-CSCAPI-KEY": os.environ["CSC_API_KEY"]} def get(path): return requests.get(f"{BASE}{path}", headers=HEADERS).json() countries = get("/countries") states = get("/countries/IN/states") cities = get("/countries/IN/states/MH/cities") print(len(countries), len(states), len(cities)) ``` ```php PHP theme={null} [ 'header' => 'X-CSCAPI-KEY: ' . getenv('CSC_API_KEY'), ]]); $get = fn ($path) => json_decode(file_get_contents($base . $path, false, $ctx), true); $countries = $get('/countries'); $states = $get('/countries/IN/states'); $cities = $get('/countries/IN/states/MH/cities'); ``` Country, state and city lists change rarely. Cache responses for at least 24 hours to stay well within your plan's rate limits. See [Errors and Rate Limits](/api/errors). ## Next steps Send live requests from any endpoint page with your own key. Official npm, Python and TypeScript packages. Return only the fields you need and sort results. Prefer no API calls? Download the full database. # FAQ Source: https://docs.countrystatecity.in/tools/export-tool/faq Common questions and answers about the CSC Export Tool including credits, data freshness, formats, billing, and troubleshooting. Find answers to common questions about the CSC Export Tool. Can't find what you're looking for? Contact us at [export@countrystatecity.in](mailto:export@countrystatecity.in). ## Getting Started Getting started is simple: 1. **Sign up** at [export.countrystatecity.in](https://export.countrystatecity.in) 2. **Choose your method**: GitHub (one-click) or Email (magic link) 3. **Get free trial credits** automatically added to your account 4. **Create your first export** following our [getting started guide](/tools/export-tool/getting-started) **Free Trial**: Every new user gets trial credits to test the system before purchasing additional credits. Your free trial credits allow you to test the system with various export options. **Example Uses:** * Countries export in JSON format * States export in JSON format * Format testing with different output types See how many trial credits you get and explore credit packages **Pro Tip**: Use your free credits to test different combinations, then purchase the package that matches your project needs! Export processing times depend on the dataset size and format: **Typical Processing Times:** * **Countries only**: 10-30 seconds * **Countries + States**: 30-60 seconds * **All datasets**: 1-3 minutes * **SQL format**: Additional 30-60 seconds for relationship processing **Factors affecting speed:** * Dataset size (Countries \< States \< Cities) * Export format (JSON fastest, SQL slowest) * Number of selected fields * Current system load You'll receive a notification when your export is ready for download. ## Credits & Billing Credits are used to generate exports based on datasets and formats: **Dataset Costs:** * **Countries**: 1 credit (\~250 countries) * **States**: 3 credits (\~5,299 states/provinces) * **Cities**: 4 credits (\~153,765 cities) **Format Costs (added to dataset cost):** * **JSON / NDJSON / XML / YAML**: +2 credits * **CSV / Markdown**: +3 credits * **SQL**: +4 credits * **Excel (.xlsx) / GeoJSON**: +4 credits * **PostgreSQL / SQL Server / SQLite3**: +5 credits * **MongoDB Extended JSON**: +3 credits Filters and add-ons (single or multi-region, multi-country, translation locales, sort, flag bundle) can add more. The preview always shows the exact cost before you confirm. **Example Calculations:** ``` Countries + States in JSON = 1 + 3 + 2 = 6 credits Countries in CSV = 1 + 3 = 4 credits Cities in SQL = 4 + 4 = 8 credits Cities in PostgreSQL = 4 + 5 = 9 credits All datasets in JSON = 1 + 3 + 4 + 2 = 10 credits ``` **No, credits never expire!** You can use them whenever you need to create new exports. **Credit Features:** * ✅ No expiration date * ✅ Use at your own pace * ✅ Accumulate credits across purchases * ✅ Transparent usage tracking * ✅ No hidden fees or recurring charges **Export Access:** * Exports are available for **30 days** after creation * After 30 days, you can renew expired exports for the same credit cost * This ensures you always get the most up-to-date geographical data We use **Dodo Payments** for secure, encrypted transactions: **Accepted Payment Methods:** * Visa, Mastercard, American Express * Discover, JCB, Diners Club * Apple Pay, Google Pay **Payment Features:** * 🔒 Secure SSL encryption * 💳 No stored payment information * 📧 Instant email receipts * 🚀 Credits added immediately * 🌍 International payments supported **Billing Process:** 1. Enter billing information 2. Select credit package 3. Complete secure payment 4. Credits added instantly to your account 5. Email confirmation sent **Our refund policy:** **Preview Before Purchase:** Use our free preview feature to see exactly what your export will contain before spending credits. **Refund Scenarios:** * ✅ **Technical issues**: Full refund if export fails due to our system errors * ✅ **Billing errors**: Immediate refund for incorrect charges * ❌ **Preview mismatch**: Refunds not available if export matches preview * ❌ **Changed requirements**: Credits spent on completed exports are non-refundable **Prevention Tips:** * Always use the **preview feature** (free) before creating exports * Review field selections and format choices carefully * Start with small test exports using trial credits * Contact support if you're unsure about an export **Important**: Preview your exports before confirming to ensure they meet your needs. This prevents unnecessary credit usage. We offer flexible credit packages to suit different needs, from individual credits to value packs with significant savings. **Smart Purchasing Strategy:** * Credits never expire, so larger packages offer better value * Larger packs provide maximum savings for frequent users * Share credits across team members * Calculate your project needs to choose the right package Compare packages and find the best option for your needs Contact us at [export@countrystatecity.in](mailto:export@countrystatecity.in) for enterprise needs. ## Data & Export Management Our data is **updated daily** to ensure accuracy: **Update Schedule:** * **Daily sync**: Database updated every 24 hours * **Source monitoring**: Continuous tracking of official sources * **Change detection**: Automatic identification of updates * **Quality assurance**: Validation before publishing **Data Sources:** * Official government databases * ISO country code registries * United Nations statistical databases * World Bank economic indicators * Geographic coordinate systems **What gets updated:** * New countries, states, or cities * Population changes * Economic indicators (GDP, currency changes) * Administrative boundary changes * Political changes (country names, capitals) **Always Current**: When you export data, you get the latest available information as of that day. **Export Download Period: 30 days** **What happens after 30 days:** * Export links expire (to ensure data freshness) * You can **renew** expired exports for the same credit cost * Renewal gives you updated data with any changes **Best Practices:** * Download exports immediately after creation * Save exports locally for your project needs * Set reminders for important exports before they expire * Consider renewal if you need updated data **Renewal Benefits:** * Same credit cost as original export * Updated with any data changes since original export * Fresh 30-day download window * Maintains your original field selection and format **Pro Tip**: For ongoing projects, plan exports around your development schedule to maximize the 30-day access window. **Use our country filtering feature!** **Available Filters:** * **Country selection**: Choose specific countries (US, CA, GB, etc.) * **Region filtering**: Select by region (Americas, Europe, Asia, etc.) * **Subregion filtering**: Narrow down to subregions (Northern America, Western Europe) **Benefits:** * Reduced file sizes * Faster processing * Cleaner datasets * Same credit costs **Example Use Cases:** ```json US-only States Export theme={null} { "filters": { "countries": ["US"] }, "datasets": ["states"], "fields": ["name", "state_code", "latitude", "longitude"] } // Result: Only US states (50 records instead of 4,000+) // Cost: Still 2 credits for states dataset ``` **Available in All Formats:** * JSON, CSV, XML, YAML, and SQL all support filtering * Same credit costs regardless of filtered data size * Filtering applied during export generation **Current Limitation:** We provide current data only, not historical versions. **What We Provide:** * ✅ Most current geographical data * ✅ Daily updates with latest changes * ✅ Real-time accuracy * ❌ Historical snapshots not available * ❌ Previous versions not stored **For Historical Analysis:** * Export and save data regularly for your own historical tracking * Use version control to track changes over time * Document important data snapshots for your projects **Future Plans:** We're considering historical data features for enterprise customers. Contact us if this is important for your use case. **Workaround:** Create regular exports (monthly/quarterly) and save them locally to build your own historical dataset. ## Technical Questions Each format serves different use cases and has different credit costs: **Best for:** Web applications, APIs, mobile apps **Advantages:** * Native JavaScript support * Nested structure preserves relationships * Lightweight and fast parsing * Perfect for REST APIs **Sample Structure:** ```json theme={null} { "countries": [...], "states": [...], "metadata": {...} } ``` **Best for:** Spreadsheets, data analysis, BI tools **Advantages:** * Excel/Google Sheets ready * Great for data science (Pandas, R) * Easy pivot table creation * Universal format support **Sample Structure:** ```csv theme={null} name,iso2,currency,population United States,US,USD,331002651 Canada,CA,CAD,37742154 ``` **Best for:** Enterprise systems, SOAP APIs, legacy systems **Advantages:** * Schema validation support * Hierarchical structure * Industry standard for enterprise * SOAP web service compatibility **Sample Structure:** ```xml theme={null} United States ``` **Best for:** Configuration files, documentation, DevOps **Advantages:** * Human-readable format * Great for config files * Kubernetes/Docker integration * Documentation-friendly **Sample Structure:** ```yaml theme={null} countries: - name: United States iso2: US currency: USD ``` **Best for:** Database seeding, data migration, production setup **Advantages:** * Ready-to-execute statements * Proper table relationships * Indexes and constraints included * Production database ready **Sample Structure:** ```sql theme={null} CREATE TABLE countries (...); INSERT INTO countries VALUES (...); CREATE INDEX idx_countries_iso2 ON countries (iso2); ``` **Yes!** You can combine Countries, States, and Cities in a single export: **Benefits of Combined Exports:** * ✅ Data consistency across datasets * ✅ Proper relationships maintained * ✅ Single download and processing * ✅ Unified timestamp and version **Cost Calculation:** Combined exports sum the individual dataset costs plus format cost: ``` Countries (1) + States (2) + Cities (3) + CSV (1) = 7 credits total ``` **Output Structure Examples:** ```json JSON Combined Export theme={null} { "countries": [ { "name": "United States", "iso2": "US", "states_count": 50, "cities_count": 19495 } ], "states": [ { "name": "California", "country_code": "US", "cities_count": 1538 } ], "cities": [ { "name": "Los Angeles", "state_name": "California", "country_name": "United States" } ] } ``` ```csv CSV Combined Export theme={null} # countries.csv name,iso2,states_count,cities_count United States,US,50,19495 # states.csv name,country_code,country_name,cities_count California,US,United States,1538 # cities.csv name,state_name,country_name,population Los Angeles,California,United States,3971883 ``` Integration depends on your chosen format and technology stack: **Web Applications (JSON):** ```javascript Frontend Integration theme={null} // Load and cache data const geoData = await fetch('/data/export.json').then(r => r.json()); // Create lookup maps for performance const countryMap = new Map(geoData.countries.map(c => [c.iso2, c])); const stateMap = new Map(geoData.states.map(s => [s.state_code, s])); // Use in components function CountryDropdown() { return ( ); } ``` **Database Integration (SQL):** ```bash Database Import theme={null} # PostgreSQL psql -d mydb -f csc_export.sql # MySQL mysql -u user -p mydb < csc_export.sql # SQLite sqlite3 mydb.db < csc_export.sql ``` **Data Analysis (CSV):** ```python Python/Pandas theme={null} import pandas as pd # Load data countries = pd.read_csv('countries.csv') states = pd.read_csv('states.csv') # Analysis regional_stats = countries.groupby('region').agg({ 'population': 'sum', 'gdp': 'mean' }) ``` **Configuration (YAML):** ```yaml Kubernetes Config theme={null} apiVersion: v1 kind: ConfigMap metadata: name: geo-config data: countries.yaml: | # CSC Export Data countries: - name: "United States" currency: "USD" ``` File sizes vary by dataset, fields selected, and format: **Approximate Sizes (All Fields):** **Countries Only:** * \~250 countries: 200-300 KB * With all fields: 500 KB - 1 MB **Countries + States:** * \~4,000 states: 2-4 MB * Compressed: 500 KB - 1 MB **All Datasets:** * \~153,765 cities: 50-100 MB * Compressed: 10-20 MB **Countries Only:** * \~250 countries: 100-200 KB * More compact than JSON **Countries + States:** * Multiple CSV files: 1-3 MB total * Efficient for spreadsheet use **All Datasets:** * Large CSV files: 30-80 MB * Good for data analysis tools **Countries Only:** * With indexes: 300-500 KB * Includes CREATE statements **Countries + States:** * With relationships: 3-6 MB * Production-ready structure **All Datasets:** * Complete database: 80-150 MB * Includes all optimizations **Optimization Tips:** * Select only needed fields to reduce size * Use country filters for regional data * JSON format is generally most compact * ZIP compression reduces download size by 60-80% ## Troubleshooting **Normal Processing Times:** * Countries: 10-30 seconds * States: 30-60 seconds * Cities: 1-3 minutes * SQL format: +30-60 seconds **If your export is taking longer:** **Check System Status:** 1. Visit our status page for any known issues 2. Peak usage times may cause delays (typically 9 AM - 5 PM EST) **Common Causes:** * Large dataset (Cities with all fields) * Complex SQL format processing * High system load during peak hours * Network connectivity issues **What To Do:** 1. **Wait patiently**: Large exports genuinely take time 2. **Check your network**: Ensure stable internet connection 3. **Contact support**: If over 10 minutes for any export **Optimization for Future:** * Use field filtering to reduce processing time * Consider JSON format for faster processing * Schedule large exports during off-peak hours **Status Updates**: You'll receive browser notifications when your export is ready, even if you close the tab. **Common Download Issues:** **Browser Issues:** * Try a different browser (Chrome, Firefox, Safari) * Disable browser extensions temporarily * Clear browser cache and cookies * Check if popup blockers are interfering **Network Issues:** * Check your internet connection * Try downloading on a different network * Use a VPN if experiencing regional restrictions * Restart your router/modem **File Size Issues:** * Large files may timeout on slow connections * Try using a download manager for large files * Consider splitting large exports into smaller ones **Account Issues:** * Ensure you're logged into the correct account * Check if the export has expired (30-day limit) * Verify you have permission to download **Quick Fixes:** 1. **Refresh the page** and try again 2. **Right-click and "Save Link As"** instead of direct click 3. **Try incognito/private browsing mode** 4. **Contact support** with your export ID **Export Expiration**: Downloads are only available for 30 days. After that, you'll need to renew the export. **This should not happen** - exports should exactly match previews. If you experience this: **Immediate Steps:** 1. **Don't panic** - we'll resolve this quickly 2. **Take screenshots** of both preview and actual export 3. **Note the export ID** (found in your export history) 4. **Contact support immediately** at [export@countrystatecity.in](mailto:export@countrystatecity.in) **Information to Include:** * Export ID and timestamp * Screenshots of preview vs actual data * Browser and operating system details * Specific fields or records that don't match **Our Response:** * **Investigation**: We'll investigate the discrepancy immediately * **Resolution**: Bug fixes are prioritized and deployed quickly * **Compensation**: Credit refund if the issue was on our end * **Prevention**: System improvements to prevent recurrence **Temporary Workaround:** If you need the correct data urgently, create a new export and we'll refund the credits for the incorrect one. **Quality Guarantee**: Preview accuracy is fundamental to our service. We take these issues very seriously. **Common Error Types:** **"Insufficient Credits" Error:** * **Cause**: Not enough credits for the selected configuration * **Solution**: Purchase additional credits or reduce export scope * **Prevention**: Check credit requirements before confirming **"Field Selection Required" Error:** * **Cause**: No fields selected for export * **Solution**: Select at least one field from each chosen dataset * **Prevention**: Review field selections before proceeding **"Export Processing Failed" Error:** * **Cause**: Server-side processing issue * **Solution**: Try again in a few minutes, contact support if persistent * **Prevention**: Avoid very large exports during peak hours **"Invalid Format Combination" Error:** * **Cause**: Certain formats may not support specific field combinations * **Solution**: Try a different format or adjust field selection * **Prevention**: Use preview to validate before creating **Browser/Network Errors:** * Clear browser cache and cookies * Disable browser extensions temporarily * Try a different network or browser * Check if JavaScript is enabled **Steps to Resolve:** 1. **Try again**: Many errors are temporary 2. **Simplify**: Reduce fields or datasets to isolate the issue 3. **Switch browsers**: Rule out browser-specific issues 4. **Contact support**: Include error message and export configuration **Bug Reports:** Send bug reports to: **[export@countrystatecity.in](mailto:export@countrystatecity.in)** **Include the Following:** * Detailed description of the issue * Steps to reproduce the problem * Browser and operating system details * Screenshots or error messages * Export ID if applicable **Feature Requests:** Send feature requests to: **[export@countrystatecity.in](mailto:export@countrystatecity.in)** **Popular Feature Requests:** * Historical data snapshots * API access for automated exports * Bulk export operations * Custom field calculations * Advanced filtering options * Team collaboration features **Our Response Time:** * **Bug reports**: Within 24 hours * **Feature requests**: Reviewed monthly * **Critical issues**: Same day response * **Enhancement updates**: Quarterly releases **Community Feedback:** * Join our community forum for discussions * Vote on feature requests * Share use cases and success stories * Connect with other users *** ## Still Need Help? **[export@countrystatecity.in](mailto:export@countrystatecity.in)** General questions, technical issues, and billing inquiries **Response Time**: We typically respond to all inquiries within 24 hours. Enterprise customers receive priority support with faster response times. # Export Formats Source: https://docs.countrystatecity.in/tools/export-tool/formats Complete guide to all available export formats including JSON, CSV, XML, YAML, and SQL with code examples and use case recommendations. Choose the perfect format for your project. Each format is optimized for specific use cases and comes with different credit costs and integration patterns. ## Format Overview **+2 Credits** * Web applications * APIs and microservices * JavaScript/Node.js projects * Mobile app backends **+2 Credits** * Streaming and ETL pipelines * Line-by-line processing * Bulk loading (BigQuery, Elasticsearch) * Append-only data logs **+3 Credits** * Spreadsheet analysis * Data science workflows * Excel/Google Sheets * BI tools and dashboards **+4 Credits** * Multi-sheet .xlsx workbooks * Business and reporting * Offline analysis (Sheets, Calc) * No-code sort, filter, pivot **+3 Credits** * Documentation and READMEs * Wikis and knowledge bases * Pull-request summaries * Human-readable tables **+2 Credits** * Enterprise systems * SOAP APIs * Legacy system integration * Configuration files **+2 Credits** * Configuration files * DevOps and CI/CD * Documentation * Human-readable data **+4 Credits** * MySQL / MariaDB imports * Full schema with indexes * Foreign key constraints * Production-ready DDL **+5 Credits** * PostgreSQL-specific DDL * Type inference and indexes * Constraints and check rules * PostGIS compatible **+5 Credits** * Embedded and mobile apps * Single-file database * Lightweight deployments * Zero-config setup **+5 Credits** * Microsoft SQL Server DDL * T-SQL compatible * Enterprise deployments * Azure SQL ready **+4 Credits** * NoSQL document store * Flexible schema queries * mongorestore compatible * Aggregation pipeline ready **+4 Credits** * Mapping and GIS * Spatial queries * Map libraries (Mapbox, Leaflet) * Compatible with PostGIS PostgreSQL, SQLite, and SQL Server produce dialect-specific DDL with type inference, indexes, and constraints. MySQL produces a full schema with foreign keys and indexes. All SQL formats are ready-to-import. ## JSON Format (+2 Credits) The most popular format for modern web development. Perfect for APIs, web applications, and mobile backends. **Clean, nested structure with logical grouping:** ```json Sample Countries Export theme={null} { "countries": [ { "name": "United States", "iso2": "US", "iso3": "USA", "phonecode": "1", "currency": "USD", "currency_symbol": "$", "region": "Americas", "subregion": "Northern America", "latitude": 37.09024, "longitude": -95.712891, "emoji": "🇺🇸", "native": "United States" }, { "name": "Canada", "iso2": "CA", "iso3": "CAN", "phonecode": "1", "currency": "CAD", "currency_symbol": "C$", "region": "Americas", "subregion": "Northern America", "latitude": 56.130366, "longitude": -106.346771, "emoji": "🇨🇦", "native": "Canada" } ], "metadata": { "total_countries": 2, "export_date": "2024-01-15T10:30:00Z", "fields": ["name", "iso2", "iso3", "phonecode", "currency"], "version": "1.0" } } ``` **Multi-dataset structure:** ```json Countries + States Export theme={null} { "countries": [ { "name": "United States", "iso2": "US", "states_count": 50 } ], "states": [ { "name": "California", "iso2": "CA", "country_code": "US", "latitude": 36.778261, "longitude": -119.417932 } ], "metadata": { "countries_count": 1, "states_count": 50, "export_date": "2024-01-15T10:30:00Z" } } ``` **JavaScript/Node.js Integration:** ```javascript Frontend Usage theme={null} // Load and parse JSON data const geoData = await fetch('/data/export.json').then(r => r.json()); // Create country dropdown function populateCountryDropdown(selectElement) { geoData.countries.forEach(country => { const option = new Option( `${country.emoji} ${country.name}`, country.iso2 ); selectElement.add(option); }); } // Quick lookup functions const countryLookup = new Map( geoData.countries.map(c => [c.iso2, c]) ); function getCountryByCode(code) { return countryLookup.get(code); } function getCurrencySymbol(countryCode) { const country = countryLookup.get(countryCode); return country?.currency_symbol || '$'; } ``` **React Component Example:** ```jsx React Integration theme={null} import { useState, useEffect } from 'react'; function CountrySelector({ onCountryChange }) { const [countries, setCountries] = useState([]); const [selectedCountry, setSelectedCountry] = useState(''); useEffect(() => { fetch('/data/countries-export.json') .then(response => response.json()) .then(data => setCountries(data.countries)); }, []); const handleChange = (e) => { const countryCode = e.target.value; const country = countries.find(c => c.iso2 === countryCode); setSelectedCountry(countryCode); onCountryChange(country); }; return (
{selectedCountry && (

Currency: {getSelectedCountry()?.currency_symbol}

Region: {getSelectedCountry()?.region}

)}
); function getSelectedCountry() { return countries.find(c => c.iso2 === selectedCountry); } } ``` **API Response Enhancement:** ```javascript Express.js API theme={null} const geoData = require('./exports/countries.json'); const countryMap = new Map(geoData.countries.map(c => [c.iso2, c])); app.get('/api/users/:id', async (req, res) => { const user = await User.findById(req.params.id); const countryInfo = countryMap.get(user.country_code); res.json({ ...user, location: { country_name: countryInfo?.name, country_emoji: countryInfo?.emoji, currency: countryInfo?.currency, region: countryInfo?.region } }); }); ```
**Performance Optimization:** ```javascript Efficient Data Loading theme={null} // Lazy load large datasets class GeoDataManager { constructor() { this.countriesCache = null; this.statesCache = new Map(); // Country code -> states } async getCountries() { if (!this.countriesCache) { const response = await fetch('/data/countries.json'); this.countriesCache = (await response.json()).countries; } return this.countriesCache; } async getStatesByCountry(countryCode) { if (!this.statesCache.has(countryCode)) { // Load states on demand const response = await fetch(`/data/states-${countryCode}.json`); const states = (await response.json()).states; this.statesCache.set(countryCode, states); } return this.statesCache.get(countryCode); } } ``` **Data Validation:** ```javascript Input Validation theme={null} function validateCountryCode(code) { const country = geoData.countries.find(c => c.iso2 === code); if (!country) { throw new Error(`Invalid country code: ${code}`); } return country; } function validateStateForCountry(stateCode, countryCode) { const states = geoData.states.filter(s => s.country_code === countryCode); const state = states.find(s => s.iso2 === stateCode); if (!state) { throw new Error(`Invalid state ${stateCode} for country ${countryCode}`); } return state; } ```
## CSV Format (+3 Credits) Perfect for spreadsheet analysis, data science workflows, and business intelligence tools. **Clean, standardized CSV with headers:** ```csv Countries Export theme={null} name,iso2,iso3,phonecode,currency,currency_symbol,region,subregion,latitude,longitude,capital,emoji United States,US,USA,1,USD,$,Americas,Northern America,37.09024,-95.712891,Washington,🇺🇸 Canada,CA,CAN,1,CAD,C$,Americas,Northern America,56.130366,-106.346771,Ottawa,🇨🇦 United Kingdom,GB,GBR,44,GBP,£,Europe,Northern Europe,55.378051,-3.435973,London,🇬🇧 ``` **Multi-sheet structure for combined datasets:** ```csv countries.csv theme={null} name,iso2,currency,capital United States,US,USD,Washington Canada,CA,CAD,Ottawa ``` ```csv states.csv theme={null} name,iso2,country_code,latitude,longitude California,CA,US,36.778261,-119.417932 Texas,TX,US,31.968599,-99.901813 ``` **Power Query Import:** ```excel Power Query M Formula theme={null} let Source = Csv.Document(File.Contents("C:\exports\countries.csv"),[Delimiter=",", Encoding=1252]), PromotedHeaders = Table.PromoteHeaders(Source,[PromoteAllScalars=true]), // Type conversions ChangedTypes = Table.TransformColumnTypes(PromotedHeaders,{ {"latitude", type number}, {"longitude", type number}, {"phonecode", type text} }), // Add calculated columns AddedPhoneDisplay = Table.AddColumn(ChangedTypes, "PhoneDisplay", each "+" & [phonecode]), AddedCurrencyDisplay = Table.AddColumn(AddedPhoneDisplay, "CurrencyDisplay", each [currency_symbol] & " " & [currency]) in AddedCurrencyDisplay ``` **Pivot Table Analysis:** ```excel VBA Macro theme={null} Sub CreateCountryAnalysis() Dim ws As Worksheet Dim pt As PivotTable ' Create pivot table from imported data Set ws = ActiveSheet Set pt = ws.PivotTableWizard(xlDatabase, ws.Range("A1").CurrentRegion) With pt.PivotFields("region") .Orientation = xlRowField End With With pt.PivotFields("name") .Orientation = xlDataField .Function = xlCount End With With pt.PivotFields("currency") .Orientation = xlColumnField End With End Sub ``` **Data Analysis with Pandas:** ```python Data Science Workflow theme={null} import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # Load CSV data countries = pd.read_csv('exports/countries.csv') states = pd.read_csv('exports/states.csv') # Basic analysis print(countries.describe()) print(countries['region'].value_counts()) # Advanced analytics countries['phone_display'] = '+' + countries['phonecode'].astype(str) countries['currency_display'] = countries['currency_symbol'] + ' ' + countries['currency'] # Regional analysis regional_stats = countries.groupby('region').agg({ 'name': 'count', 'currency': 'nunique', 'phonecode': 'nunique', 'latitude': 'mean', 'longitude': 'mean' }).round(2) print(regional_stats) # Visualization plt.figure(figsize=(12, 8)) sns.scatterplot(data=countries, x='longitude', y='latitude', hue='region', style='currency', s=60) plt.title('Countries by Geographic Location and Currency') plt.show() # Export analysis results regional_stats.to_csv('analysis/regional_summary.csv') ``` **Machine Learning Preprocessing:** ```python ML Feature Engineering theme={null} from sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.cluster import KMeans import numpy as np # Prepare features for clustering (using numeric fields only) features = ['latitude', 'longitude'] X = countries[features].fillna(countries[features].mean()) # Add encoded categorical features le_region = LabelEncoder() le_currency = LabelEncoder() countries['region_encoded'] = le_region.fit_transform(countries['region']) countries['currency_encoded'] = le_currency.fit_transform(countries['currency']) # Combine features features_extended = features + ['region_encoded', 'currency_encoded'] X_extended = countries[features_extended] # Normalize features scaler = StandardScaler() X_scaled = scaler.fit_transform(X_extended) # Country clustering kmeans = KMeans(n_clusters=5, random_state=42) countries['cluster'] = kmeans.fit_predict(X_scaled) # Analyze clusters cluster_summary = countries.groupby('cluster')[['latitude', 'longitude', 'region']].agg({ 'latitude': 'mean', 'longitude': 'mean', 'region': lambda x: x.mode().iloc[0] # Most common region }) print(cluster_summary) ``` ## XML Format (+2 Credits) Ideal for enterprise systems, SOAP APIs, and legacy system integrations that require structured, schema-validated data. **Well-formed XML with proper namespaces:** ```xml Countries Export theme={null} 2024-01-15T10:30:00Z 1.0 250 United States United States US USA 🇺🇸 1 Washington US Dollar US Dollar Americas Northern America 37.09024 -95.712891 American United States America/New_York America/Chicago America/Denver America/Los_Angeles ``` **Java/Spring Boot Integration:** ```java XML Processing with JAXB theme={null} @XmlRootElement(name = "export", namespace = "https://countrystatecity.in/schemas/v1") public class CSCExport { @XmlElement(name = "countries") private Countries countries; @XmlElement(name = "metadata") private Metadata metadata; // Getters and setters } @XmlAccessorType(XmlAccessType.FIELD) public class Countries { @XmlElement(name = "country") private List countryList; } @XmlAccessorType(XmlAccessType.FIELD) public class Country { @XmlElement(name = "basic_info") private BasicInfo basicInfo; @XmlElement(name = "economic_info") private EconomicInfo economicInfo; @XmlElement(name = "geographic_info") private GeographicInfo geographicInfo; } // Service class for XML processing @Service public class GeographicalDataService { @Autowired private JAXBContext jaxbContext; public CSCExport loadGeographicalData(String xmlFilePath) throws Exception { Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); return (CSCExport) unmarshaller.unmarshal(new File(xmlFilePath)); } public List getCountriesByRegion(String region) { return loadedData.getCountries().getCountryList().stream() .filter(c -> region.equals(c.getGeographicInfo().getRegion())) .collect(Collectors.toList()); } } ``` **SOAP Web Service Integration:** ```java SOAP Service theme={null} @WebService @SOAPBinding(style = SOAPBinding.Style.DOCUMENT) public class GeographicalWebService { @WebMethod public CountryInfo getCountryByCode(@WebParam(name = "countryCode") String code) { // Load from CSC XML export CSCExport data = geographicalDataService.loadGeographicalData("csc-export.xml"); Country country = data.getCountries().getCountryList().stream() .filter(c -> code.equals(c.getBasicInfo().getIso2())) .findFirst() .orElse(null); return convertToCountryInfo(country); } @WebMethod public List getCountriesByRegion(@WebParam(name = "region") String region) { return geographicalDataService.getCountriesByRegion(region).stream() .map(this::convertToCountryInfo) .collect(Collectors.toList()); } } ``` **Advanced XML Querying:** ```xquery XPath Examples theme={null} (: Get all countries in Americas region :) //csc:country[csc:geographic_info/csc:region='Americas']/csc:basic_info/csc:name (: Find countries with USD currency :) //csc:country[csc:economic_info/csc:currency/@code='USD']/csc:basic_info/csc:name (: Get currency codes for European countries :) //csc:country[csc:geographic_info/csc:region='Europe']/csc:economic_info/csc:currency/@code (: Find countries with multiple timezones :) //csc:country[count(csc:timezones/csc:timezone) > 1]/csc:basic_info/csc:name ``` **C# .NET XML Processing:** ```csharp XML Document Processing theme={null} using System.Xml; using System.Xml.Linq; public class GeographicalDataProcessor { private XDocument xmlDoc; private XNamespace ns = "https://countrystatecity.in/schemas/v1"; public void LoadData(string xmlFilePath) { xmlDoc = XDocument.Load(xmlFilePath); } public IEnumerable GetCountriesByRegion(string region) { return xmlDoc.Descendants(ns + "country") .Where(c => c.Element(ns + "geographic_info") ?.Element(ns + "region")?.Value == region) .Select(c => new CountryModel { Name = c.Element(ns + "basic_info")?.Element(ns + "name")?.Value, Iso2 = c.Element(ns + "basic_info")?.Element(ns + "iso2")?.Value, Currency = c.Element(ns + "economic_info")?.Element(ns + "currency")?.Attribute("code")?.Value }); } public int GetCountryCountByRegion(string region) { return xmlDoc.Descendants(ns + "country") .Where(c => c.Element(ns + "geographic_info")?.Element(ns + "region")?.Value == region) .Count(); } } ``` ## YAML Format (+2 Credits) Human-readable format perfect for configuration files, documentation, and DevOps workflows. **Clean, hierarchical YAML structure:** ```yaml Countries Export theme={null} metadata: export_date: "2024-01-15T10:30:00Z" version: "1.0" total_countries: 250 fields: - name - iso2 - currency - region countries: - name: "United States" iso2: "US" iso3: "USA" basic_info: native_name: "United States" emoji: "🇺🇸" capital: "Washington" contact_info: phonecode: "1" tld: ".us" economic_info: currency: "USD" currency_symbol: "$" currency_name: "US Dollar" geographic_info: region: "Americas" subregion: "Northern America" coordinates: latitude: 37.09024 longitude: -95.712891 cultural_info: nationality: "American" native_name: "United States" timezones: - "America/New_York" - "America/Chicago" - "America/Denver" - "America/Los_Angeles" - name: "Canada" iso2: "CA" iso3: "CAN" basic_info: native_name: "Canada" emoji: "🇨🇦" capital: "Ottawa" economic_info: currency: "CAD" currency_symbol: "C$" currency_name: "Canadian Dollar" geographic_info: region: "Americas" subregion: "Northern America" coordinates: latitude: 56.130366 longitude: -106.346771 ``` **Kubernetes Deployment Configuration:** ```yaml k8s-deployment.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: geo-service spec: replicas: 3 selector: matchLabels: app: geo-service template: metadata: labels: app: geo-service spec: containers: - name: geo-service image: geo-service:latest ports: - containerPort: 8080 volumeMounts: - name: geo-data mountPath: /app/data env: - name: GEO_DATA_PATH value: "/app/data/countries.yaml" volumes: - name: geo-data configMap: name: geographical-data --- apiVersion: v1 kind: ConfigMap metadata: name: geographical-data data: countries.yaml: | # CSC Export Data countries: - name: "United States" iso2: "US" currency: "USD" region: "Americas" ``` **Docker Compose with YAML Config:** ```yaml docker-compose.yml theme={null} version: '3.8' services: geo-api: build: . ports: - "8080:8080" volumes: - ./exports/countries.yaml:/app/config/countries.yaml:ro environment: - CONFIG_FILE=/app/config/countries.yaml - LOG_LEVEL=info depends_on: - redis redis: image: redis:alpine ports: - "6379:6379" volumes: geo-data: driver: local ``` **Python YAML Processing:** ```python Python Integration theme={null} import yaml from pathlib import Path class GeographicalConfig: def __init__(self, config_path: str): self.config_path = Path(config_path) self.data = None self.load_config() def load_config(self): """Load YAML configuration file""" with open(self.config_path, 'r', encoding='utf-8') as file: self.data = yaml.safe_load(file) def get_countries_by_region(self, region: str) -> list: """Filter countries by region""" return [ country for country in self.data['countries'] if country['geographic_info']['region'] == region ] def get_country_by_code(self, iso2_code: str) -> dict: """Get country by ISO2 code""" for country in self.data['countries']: if country['iso2'] == iso2_code: return country return None def get_supported_currencies(self) -> set: """Get all unique currencies""" return { country['economic_info']['currency'] for country in self.data['countries'] } # Usage in Flask application from flask import Flask, jsonify app = Flask(__name__) geo_config = GeographicalConfig('exports/countries.yaml') @app.route('/api/countries/') def get_countries_by_region(region): countries = geo_config.get_countries_by_region(region) return jsonify(countries) @app.route('/api/currencies') def get_currencies(): currencies = list(geo_config.get_supported_currencies()) return jsonify(sorted(currencies)) ``` **Node.js YAML Configuration:** ```javascript Node.js Integration theme={null} const yaml = require('js-yaml'); const fs = require('fs'); const path = require('path'); class GeoDataManager { constructor(configPath) { this.configPath = configPath; this.data = null; this.loadConfig(); } loadConfig() { try { const fileContents = fs.readFileSync(this.configPath, 'utf8'); this.data = yaml.load(fileContents); console.log(`Loaded ${this.data.countries.length} countries`); } catch (error) { console.error('Error loading YAML config:', error); throw error; } } // Hot reload configuration on file changes watchConfig() { fs.watchFile(this.configPath, (curr, prev) => { console.log('Config file changed, reloading...'); this.loadConfig(); }); } getCountryByCode(iso2) { return this.data.countries.find(c => c.iso2 === iso2); } getRegionStats() { const stats = {}; this.data.countries.forEach(country => { const region = country.geographic_info.region; stats[region] = (stats[region] || 0) + 1; }); return stats; } } // Express.js integration const express = require('express'); const app = express(); const geoData = new GeoDataManager('./exports/countries.yaml'); // Enable hot reload in development if (process.env.NODE_ENV === 'development') { geoData.watchConfig(); } app.get('/api/stats/regions', (req, res) => { res.json(geoData.getRegionStats()); }); ``` ## SQL Format (+4 Credits, dialects +5) Ready-to-execute SQL statements for direct database imports, perfect for production deployments and data migrations. **Complete database schema with relationships:** ```sql Countries & States Export theme={null} -- CSC Export Generated SQL -- Generated on: 2024-01-15T10:30:00Z -- Version: 1.0 -- Contains: Countries, States -- Drop existing tables (optional) -- DROP TABLE IF EXISTS states; -- DROP TABLE IF EXISTS countries; -- Create countries table CREATE TABLE IF NOT EXISTS countries ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, iso2 CHAR(2) UNIQUE NOT NULL, iso3 CHAR(3) UNIQUE NOT NULL, numeric_code VARCHAR(10), phonecode VARCHAR(20), capital VARCHAR(255), currency CHAR(3), currency_name VARCHAR(100), currency_symbol VARCHAR(10), tld VARCHAR(10), native VARCHAR(255), region VARCHAR(100), subregion VARCHAR(100), latitude DECIMAL(10, 8), longitude DECIMAL(11, 8), emoji VARCHAR(10), emojiU VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag BOOLEAN DEFAULT TRUE, -- Indexes for performance INDEX idx_countries_iso2 (iso2), INDEX idx_countries_iso3 (iso3), INDEX idx_countries_region (region), INDEX idx_countries_currency (currency) ); -- Create states table CREATE TABLE IF NOT EXISTS states ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, country_id INTEGER NOT NULL, country_code CHAR(2) NOT NULL, fips_code VARCHAR(10), iso2 VARCHAR(10), type VARCHAR(50), latitude DECIMAL(10, 8), longitude DECIMAL(11, 8), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag BOOLEAN DEFAULT TRUE, wikiDataId VARCHAR(50), -- Foreign key relationship FOREIGN KEY (country_id) REFERENCES countries(id) ON DELETE CASCADE, -- Indexes INDEX idx_states_country_id (country_id), INDEX idx_states_country_code (country_code), INDEX idx_states_name (name) ); -- Insert countries data INSERT INTO countries ( name, iso2, iso3, numeric_code, phonecode, capital, currency, currency_name, currency_symbol, tld, native, region, subregion, latitude, longitude, emoji, emojiU ) VALUES ('United States', 'US', 'USA', '840', '1', 'Washington', 'USD', 'US dollar', '$', '.us', 'United States', 'Americas', 'Northern America', 37.09024000, -95.71289100, '🇺🇸', 'U+1F1FA U+1F1F8'), ('Canada', 'CA', 'CAN', '124', '1', 'Ottawa', 'CAD', 'Canadian dollar', '$', '.ca', 'Canada', 'Americas', 'Northern America', 56.13036600, -106.34677100, '🇨🇦', 'U+1F1E8 U+1F1E6'), ('United Kingdom', 'GB', 'GBR', '826', '44', 'London', 'GBP', 'British pound', '£', '.uk', 'United Kingdom', 'Europe', 'Northern Europe', 55.37805100, -3.43597300, '🇬🇧', 'U+1F1EC U+1F1E7'); -- Insert states data (with proper country_id references) INSERT INTO states ( name, country_id, country_code, fips_code, iso2, type, latitude, longitude, wikiDataId ) VALUES ('Alabama', (SELECT id FROM countries WHERE iso2 = 'US'), 'US', '01', 'AL', 'state', 32.31823140, -86.90229800, 'Q173'), ('Alaska', (SELECT id FROM countries WHERE iso2 = 'US'), 'US', '02', 'AK', 'state', 66.16044020, -153.36914700, 'Q797'), ('Alberta', (SELECT id FROM countries WHERE iso2 = 'CA'), 'CA', NULL, 'AB', 'province', 53.93327060, -116.57664600, 'Q1951'); -- Create views for common queries CREATE VIEW countries_with_stats AS SELECT c.*, COUNT(s.id) as states_count, AVG(s.latitude) as avg_state_latitude, AVG(s.longitude) as avg_state_longitude FROM countries c LEFT JOIN states s ON c.id = s.country_id GROUP BY c.id; -- Create stored procedures for common operations DELIMITER // CREATE PROCEDURE GetCountryWithStates(IN country_code CHAR(2)) BEGIN SELECT c.*, s.name as state_name, s.iso2 as state_iso2 FROM countries c LEFT JOIN states s ON c.id = s.country_id WHERE c.iso2 = country_code ORDER BY s.name; END // DELIMITER ; -- Grant permissions (adjust as needed) -- GRANT SELECT, INSERT, UPDATE ON countries TO 'app_user'@'%'; -- GRANT SELECT, INSERT, UPDATE ON states TO 'app_user'@'%'; ``` **PostgreSQL Migration:** ```sql PostgreSQL Optimized theme={null} -- migration_001_geographical_data.sql -- PostgreSQL specific optimizations -- Enable extensions CREATE EXTENSION IF NOT EXISTS postgis; -- For geographic data types CREATE EXTENSION IF NOT EXISTS pg_trgm; -- For text search -- Create countries table with PostgreSQL features CREATE TABLE countries ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, iso2 CHAR(2) UNIQUE NOT NULL, iso3 CHAR(3) UNIQUE NOT NULL, phonecode VARCHAR(20), currency CHAR(3), region VARCHAR(100), subregion VARCHAR(100), -- PostGIS geometry column for advanced geographic queries geom GEOMETRY(POINT, 4326), -- JSON column for flexible data storage metadata JSONB, -- Full text search search_vector tsvector, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Create advanced indexes CREATE INDEX CONCURRENTLY idx_countries_geom ON countries USING GIST (geom); CREATE INDEX CONCURRENTLY idx_countries_search ON countries USING GIN (search_vector); CREATE INDEX CONCURRENTLY idx_countries_metadata ON countries USING GIN (metadata); CREATE INDEX CONCURRENTLY idx_countries_region_hash ON countries USING HASH (region); -- Insert data with PostGIS points INSERT INTO countries (name, iso2, iso3, phonecode, currency, region, subregion, geom, metadata) VALUES ('United States', 'US', 'USA', '1', 'USD', 'Americas', 'Northern America', ST_SetSRID(ST_MakePoint(-95.71289100, 37.09024000), 4326), '{"capital": "Washington", "native": "United States", "nationality": "American"}'::jsonb); -- Update search vectors UPDATE countries SET search_vector = to_tsvector('english', name || ' ' || region || ' ' || subregion); -- Create trigger for automatic search vector updates CREATE OR REPLACE FUNCTION update_countries_search_vector() RETURNS trigger AS $$ BEGIN NEW.search_vector := to_tsvector('english', NEW.name || ' ' || NEW.region || ' ' || NEW.subregion); NEW.updated_at := NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_update_countries_search_vector BEFORE INSERT OR UPDATE ON countries FOR EACH ROW EXECUTE FUNCTION update_countries_search_vector(); ``` **MySQL Production Setup:** ```sql MySQL Optimized theme={null} -- MySQL 8.0+ with JSON support CREATE TABLE countries ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, iso2 CHAR(2) UNIQUE NOT NULL, iso3 CHAR(3) UNIQUE NOT NULL, phonecode VARCHAR(20), currency CHAR(3), region VARCHAR(100), subregion VARCHAR(100), -- Spatial data type coordinates POINT SRID 4326, -- JSON for flexible metadata metadata JSON, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Indexes INDEX idx_countries_region (region), INDEX idx_countries_currency (currency), SPATIAL INDEX idx_countries_coordinates (coordinates), -- Full text search FULLTEXT INDEX idx_countries_search (name, region, subregion) ) ENGINE=InnoDB CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Insert with spatial data INSERT INTO countries (name, iso2, iso3, phonecode, currency, region, subregion, coordinates, metadata) VALUES ('United States', 'US', 'USA', '1', 'USD', 'Americas', 'Northern America', ST_GeomFromText('POINT(-95.71289100 37.09024000)', 4326), JSON_OBJECT('capital', 'Washington', 'native', 'United States', 'nationality', 'American')); -- Create stored procedures DELIMITER // CREATE PROCEDURE GetNearbyCountries(IN target_lat DECIMAL(10,8), IN target_lng DECIMAL(11,8), IN radius_km INT) BEGIN SELECT name, iso2, ST_Distance_Sphere(coordinates, ST_GeomFromText(CONCAT('POINT(', target_lng, ' ', target_lat, ')'), 4326)) / 1000 AS distance_km FROM countries WHERE ST_Distance_Sphere(coordinates, ST_GeomFromText(CONCAT('POINT(', target_lng, ' ', target_lat, ')'), 4326)) / 1000 <= radius_km ORDER BY distance_km; END // DELIMITER ; ``` **Django Model Integration:** ```python Django Models theme={null} # models.py from django.contrib.gis.db import models from django.contrib.postgres.fields import ArrayField import uuid class Country(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) iso2 = models.CharField(max_length=2, unique=True, db_index=True) iso3 = models.CharField(max_length=3, unique=True) phonecode = models.CharField(max_length=20, null=True, blank=True) currency = models.CharField(max_length=3, db_index=True) region = models.CharField(max_length=100, db_index=True) subregion = models.CharField(max_length=100) # PostGIS fields coordinates = models.PointField(srid=4326, null=True, blank=True) # JSON field for flexible data metadata = models.JSONField(default=dict, blank=True) # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'countries' indexes = [ models.Index(fields=['region', 'currency']), models.Index(fields=['name'], name='idx_country_name'), ] def __str__(self): return self.name # Management command to load CSC data # management/commands/load_csc_data.py from django.core.management.base import BaseCommand from django.db import connection import os class Command(BaseCommand): help = 'Load CSC geographical data from SQL export' def add_arguments(self, parser): parser.add_argument('--sql-file', type=str, required=True, help='Path to CSC SQL export file') def handle(self, *args, **options): sql_file = options['sql_file'] if not os.path.exists(sql_file): self.stdout.write( self.style.ERROR(f'SQL file not found: {sql_file}') ) return with open(sql_file, 'r') as file: sql_content = file.read() # Execute SQL statements with connection.cursor() as cursor: # Split and execute statements statements = sql_content.split(';') for statement in statements: if statement.strip(): cursor.execute(statement) self.stdout.write( self.style.SUCCESS('Successfully loaded CSC data') ) # Usage: python manage.py load_csc_data --sql-file exports/countries_states.sql ``` **Laravel/PHP Integration:** ```php Laravel Migration & Models theme={null} 'decimal:8', 'longitude' => 'decimal:8', 'metadata' => 'array' ]; public function states(): HasMany { return $this->hasMany(State::class); } // Scope for filtering by region public function scopeByRegion($query, $region) { return $query->where('region', $region); } // Get countries with state count public static function withStateCount() { return static::withCount('states')->get(); } } // API Controller has('region')) { $query->byRegion($request->region); } if ($request->has('currency')) { $query->where('currency', $request->currency); } return $query->get(); } public function countriesWithStates() { return Country::with('states')->get(); } } ``` ## MongoDB Format (+4 Credits) A `mongodump`-compatible BSON/JSON export ready to load directly into MongoDB using `mongorestore` or `mongoimport`. **Document structure for countries:** ```json Sample Countries Export theme={null} [ { "_id": { "$oid": "64f1a2b3c4d5e6f7a8b9c0d1" }, "name": "India", "iso2": "IN", "iso3": "IND", "phonecode": "+91", "capital": "New Delhi", "currency": "INR", "currency_symbol": "₹", "region": "Asia", "subregion": "Southern Asia", "latitude": 20.593684, "longitude": 78.96288, "emoji": "🇮🇳" } ] ``` **Nested states export:** ```json Countries with States theme={null} [ { "_id": { "$oid": "64f1a2b3c4d5e6f7a8b9c0d2" }, "name": "India", "iso2": "IN", "states": [ { "name": "Maharashtra", "state_code": "MH", "latitude": 19.7515, "longitude": 75.7139 }, { "name": "Karnataka", "state_code": "KA", "latitude": 15.3173, "longitude": 75.7139 } ] } ] ``` **Import using mongoimport:** ```bash theme={null} # Import countries mongoimport --uri "mongodb://localhost:27017/geoDb" \ --collection countries \ --file countries.json \ --jsonArray # Import states mongoimport --uri "mongodb://localhost:27017/geoDb" \ --collection states \ --file states.json \ --jsonArray # Import cities mongoimport --uri "mongodb://localhost:27017/geoDb" \ --collection cities \ --file cities.json \ --jsonArray ``` **Common queries:** ```javascript Node.js with MongoDB driver theme={null} const { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); const db = client.db('geoDb'); // Get all states for India const states = await db.collection('states') .find({ country_code: 'IN' }) .sort({ name: 1 }) .toArray(); // Get cities in Maharashtra const cities = await db.collection('cities') .find({ country_code: 'IN', state_code: 'MH' }) .project({ name: 1, latitude: 1, longitude: 1 }) .toArray(); // Countries by region const asianCountries = await db.collection('countries') .find({ region: 'Asia' }) .sort({ name: 1 }) .toArray(); ``` ## GeoJSON Format (+4 Credits) GeoJSON is a standard format for encoding geographical data structures. Each city or region is exported as a GeoJSON `Feature` with coordinates and properties, making it directly usable with mapping libraries and spatial databases. **FeatureCollection for cities:** ```json Sample GeoJSON Export theme={null} { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-118.24368, 34.05223] }, "properties": { "id": 111968, "name": "Los Angeles", "state_code": "CA", "state_name": "California", "country_code": "US", "country_name": "United States" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [72.87765, 19.07283] }, "properties": { "id": 1, "name": "Mumbai", "state_code": "MH", "state_name": "Maharashtra", "country_code": "IN", "country_name": "India" } } ] } ``` Coordinates follow the GeoJSON standard: `[longitude, latitude]` (not `[latitude, longitude]`). The coordinate reference system is WGS 84 (EPSG:4326). **Mapbox GL JS:** ```javascript theme={null} map.addSource('cities', { type: 'geojson', data: '/data/cities.geojson' }); map.addLayer({ id: 'city-points', type: 'circle', source: 'cities', paint: { 'circle-radius': 4, 'circle-color': '#2563EB' } }); ``` **Leaflet:** ```javascript theme={null} fetch('/data/cities.geojson') .then(r => r.json()) .then(data => { L.geoJSON(data, { pointToLayer: (feature, latlng) => L.circleMarker(latlng, { radius: 5 }), onEachFeature: (feature, layer) => layer.bindPopup(feature.properties.name) }).addTo(map); }); ``` **Load into PostGIS:** ```sql theme={null} -- Requires ogr2ogr (GDAL) -- ogr2ogr -f PostgreSQL PG:"dbname=geoDb" cities.geojson -- Or use ST_GeomFromGeoJSON INSERT INTO city_points (name, country_code, geom) SELECT f->>'name', f->>'country_code', ST_SetSRID(ST_GeomFromGeoJSON(f->'geometry'), 4326) FROM jsonb_array_elements(:'geojson'::jsonb->'features') AS f; ``` ## Format Selection Guide Choosing the right format can save you credits and development time. Use this decision tree: **Ask yourself:** * Where will this data be used? * Who will consume the data? * What tools do you have available? * Do you need real-time updates? **Development Time vs Credit Cost:** **Recommendation: JSON (+2 credits)** * Native JavaScript support * Easy API integration * No additional parsing needed * Perfect for React, Vue, Angular **Recommendation: CSV (+3 credits)** * Excel/Google Sheets ready * Pandas compatibility * Tableau/PowerBI import * Statistical analysis tools **Recommendation: XML (+2 credits)** * Schema validation * SOAP web services * Legacy system integration * Structured data requirements **Recommendation: YAML (+2 credits)** * Human-readable format * Kubernetes configs * CI/CD pipelines * Documentation-friendly **Recommendation: SQL (+4 credits) or PostgreSQL/SQL Server/SQLite3 (+5 credits)** * Direct database import * Proper relationships * Production-ready * Migration-friendly **Final Cost Formula:** ``` Total Credits = Dataset Cost + Format Cost Examples: - Countries (1) + JSON (1) = 2 credits - Countries (1) + States (2) + CSV (1) = 4 credits - Cities (3) + SQL (3) = 6 credits ``` ## Best Practices by Format **Performance Tips:** ```javascript Lazy Loading theme={null} // Don't load all data at once for large datasets class GeoDataLoader { async loadCountries() { if (!this.countriesCache) { const response = await fetch('/data/countries.json'); this.countriesCache = await response.json(); } return this.countriesCache; } async loadStatesByCountry(countryCode) { const cacheKey = `states_${countryCode}`; if (!this.statesCache[cacheKey]) { const response = await fetch(`/data/states/${countryCode}.json`); this.statesCache[cacheKey] = await response.json(); } return this.statesCache[cacheKey]; } } ``` **Memory Management:** ```javascript Efficient Processing theme={null} // Process large JSON files in chunks function processCountriesInBatches(countries, batchSize = 100) { const results = []; for (let i = 0; i < countries.length; i += batchSize) { const batch = countries.slice(i, i + batchSize); const processed = batch.map(processCountry); results.push(...processed); // Allow garbage collection between batches if (i % 1000 === 0) { await new Promise(resolve => setTimeout(resolve, 0)); } } return results; } ``` **Data Type Handling:** ```python Pandas Optimization theme={null} import pandas as pd import numpy as np # Optimize data types for memory efficiency def optimize_csv_dtypes(df): # Optimize string columns for col in df.select_dtypes(include=['object']).columns: if df[col].nunique() < df.shape[0] * 0.5: # Less than 50% unique df[col] = df[col].astype('category') # Optimize numeric columns for col in df.select_dtypes(include=['int64']).columns: if df[col].min() >= 0: if df[col].max() <= 255: df[col] = df[col].astype('uint8') elif df[col].max() <= 65535: df[col] = df[col].astype('uint16') return df # Load and optimize df = pd.read_csv('exports/countries.csv') df = optimize_csv_dtypes(df) print(f"Memory usage reduced by {(1 - df.memory_usage(deep=True).sum() / original_memory) * 100:.1f}%") ``` **Excel Integration:** ```python Excel Automation theme={null} import pandas as pd from openpyxl import Workbook from openpyxl.chart import BarChart, Reference def create_analytical_report(csv_file): df = pd.read_csv(csv_file) # Create workbook with multiple sheets with pd.ExcelWriter('geographical_analysis.xlsx', engine='openpyxl') as writer: # Raw data df.to_excel(writer, sheet_name='Raw Data', index=False) # Regional summary regional_summary = df.groupby('region').agg({ 'name': 'count', 'currency': 'nunique', 'latitude': 'mean', 'longitude': 'mean' }).round(2) regional_summary.to_excel(writer, sheet_name='Regional Summary') # Countries by currency currency_countries = df.groupby('currency')['name'].count().sort_values(ascending=False).head(20) currency_countries.to_excel(writer, sheet_name='Currency Distribution') ``` **Performance Optimization:** ```sql Index Strategy theme={null} -- Create covering indexes for common queries CREATE INDEX idx_countries_region_currency_covering ON countries (region, currency) INCLUDE (name, iso2, phonecode); -- Partial indexes for active records CREATE INDEX idx_active_countries ON countries (iso2) WHERE flag = true; -- Composite indexes for complex queries CREATE INDEX idx_countries_location_search ON countries (region, subregion, currency, name); ``` **Data Validation:** ```sql Data Quality Checks theme={null} -- Add constraints to ensure data integrity ALTER TABLE countries ADD CONSTRAINT chk_iso2_length CHECK (LENGTH(iso2) = 2); ALTER TABLE countries ADD CONSTRAINT chk_currency_format CHECK (currency ~ '^[A-Z]{3}$'); ALTER TABLE countries ADD CONSTRAINT chk_phonecode_numeric CHECK (phonecode ~ '^[0-9+\s-]+$'); -- Create validation views CREATE VIEW invalid_countries AS SELECT id, name, iso2, 'Missing currency' as issue FROM countries WHERE currency IS NULL OR currency = '' UNION ALL SELECT id, name, iso2, 'Invalid ISO2 format' as issue FROM countries WHERE LENGTH(iso2) != 2 OR iso2 !~ '^[A-Z]{2}$'; ``` *** ## NDJSON Format (+2 Credits) NDJSON (Newline-Delimited JSON) writes one JSON object per line with no enclosing array, so each line is an independent, fully-valid JSON record. This makes it ideal for streaming, append-only logs, and big-data tools that process input line by line without loading the whole file into memory. ``` {"id":101,"name":"India","iso2":"IN","iso3":"IND","capital":"New Delhi","currency":"INR"} {"id":231,"name":"United States","iso2":"US","iso3":"USA","capital":"Washington","currency":"USD"} {"id":232,"name":"United Kingdom","iso2":"GB","iso3":"GBR","capital":"London","currency":"GBP"} ``` Because each line stands alone, you can stream an NDJSON export straight into tools like `jq`, BigQuery, ClickHouse, Spark, or Elasticsearch's bulk API with no array-unwrapping step. ### When to use NDJSON Process records one line at a time in data pipelines without buffering the entire dataset in memory. Feed warehouses and search engines (BigQuery, ClickHouse, Elasticsearch) that natively accept newline-delimited JSON. ## Markdown Format (+3 Credits) The Markdown export renders your selected data as GitHub-flavoured Markdown tables — one table per dataset, with a header row and one row per record. It is meant for documentation, READMEs, wikis, and pull-request descriptions where the data needs to be readable at a glance without any extra tooling. ``` | id | name | iso2 | iso3 | capital | currency | | --- | -------------- | ---- | ---- | ---------- | -------- | | 101 | India | IN | IND | New Delhi | INR | | 231 | United States | US | USA | Washington | USD | | 232 | United Kingdom | GB | GBR | London | GBP | ``` Markdown is best for smaller, curated exports embedded in docs. For large datasets, prefer CSV or JSON — a table with tens of thousands of rows is impractical to render in a Markdown viewer. ### When to use Markdown Drop ready-made tables into READMEs, wikis, or Mintlify / Docusaurus pages. Share a quick, readable snapshot of geographical data in issues, pull requests, or notes. ## Excel Format (+4 Credits) The Excel export produces a native `.xlsx` workbook — a binary spreadsheet with one worksheet per dataset (Countries, States, Cities), a bold header row, and sensible column widths. It opens directly in Microsoft Excel, Google Sheets, LibreOffice Calc, or Numbers with no import step. Use the native `.xlsx` export when you want a ready-to-share workbook with multiple sheets. If you only need a single table for scripting or BI ingestion, CSV is lighter and costs one credit less. ### When to use Excel Hand a polished, multi-sheet workbook to analysts and stakeholders who live in spreadsheets. Sort, filter, pivot, and chart geographical data without writing any code. *** **Need help choosing a format?** Consider your primary use case: * **Quick prototyping**: Start with JSON for web apps * **Data analysis**: Use CSV for spreadsheet compatibility * **Production database**: Invest in SQL for proper setup * **Configuration management**: YAML for readability * **Streaming & big data**: Use NDJSON for line-delimited records See detailed pricing for all formats and credit packages # Getting Started Source: https://docs.countrystatecity.in/tools/export-tool/getting-started Complete guide to signing up, understanding credits, and creating your first custom geographical data export in minutes. Get up and running with custom geographical data exports in just a few minutes. This guide walks you through account setup, understanding the credit system, and creating your first export. ## Step 1: Create Your Account Choose your preferred sign-up method to get started with free trial credits. **Quick One-Click Registration** 1. Visit [CSC Export Tool](https://export.countrystatecity.in) 2. Click "Sign up with GitHub" 3. Authorize the application 4. You're ready to go! **Instant Setup**: Your account is created immediately with free trial credits added automatically. **Magic Link Authentication** 1. Visit [CSC Export Tool](https://export.countrystatecity.in) 2. Click "Sign up with Email" 3. Enter your email address 4. Check your inbox for the magic link 5. Click the link to complete registration **No Password Required**: We use secure magic links for authentication - no need to remember another password! ## Step 2: Understanding the Credit System Credits are the currency used to generate exports. Here's everything you need to know: ### How Credits Work **Per Dataset** * Countries: 1 credit * States: 3 credits * Cities: 4 credits **Additional Format Fees** * JSON / NDJSON / XML / YAML: +2 credits * CSV / Markdown: +3 credits * SQL: +4 credits * Excel (.xlsx) / GeoJSON: +4 credits * PostgreSQL / SQL Server / SQLite3: +5 credits * MongoDB Extended JSON: +3 credits ### Credit Calculation Examples **Scenario**: Countries data in JSON format * Countries dataset: 1 credit * JSON format: +2 credits * **Total: 3 credits** Perfect for basic country dropdowns in web forms. **Scenario**: Countries with states data in JSON format * Countries dataset: 1 credit * States dataset: 3 credits * JSON format: +2 credits * **Total: 6 credits** Ideal for multi-level location selection in checkout forms. **Scenario**: All datasets in CSV format for Excel analysis * Countries dataset: 1 credit * States dataset: 3 credits * Cities dataset: 4 credits * CSV format: +3 credits * **Total: 11 credits** Great for comprehensive data analysis and visualization. **Scenario**: Countries data ready for PostgreSQL import (dialect-specific schema) * Countries dataset: 1 credit * PostgreSQL format: +5 credits * **Total: 6 credits** Perfect for populating database tables with geographical data. ### Free Trial Credits **Every new user receives free trial credits** to test the system and explore different export options before purchasing additional credits. See how many trial credits you get and compare credit packages ## Step 3: Your First Export Let's create a simple countries export to get familiar with the process. 1. Log into your account 2. Click "Create New Export" 3. Under "Datasets," select **Countries** 4. You'll see "1 credit" cost indicator Select the specific data fields you need: **Recommended for first export:** * `name` - Country name * `iso2` - 2-letter country code * `currency` - Currency code * `phone_code` - International dialing code **Start Simple**: Choose 4-5 essential fields for your first export to see how the system works. Choose **JSON** for your first export - it works great for web applications. ```json JSON Output Preview theme={null} { "countries": [ { "name": "United States", "iso2": "US", "currency": "USD", "phone_code": "1" }, { "name": "Canada", "iso2": "CA", "currency": "CAD", "phone_code": "1" } ] } ``` ```csv Alternative: CSV Format theme={null} name,iso2,currency,phone_code United States,US,USD,1 Canada,CA,CAD,1 ``` 1. Click **"Preview Export"** to see a sample of your data 2. Review the credit cost displayed 3. If satisfied, click **"Create Export"** **Credit Deduction**: Credits are deducted only when you click "Create Export," not during preview. 1. Wait for processing (usually 10-30 seconds) 2. Click **"Download ZIP"** when ready 3. Extract the ZIP file to access your JSON data **Success!** You've created your first custom geographical data export. ## Managing Your Credits ### Checking Your Balance Your credit balance is prominently displayed on the main dashboard: * Current credits remaining * Recent transaction history * Usage patterns and recommendations Access detailed credit information: * Complete transaction history * Credit purchase receipts * Usage analytics and insights * Upcoming credit expiration (credits never expire!) ### When You Need More Credits Compare credit packages and find the best option for your needs ### Purchasing Credits Choose from flexible credit packages and purchase securely **Payment Features:** * Powered by Dodo Payments * Major credit cards accepted * Secure, encrypted transactions * Credits added instantly to your account ## Account Dashboard Overview Your dashboard provides everything you need to manage exports and credits: ### Main Features * View all previous exports * Re-download files for 30 days * See credit costs for each export * Track usage patterns over time * Current balance display * Purchase history with receipts * Usage analytics and recommendations * No expiration dates on credits * Personal information management * Email notification preferences * API access (coming soon) * Security settings and login methods ## Next Steps Now that you've got the basics down, explore these advanced features: See real-world examples for e-commerce, mobile apps, and analytics Deep dive into JSON, CSV, XML, YAML, and SQL format options ## Common Getting Started Questions Most users complete their first export within 2-3 minutes of signing up. The process is designed to be intuitive and fast. Use the preview feature before confirming to see exactly what you'll get. Preview is free and doesn't use credits. You'll need to create a new export for different fields or formats. However, you can re-download the same export for 30 days. Simply purchase more credits - they're added instantly to your account and never expire. *** **Ready to create more exports?** Check out our [use cases guide](/tools/export-tool/use-cases) for inspiration and practical examples. # Overview Source: https://docs.countrystatecity.in/tools/export-tool/index Create custom geographical data exports without downloading massive databases. Get exactly the data you need, in the format you want, with just a few clicks. Sign up and get free trial credits to test the system Real-world examples for e-commerce, apps, and analytics JSON, CSV, XML, YAML, SQL, GeoJSON, and more formats available Have Questions? ## Why Choose CSC Export Tool? ### The Problem with Traditional Approaches **The Old Way**: Download entire geographical databases (hundreds of MBs), write code to filter data, deal with inconsistent formats, and waste hours on data preparation. ### The CSC Export Tool Solution **The Smart Way**: Select only the fields you need, choose from 10+ ready-to-use formats, get clean optimized datasets, and save hours of development time. ## Key Benefits **Choose Exactly What You Need** * Pick specific datasets (Countries, States, Cities) * Select only the fields you require * Filter by country or region * No unnecessary data bloat ```json Example: E-commerce Checkout theme={null} { "countries": [ { "name": "United States", "iso2": "US", "currency": "USD", "currency_symbol": "$" } ] } ``` **Ready for Any System** * **JSON**: Web apps, APIs, JavaScript * **CSV**: Spreadsheets, data analysis * **XML**: Enterprise systems * **YAML**: Configuration files * **SQL**: Database imports ```csv CSV for Analytics theme={null} name,iso2,population,gdp,area United States,US,331449281,21427700,9372610 China,CN,1439323776,14342300,9596960 ``` **Pay Only for What You Use** * Credits-based system * No monthly subscriptions * Free trial credits included * Credits never expire See credit packages and pricing details ## How It Works Choose GitHub or email sign-up and receive free trial credits to test the system. Pick datasets (Countries, States, Cities) and choose exactly which fields you need. Select from JSON, CSV, XML, YAML, or SQL based on your use case. Review your selections, confirm credit cost, and generate your custom dataset. Get your data as a convenient ZIP file and integrate it into your project. ## Popular Use Cases **Country dropdowns with currency info** * Dataset: Countries * Format: JSON * Fields: name, iso2, currency **Location services with coordinates** * Dataset: Cities * Format: JSON * Fields: cities with lat/lng **Population and economic data** * Dataset: Countries * Format: CSV * Fields: countries with stats ## Credit System Overview Understanding how credits work helps you plan your exports efficiently: **Dataset Costs:** * Countries: **1 credit** * States: **3 credits** * Cities: **4 credits** **Format Costs:** * JSON / NDJSON / XML / YAML: **+2 credits** * CSV / Markdown: **+3 credits** * SQL: **+4 credits** * Excel (.xlsx) / GeoJSON: **+4 credits** * PostgreSQL / SQL Server / SQLite3: **+5 credits** * MongoDB Extended JSON: **+3 credits** Filters and add-ons may add more (region/multi-country, translations, sort, flag bundle). The preview always shows the exact cost before you confirm. **Example Calculation**: Countries + States data in CSV format = 1 + 3 + 3 = **7 credits total** ## Pricing & Credit Packages Compare credit packages and find the best option for your needs ## Data Quality & Freshness **Always Current**: Our geographical database is updated daily with the latest information from reliable sources, ensuring you get the most current data available. * **Daily Updates**: Fresh data synchronized every 24 hours * **Reliable Sources**: Data from authoritative geographical databases * **Version Tracking**: Consistent data across all exports * **Quality Assurance**: Validated and cleaned data ready for production use * **Bulk Exports**: Handle large datasets efficiently * **Custom Filtering**: Filter by specific countries or regions * **Export History**: Track and manage all your previous exports * **30-Day Access**: Re-download exports for a full month ## Ready to Start? Get started today with free trial credits and see how the CSC Export Tool can streamline your development workflow. Sign up for free and create your first custom export in minutes *** **Need Help?** Our documentation covers everything from basic usage to advanced features. For additional support, contact us at [export@countrystatecity.in](mailto:export@countrystatecity.in) # Use Cases Source: https://docs.countrystatecity.in/tools/export-tool/use-cases Real-world examples showing how to use CSC Export Tool for e-commerce, mobile apps, analytics, and more with exact cost breakdowns. Discover how developers across different industries use the CSC Export Tool to solve real-world challenges. Each use case includes exact field selections, format recommendations, and cost breakdowns. ## E-commerce & Online Stores ### 🛒 Checkout Country Dropdown **Challenge**: Create a country selection dropdown for checkout forms with currency information. **What You Need:** * Country names for the dropdown * ISO codes for form processing * Currency codes for payment processing * Currency symbols for price display **Expected Outcome:** Clean dropdown with \~195 countries, each with currency information for seamless checkout experience. **Dataset Selection:** * ✅ Countries **Field Selection:** * `name` - Display name for dropdown * `iso2` - For form processing and validation * `currency` - Payment processing integration * `currency_symbol` - Price display formatting * `emoji` - Enhanced UX (optional) **Format:** JSON (perfect for web applications) See current credit costs for this configuration ```json Sample Output theme={null} { "countries": [ { "name": "United States", "iso2": "US", "currency": "USD", "currency_symbol": "$", "emoji": "🇺🇸" }, { "name": "United Kingdom", "iso2": "GB", "currency": "GBP", "currency_symbol": "£", "emoji": "🇬🇧" } ] } ``` ```javascript Frontend Integration theme={null} // Load countries data const countries = exportData.countries; // Populate dropdown countries.forEach(country => { const option = document.createElement('option'); option.value = country.iso2; option.textContent = `${country.emoji} ${country.name}`; countrySelect.appendChild(option); }); // Update currency display when country changes countrySelect.addEventListener('change', (e) => { const selectedCountry = countries.find(c => c.iso2 === e.target.value); updatePriceDisplay(selectedCountry.currency_symbol); }); ``` ### 🌍 Multi-Region Shipping Calculator **Challenge**: Build a shipping calculator that works across countries and states. * Countries with regions * States with country relationships * Geographic coordinates for distance calculation See current credit costs for Countries + States exports **Datasets:** Countries + States **Countries Fields:** * `name`, `iso2`, `currency`, `region`, `subregion` **States Fields:** * `name`, `iso2`, `country_code`, `latitude`, `longitude` **Format:** JSON ```javascript Shipping Calculator theme={null} // Group states by country for efficient lookup const statesByCountry = {}; exportData.states.forEach(state => { if (!statesByCountry[state.country_code]) { statesByCountry[state.country_code] = []; } statesByCountry[state.country_code].push(state); }); // Calculate shipping based on location function calculateShipping(countryCode, stateCode = null) { const country = exportData.countries.find(c => c.iso2 === countryCode); if (stateCode && statesByCountry[countryCode]) { const state = statesByCountry[countryCode].find(s => s.iso2 === stateCode); return calculateDistanceShipping(state.latitude, state.longitude); } return getRegionalShippingRate(country.region); } ``` ## Mobile Application Development ### 📱 Location-Based Service App **Challenge**: Create a travel app with city-based recommendations and weather integration. **App Type:** Travel recommendations app **Need:** Cities with coordinates for weather API integration and location services **Target:** All available cities with coordinate data **Dataset:** Cities **Essential Fields:** * `name` - City display name * `country_code` - Country context * `state_code` - State/province context * `latitude`, `longitude` - Weather API integration **Format:** JSON See current credit costs for Cities exports ```swift iOS Implementation theme={null} struct City: Codable { let name: String let countryCode: String let stateCode: String let latitude: Double let longitude: Double } // Filter cities by country or region if needed let filteredCities = cities.filter { $0.countryCode == "US" } // Integrate with weather service func getWeatherForCity(_ city: City) { let weatherAPI = "https://api.weather.com/v1/current" let url = "\(weatherAPI)?lat=\(city.latitude)&lng=\(city.longitude)" // Use coordinates for weather API integration // ... weather integration } ``` ### 🗺️ Offline Maps Application **Challenge**: Pre-load geographical boundaries for offline map functionality. **Data Volume Consideration**: Cities dataset contains 153,765+ records. Consider filtering by country or region to optimize app size and performance. Use the built-in country filter to export only relevant regions: ```json Export Configuration theme={null} { "datasets": ["states"], "country_filter": ["US", "CA", "MX"], // North America only "fields": ["name", "state_code", "country_code", "latitude", "longitude"], "format": "json" } ``` ```javascript Data Processing theme={null} // Group by country for efficient storage const statesByCountry = exportData.states.reduce((acc, state) => { acc[state.country_code] = acc[state.country_code] || []; acc[state.country_code].push({ name: state.name, code: state.state_code, coords: [state.latitude, state.longitude] }); return acc; }, {}); // Store in mobile app's local database await storeOfflineData('geographical_boundaries', statesByCountry); ``` ## Data Analytics & Business Intelligence ### 📊 Market Analysis Dashboard **Challenge**: Create a comprehensive dashboard showing global market data with geographic and currency insights. **Countries with:** * Regional classifications * Currency information * Geographic data * Language and timezone data **CSV for Excel/Tableau** * Easy pivot table creation * Chart integration * Stakeholder sharing See current credit costs for analytics exports **Comprehensive Analytics Fields:** ```csv Field Configuration theme={null} name,iso2,iso3,region,subregion,currency,currency_name, currency_symbol,phonecode,capital,latitude,longitude, timezones,translations,native,emoji,nationality ``` **Why These Fields:** * **Geographic**: region, subregion, coordinates for mapping * **Currency**: currency codes, names, symbols for market analysis * **Communication**: phonecode, timezones for business operations * **Cultural**: native names, translations, nationality for localization ```excel Power Query Integration theme={null} // Excel Power Query M formula let Source = Csv.Document(File.Contents("C:\exports\countries_analytics.csv")), Headers = Table.PromoteHeaders(Source), // Add calculated columns WithPhonePrefix = Table.AddColumn(Headers, "PhonePrefix", each "+" & [phonecode]), WithCurrencyInfo = Table.AddColumn(WithPhonePrefix, "CurrencyDisplay", each [currency_symbol] & " (" & [currency] & ")"), // Group by region for analysis GroupedByRegion = Table.Group(WithCurrencyInfo, {"region"}, {{"Countries", each _, type table}}) in GroupedByRegion ``` **Key Visualizations:** 1. **World Map**: Currency zones by country 2. **Regional Distribution**: Country count by region 3. **Communication Coverage**: Phone code analysis 4. **Time Zone Analysis**: Global business hours overlap ```sql Tableau Calculated Fields theme={null} // Currency Zone Display [currency_symbol] + " " + [currency_name] // Phone Code Formatting "+" + [phonecode] // Regional Country Count COUNT([name]) ``` ### 📈 Sales Territory Planning **Challenge**: Optimize sales territories based on geographic and regional data. **Datasets Needed:** * Countries: Base territory definition * States: Sub-territory planning * Cities: Account distribution analysis **Export Configuration:** * Countries + States + Cities * CSV format for spreadsheet analysis See current credit costs for territory mapping exports **ROI Justification:** Saves 20+ hours of data collection and cleaning ```sql Territory Analysis Queries theme={null} -- Create territory performance views CREATE VIEW territory_performance AS SELECT co.region as territory, COUNT(DISTINCT co.iso2) as countries_covered, COUNT(DISTINCT co.currency) as currencies_supported, COUNT(DISTINCT s.id) as states_count, COUNT(DISTINCT ci.id) as cities_count, GROUP_CONCAT(DISTINCT co.currency) as currency_list FROM countries co LEFT JOIN states s ON co.iso2 = s.country_code LEFT JOIN cities ci ON s.id = ci.state_id GROUP BY co.region; -- Identify expansion opportunities SELECT territory, countries_covered, currencies_supported, currency_list FROM territory_performance WHERE countries_covered < 5 -- Underrepresented territories ORDER BY countries_covered DESC; ``` ## Web Development & APIs ### 🌐 Multi-language Website Localization **Challenge**: Support international users with localized country/region names. **Localization Tip**: CSC data includes native country names and translations, perfect for international websites serving global audiences. **Dataset:** Countries **Localization Fields:** * `name` - English name * `native` - Native language name * `translations` - Multiple language support * `currency` - Local currency * `nationality` - Country nationality * `emoji` - Country flag emoji **Format:** JSON See current credit costs ```javascript i18n Integration theme={null} // Country data with translations const countryData = { "US": { "name": "United States", "native": "United States", "translations": { "es": "Estados Unidos", "fr": "États-Unis", "de": "Vereinigte Staaten" }, "nationality": "American", "emoji": "🇺🇸" } }; // Localization function function getLocalizedCountryName(countryCode, locale = 'en') { const country = countryData[countryCode]; if (locale === 'en') return country.name; if (locale === country.native.toLowerCase().slice(0, 2)) return country.native; if (country.translations[locale]) return country.translations[locale]; return country.name; // Fallback to English } // Usage in React component function CountrySelector({ locale }) { return ( ); } ``` ### 🔗 API Response Enhancement **Challenge**: Enhance existing API responses with geographical context. **Scenario**: Add country/region context to user profiles ```json Before Enhancement theme={null} { "user_id": "123", "name": "John Doe", "country_code": "US" } ``` ```json After Enhancement theme={null} { "user_id": "123", "name": "John Doe", "location": { "country": "United States", "country_code": "US", "region": "Northern America", "currency": "USD", "timezone": "America/New_York", "flag": "🇺🇸" } } ``` ```javascript API Enhancement theme={null} // Load geographical data at server startup const geoData = loadCSCExport('./countries_enhanced.json'); const countryLookup = new Map( geoData.countries.map(c => [c.iso2, c]) ); // Enhance API responses app.get('/users/:id', async (req, res) => { const user = await User.findById(req.params.id); const countryInfo = countryLookup.get(user.country_code); res.json({ ...user.toJSON(), location: { country: countryInfo.name, country_code: countryInfo.iso2, region: countryInfo.region, currency: countryInfo.currency, timezone: countryInfo.timezones[0], // Primary timezone flag: countryInfo.emoji } }); }); ``` ## Database & DevOps ### 🗄️ Database Seeding & Migration **Challenge**: Populate database tables with geographical reference data. **Production Tip**: Always use SQL format for database imports to ensure proper data types, constraints, and relationships. **Export Configuration:** * Dataset: Countries + States * Format: SQL See current credit costs for database exports ```sql Generated Output Example theme={null} -- Table creation (automatically included) CREATE TABLE IF NOT EXISTS countries ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, iso2 CHAR(2) UNIQUE NOT NULL, iso3 CHAR(3) UNIQUE NOT NULL, phonecode VARCHAR(255), currency CHAR(3), region VARCHAR(100), subregion VARCHAR(100) ); -- Data insertion INSERT INTO countries (name, iso2, iso3, phonecode, currency, region, subregion) VALUES ('United States', 'US', 'USA', '1', 'USD', 'Americas', 'Northern America'), ('Canada', 'CA', 'CAN', '1', 'CAD', 'Americas', 'Northern America'); -- Foreign key relationships automatically handled CREATE TABLE IF NOT EXISTS states ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, state_code VARCHAR(10) NOT NULL, country_id INTEGER REFERENCES countries(id), latitude DECIMAL(10, 8), longitude DECIMAL(11, 8) ); ``` ```javascript Node.js Migration theme={null} // database/migrations/001_seed_geographical_data.js const fs = require('fs'); const path = require('path'); exports.up = async function(knex) { // Read the CSC-generated SQL file const sqlFile = path.join(__dirname, '../seeds/csc_export.sql'); const sqlContent = fs.readFileSync(sqlFile, 'utf8'); // Execute the SQL statements const statements = sqlContent.split(';').filter(stmt => stmt.trim()); for (const statement of statements) { if (statement.trim()) { await knex.raw(statement); } } console.log('Geographical data seeded successfully'); }; exports.down = async function(knex) { await knex('states').del(); await knex('countries').del(); }; ``` ```dockerfile Database Container theme={null} # Dockerfile for PostgreSQL with geographical data FROM postgres:14 # Copy CSC export SQL file COPY ./exports/geographical_data.sql /docker-entrypoint-initdb.d/ # Environment variables ENV POSTGRES_DB=myapp ENV POSTGRES_USER=app_user ENV POSTGRES_PASSWORD=secure_password # The SQL file will be automatically executed on container startup ``` ```yaml Docker Compose theme={null} version: '3.8' services: database: build: ./database ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data - ./exports:/tmp/exports environment: - POSTGRES_DB=myapp volumes: pgdata: ``` ## Cost Optimization Strategies ### 💡 Minimize Credit Usage **Only export fields you actually need** * Audit your code to identify used fields * Remove debugging/testing fields * Consider computed fields vs exported fields **Choose the right format** * JSON: Great for web apps * CSV: Great for analysis * SQL: Great for databases ### 🎯 Strategic Export Planning **Instead of:** 3 separate exports (3 transactions, potential for higher costs) * Countries for dropdown (1 credit) * States for shipping (3 credits) * Countries for currency (1 credit) - duplicate! **Smart approach:** 1 combined export (Countries + States + JSON = 6 credits total) * Countries + States with all needed fields * Single download, unified data structure * Better data consistency **Re-download Strategy:** * Create comprehensive exports monthly * Re-download for different projects within 30 days * Share exports across team members * Plan exports around sprint cycles **Development Phase:** * Use free JSON format for prototyping * Export minimal field sets for testing * Iterate with preview feature (free) **Production Deployment:** * Create final export with all needed fields * Choose production-appropriate format * Document export configuration for future updates *** **Ready to implement your use case?** Start with the [getting started guide](/tools/export-tool/getting-started) to create your account and get free trial credits for testing. # CSC Tools Overview Source: https://docs.countrystatecity.in/tools/overview Discover powerful tools to work with geographical data - export custom datasets and contribute improvements to the world's most comprehensive location database. ## Choose Your Tool **Get Data** - Create custom geographical data exports without downloading massive databases **Contribute Data** - Help improve the world's most comprehensive geographical database *** ## Which Tool is Right for You? **Choose Export Tool if you want to:** * Get custom geographical data for your application * Export only specific fields you need * Choose from multiple formats (JSON, CSV, XML, YAML, SQL) * Save development time on data preparation * Pay only for what you use with credits **Perfect for:** E-commerce sites, mobile apps, analytics platforms, business intelligence, any application needing location data. Get free trial credits and create your first custom export **Choose Update Tool if you want to:** * Fix incorrect geographical information * Add missing countries, states, or cities * Update outdated location data * Help thousands of developers worldwide * Contribute to open-source geographical data **Perfect for:** Data enthusiasts, local experts, developers who've found data issues, anyone passionate about data accuracy. Join thousands improving geographical data worldwide *** ## Export Tool - Get the Data You Need Transform how you work with geographical data. Instead of downloading massive databases and writing complex filtering code, get exactly the data you need in minutes. ### Key Features Pick specific datasets and fields - no data bloat JSON, CSV, XML, YAML, and SQL ready for any system Pay only for what you use - no subscriptions ### Popular Use Cases | Use Case | Data Needed | Format | | :- | :- | :- | | **E-commerce Checkout** | Countries with currencies | JSON | | **Location Services** | Cities with coordinates | JSON | | **Data Analytics** | Countries with coordinates/timezones | CSV | See current credit costs for all export types ### Quick Start Process Get free trial credits instantly Choose countries, states, or cities with specific fields Choose JSON, CSV, XML, YAML, or SQL Get your custom dataset as a convenient ZIP file **Free Trial**: Start with free trial credits to test the system. [View pricing](https://countrystatecity.in/pricing) for details. *** ## Update Tool - Improve Global Data Quality Contribute to the world's most comprehensive geographical database used by thousands of developers. Your contributions help millions of users get accurate location information. ### Your Global Impact Every correction you make helps: Use this data in their applications worldwide Get accurate location information in apps and services E-commerce, weather, travel, and mapping apps Strengthen the open-source geographical data ecosystem ### What You Can Contribute * **Countries**: Add new countries or fix country information * **Regions**: Continental and sub-continental groupings * **States/Provinces**: Administrative divisions within countries * **Cities**: Urban areas and municipalities ### Simple Contribution Process Use Magic Link (email) or GitHub authentication Add, update, or delete geographical data with sources Admins and community members review your changes Approved changes are incorporated into the main database Your improvements help developers worldwide ### Track Your Contributions * **Statistics**: Total submissions and approval rate * **Notifications**: Real-time updates on your requests * **Impact Metrics**: See how many developers benefit from your changes * **Incorporation Tracking**: Follow changes through the entire system **100% Free**: Contributing is completely free and helps the entire developer community! *** ## How Tools Work Together The Export Tool and Update Tool create a powerful ecosystem: ```mermaid theme={null} graph TD A[Update Tool Contributors] --> B[Database Improvements] B --> C[Enhanced Geographical Database] C --> D[Export Tool Users] D --> E[Better Applications] E --> F[More Users Discovering Issues] F --> A ``` *** ## Need Help? Complete guide to creating custom data exports Learn how to contribute geographical improvements Integrate geographical data directly into your applications *** ## Join the Community Whether you're consuming geographical data or contributing improvements, you're part of a global community making location data more accurate and accessible for everyone. **Ready to get started?** Get free trial credits and create your first custom export Make your first contribution to improve global data quality *** **Questions?** Our community is here to help. Check our documentation, join GitHub discussions, or reach out directly for support. # FAQ Source: https://docs.countrystatecity.in/tools/update-tool/faq Find answers to common questions about using the CSC Update Tool and contributing to the database. ## Frequently Asked Questions **Q: Do I need to create an account to contribute?** A: No! Just use the Magic Link or GitHub login. An account will be created automatically. **Q: Is there a cost to use the CSC Update Tool?** A: Absolutely not! It's completely free and open-source. **Q: Can I submit multiple change requests?** A: Yes! You can submit as many as you like. **Q: How long does review take?** A: Reviews typically happen within 1-7 days, depending on complexity and admin availability. **Q: Can I edit my request after submitting?** A: You can edit pending requests. Once approved or rejected, you'll need to submit a new request. **Q: What if I make a mistake?** A: No problem! Use the comments to clarify, or submit a new request with corrections. **Q: Why was my change rejected?** A: Check the comments section for specific feedback. Common reasons include insufficient sources or data quality issues. **Q: How often is the main database updated?** A: The main database syncs automatically every week, typically on Sundays. **Q: Will my approved change definitely appear in the database?** A: Approved changes are incorporated, but our verification system checks if they persist through sync cycles. **Q: Can I see the SQL for my changes?** A: Yes! Approved changes show a "View SQL" option so you can see exactly what gets updated. **Q: What if my change gets overwritten during sync?** A: Our incorporation tracking system detects this and can re-apply your changes as needed. **Q: What sources are considered reliable?** A: Government websites, official statistical offices, census data, and reputable geographical authorities. **Q: Can I add places that don't officially exist yet?** A: Generally no. We focus on officially recognized administrative divisions and established cities. **Q: How do you handle disputed territories?** A: We follow internationally recognized standards and may defer to authoritative geographical organizations. **Q: What about historical changes?** A: We maintain current data. Historical administrative divisions are typically not included unless still relevant. **Q: I found a bug or need to report an issue. Where should I go?** A: For technical issues, bugs, or feature requests, please submit an issue on our GitHub repository: [https://github.com/dr5hn/countries-states-cities-database/issues](https://github.com/dr5hn/countries-states-cities-database/issues) **Q: What information should I include when reporting an issue?** A: Please provide: * A clear description of the problem * Steps to reproduce the issue * Expected vs actual behavior * Screenshots if relevant * Your use case or context **Q: How do I get help with using the Update Tool?** A: Start by checking this documentation and FAQ. For specific questions about your submissions, use the comments section on your change requests. For technical issues, create a GitHub issue. # Getting Started Source: https://docs.countrystatecity.in/tools/update-tool/getting-started Learn how to access the CSC Update Tool and start contributing to the world's most comprehensive geographical database. ## Getting Started ### Step 1: Access the Tool Visit the **CSC Update Tool** at your designated URL. You'll see a welcoming interface showcasing the platform ecosystem. ### Step 2: Sign In You have two convenient authentication options: Click **"Get Magic Link"** and enter your email address Click **"Send Magic Link"** and check your email Click the secure link in your email to authenticate You'll be automatically logged in and ready to contribute Click **"Continue with GitHub"** Authorize the CSC Update Tool application You'll be redirected back and automatically logged in **First-time users**: Don't worry about creating an account - both methods will automatically create your profile! # Overview Source: https://docs.countrystatecity.in/tools/update-tool/index Contribute to the world's most comprehensive geographical database. Help thousands of developers by improving location data through our collaborative update system. Get started with your first change request in minutes Learn how to submit effective change requests Understand how your changes get reviewed and approved ## What You'll Learn * How to get started and create your first change request * Understanding the review and approval process * Best practices for successful submissions *** ## Complete User Guide Access the tool and authenticate with Magic Link or GitHub Learn to submit your first change request effectively Follow your changes from submission to database integration How changes are reviewed and approved Frequently asked questions and answers *** ## Welcome to the Community! Thank you for contributing to the world's most comprehensive geographical database! Your efforts help thousands of developers create better applications and serve millions of users worldwide. Every correction you make, every new city you add, and every improvement you suggest makes the global digital ecosystem more accurate and useful. **Ready to make your first contribution?** Join thousands of contributors improving geographical data worldwide *** **Need Help?** Check our [FAQ section](/tools/update-tool/faq) for common questions. For technical issues or bug reports, please submit an issue on our [GitHub repository](https://github.com/dr5hn/countries-states-cities-database/issues). # Review Process Source: https://docs.countrystatecity.in/tools/update-tool/review-process Learn how your change requests are reviewed and how to increase your approval chances. ## The Review Process ### Who Reviews Your Changes * **👥 Community Members**: Other users can comment and discuss * **🛡️ Administrators**: CSC team members who can approve/reject * **🤖 Automated Checks**: System validates data format and consistency ### How to Increase Approval Chances #### ✅ **Best Practices** ``` ✅ Good: "According to the official census at [government-url]..." ❌ Bad: "I think this city has 50,000 people" ``` ``` ✅ Good: "The city name is misspelled. Should be 'São Paulo' not 'Sao Paulo'" ❌ Bad: "This city name is wrong" ``` ``` ✅ Good: "This new city was officially incorporated in 2023 as per [official-document]" ❌ Bad: "Please add this city" ``` ``` ✅ Good: Use official, local language names with proper diacritics ❌ Bad: Anglicized or informal names ``` #### ❌ **Common Rejection Reasons** * **📍 Insufficient Sources**: No credible references provided * **🔍 Duplicate Data**: Entry already exists in database * **📝 Poor Quality**: Incomplete or inaccurate information * **🚫 Invalid Request**: Doesn't meet database standards * **⚠️ Formatting Issues**: Incorrect data format or structure ### Responding to Feedback If your request receives comments: You'll be notified of new comments Understand what reviewers are asking Answer questions or provide clarifications Edit your request if needed (for pending requests) Keep discussions constructive and respectful # Submitting Changes Source: https://docs.countrystatecity.in/tools/update-tool/submitting-changes Learn how to submit effective change requests for countries, states, and cities in the CSC database. ## Submitting Your First Change Request ### Understanding What You Can Change The CSC Update Tool accepts changes for: Add new countries or fix country information Continental and sub-continental groupings Administrative divisions within countries Urban areas and municipalities ### Step-by-Step Submission Process * Click **"Submit a Change Request"** on the dashboard * Or use the **"New Request"** button in the navigation **📍 Adding New Data** * Select **"Add"** if you're adding a completely new entry * Example: Adding a new city that doesn't exist in the database **✏️ Updating Existing Data** * Select **"Update"** to correct existing information * Example: Fixing a misspelled city name or updating coordinates **🗑️ Removing Invalid Data** * Select **"Delete"** for entries that shouldn't exist * Example: Duplicate cities or obsolete administrative divisions Choose what type of data you're changing: * **Country**: National-level changes * **Region/Subregion**: Continental groupings * **State**: State, province, or administrative division * **City**: Cities, towns, or municipalities * **Name**: Official name of the location * **Parent Location**: Which country/state it belongs to * **Additional Info**: Population, coordinates, or other relevant data * **ISO Codes**: If applicable (for countries/states) * **Current Information**: What the database currently shows * **Proposed Changes**: What it should be changed to * **Reason**: Why this change is needed * **Item to Remove**: Specify exactly what should be deleted * **Justification**: Why this entry is incorrect or duplicate * **📝 Description**: Explain your change in detail * **🔍 Sources**: Provide reliable sources (government websites, official documents) * **📊 Impact**: Mention if this affects many users or applications * **Preview**: Review all your information * **Auto-save**: Your draft is automatically saved as you work * **Submit**: Click "Submit Change Request" when ready **Auto-save Feature**: Don't worry about losing your work! The system automatically saves your progress as you type. # Workflow Source: https://docs.countrystatecity.in/tools/update-tool/workflow Learn how change requests move through the system from submission to incorporation in the database. ## Understanding the Workflow Your change request goes through several stages: ### Status Meanings | Status | Description | What Happens Next | | - | - | - | | **📝 Draft** | You're still working on it | Complete and submit when ready | | **⏳ Pending** | Submitted and awaiting review | Community and admins will review | | **✅ Approved** | Change accepted by reviewers | Queued for incorporation into database | | **❌ Rejected** | Change not accepted | Check feedback and consider resubmitting | ### Incorporation Tracking Once approved, your changes enter the **Incorporation System**: | Incorporation Status | Description | | - | - | | **⏳ Pending** | Approved but not yet in main database | | **🔄 Incorporated** | Successfully added to main database | | **✅ Verified** | Confirmed present after database sync | | **⚠️ Missing** | May have been overwritten during sync | | **🔍 Conflicted** | Data conflict detected |