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'
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()
{
"results": [
{}
],
"results[].change_id": "<string>",
"results[].data_version": "<string>",
"results[].changed_at": "<string>",
"results[].place_type": "<string>",
"results[].place_id": "<string>",
"results[].change_type": "<string>",
"results[].old_values": {},
"results[].new_values": {},
"next_page_token": {}
}Data Changes
Data Change Feed
Keep your copy of Country State City data current without downloading the full dataset again
GET
/
v1
/
changes
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'
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()
{
"results": [
{}
],
"results[].change_id": "<string>",
"results[].data_version": "<string>",
"results[].changed_at": "<string>",
"results[].place_type": "<string>",
"results[].place_id": "<string>",
"results[].change_type": "<string>",
"results[].old_values": {},
"results[].new_values": {},
"next_page_token": {}
}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.
When several fields change in one release, the record has one main
If a token expires, is changed, or is invalid, start again with
Available on the Business plan. Compare plans 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 stablechange_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.
Authentication
string
required
Your API key.
Query parameters
string
Return changes at or after this ISO 8601 time, for example
2026-08-01T00:00:00Z. Omit it to start with the oldest retained change.string
Return only
country, state, or city changes.string
Return changes for one country and its states and cities. Use a two-letter code such as
IN or US.string
Return only one change type.
integer
default:"50"
Number of records per page. Use a value from
1 to 100.string
Opaque token returned by the previous page. Tokens expire after 24 hours.
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. |
change_type, but old_values and new_values include every changed field.
Response
The API returns changes from oldest to newest.array
Change records for this page.
string
Unique UUID for the change. Save it to make retries safe.
string
Dataset version that produced the change. It matches Get Data Version.
string
ISO 8601 time when the release was recorded.
string
country, state, or city.string
Stable ID of the affected place. IDs in this feed are strings.
string
What changed.
object | null
Values before the change. It is
null for added. An update contains only changed fields; removed contains the full caller-visible record.object | null
Values after the change. It is
null for removed. An update contains only changed fields; added contains the full caller-visible record.string | null
Token for the next page, or
null when the fixed result set is complete.200 - Renamed city
{
"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
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;
}
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'
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
Ifstart_date is older than the retained data, use details.earliestAvailableDate to restart from the oldest available change:
400 - Start date is too old
{
"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"
}
}
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:
403 - Business plan required
{
"status": "error",
"message": "This feature is not available on your current plan.",
"details": {
"feature": "dataChangeFeed",
"currentTier": "professional",
"requiredTier": "business",
"upgradeUrl": "https://app.countrystatecity.in/pricing"
}
}
Start syncing changes
Compare API plans
Choose Business to keep your database current with small incremental requests.
Get Data Version
Check which dataset release your API responses use.
Was this page helpful?
⌘I