# 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
```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
```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
```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
```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
```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
## 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:00Z1.0250United StatesUnited StatesUSUSA🇺🇸1WashingtonUS DollarUS DollarAmericasNorthern America37.09024-95.712891AmericanUnited StatesAmerica/New_YorkAmerica/ChicagoAmerica/DenverAmerica/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 |