> ## Documentation Index
> Fetch the complete documentation index at: https://docs.countrystatecity.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Change Feed

> 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.

<Note>
  **Available on the Business plan.** [Compare plans](https://countrystatecity.in/pricing?source=docs\&campaign=data_change_feed) to add the change feed to your API key.
</Note>

## 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.

## Authentication

<ParamField header="X-CSCAPI-KEY" type="string" required>
  Your API key.
</ParamField>

## Query parameters

<ParamField query="start_date" type="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.
</ParamField>

<ParamField query="place_type" type="string">
  Return only `country`, `state`, or `city` changes.
</ParamField>

<ParamField query="country_code" type="string">
  Return changes for one country and its states and cities. Use a two-letter code such as `IN` or `US`.
</ParamField>

<ParamField query="change_type" type="string">
  Return only one [change type](#change-types).
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Number of records per page. Use a value from `1` to `100`.
</ParamField>

<ParamField query="next_page_token" type="string">
  Opaque token returned by the previous page. Tokens expire after 24 hours.
</ParamField>

## 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.

<ResponseField name="results" type="array">
  Change records for this page.
</ResponseField>

<ResponseField name="results[].change_id" type="string">
  Unique UUID for the change. Save it to make retries safe.
</ResponseField>

<ResponseField name="results[].data_version" type="string">
  Dataset version that produced the change. It matches [Get Data Version](/api/endpoints/get-data-version).
</ResponseField>

<ResponseField name="results[].changed_at" type="string">
  ISO 8601 time when the release was recorded.
</ResponseField>

<ResponseField name="results[].place_type" type="string">
  `country`, `state`, or `city`.
</ResponseField>

<ResponseField name="results[].place_id" type="string">
  Stable ID of the affected place. IDs in this feed are strings.
</ResponseField>

<ResponseField name="results[].change_type" type="string">
  What changed.
</ResponseField>

<ResponseField name="results[].old_values" type="object | null">
  Values before the change. It is `null` for `added`. An update contains only changed fields; `removed` contains the full caller-visible record.
</ResponseField>

<ResponseField name="results[].new_values" type="object | null">
  Values after the change. It is `null` for `removed`. An update contains only changed fields; `added` contains the full caller-visible record.
</ResponseField>

<ResponseField name="next_page_token" type="string | null">
  Token for the next page, or `null` when the fixed result set is complete.
</ResponseField>

```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"
}
```

<Info>
  `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`.
</Info>

## 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;
}
```

<RequestExample>
  ```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()
  ```
</RequestExample>

## 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 - Business plan required theme={null}
{
  "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

<CardGroup cols={2}>
  <Card title="Compare API plans" icon="credit-card" href="https://countrystatecity.in/pricing?source=docs&campaign=data_change_feed">
    Choose Business to keep your database current with small incremental requests.
  </Card>

  <Card title="Get Data Version" icon="clock-rotate-left" href="/api/endpoints/get-data-version">
    Check which dataset release your API responses use.
  </Card>
</CardGroup>
