Skip to content

Migration from v1 ​

This page summarizes the main changes required when moving an integration from TurkiyeAPI v1 to v2.

v1 exposed a compact set of read-only endpoints under /v1 and /api/v1. v2 keeps the API read-only, but it makes the contract more explicit: routes are grouped under /v2, responses include metadata, related resources are requested intentionally, and the old towns model is replaced by the broader municipalities model.

Quick Checklist ​

  • Change the API prefix from /v1 or /api/v1 to /v2.
  • Stop reading the top-level status field from successful responses.
  • Read pagination and dataset details from the new meta object.
  • Replace name filters with search.
  • Replace parent name filters such as province and district with ID filters or nested routes.
  • Replace extend=true with include=....
  • Replace /towns with /municipalities?type=town when you only need town municipalities.
  • Review field names because several v1 scalar fields became structured objects in v2.
  • Update error handling to use error.code, error.message, and error.status.

Base URL and Version Prefix ​

v1 accepted both prefixes:

text
/v1
/api/v1

v2 uses:

text
/v2

Examples:

bash
curl "https://api.turkiyeapi.dev/v2/provinces"
curl "https://api.turkiyeapi.dev/v2/provinces/34?include=districts"

Response Format ​

v1 successful responses used a status field:

json
{
  "status": "OK",
  "data": []
}

v2 successful responses use data and meta:

json
{
  "data": [],
  "meta": {
    "count": 10,
    "total": 81,
    "limit": 10,
    "offset": 0,
    "datasetVersion": "2025",
    "lastUpdated": "2026-05-21"
  }
}

Single-resource endpoints also include dataset metadata:

json
{
  "data": {
    "id": 34,
    "name": "İstanbul"
  },
  "meta": {
    "datasetVersion": "2025",
    "lastUpdated": "2026-05-21"
  }
}

Error Format ​

v1 errors used:

json
{
  "status": "ERROR",
  "error": "No province found."
}

v2 errors use a structured object:

json
{
  "error": {
    "code": "PROVINCE_NOT_FOUND",
    "message": "Province not found.",
    "status": 404
  }
}

Validation problems are reported as 400 Bad Request. Missing resources are reported as 404 Not Found. Unknown routes return ROUTE_NOT_FOUND.

Endpoint Mapping ​

v1v2
GET /Use documentation or GET /v2/meta for API metadata
GET /provincesGET /v2/provinces
GET /provinces/:idGET /v2/provinces/{provinceId}
Province districts embedded by defaultGET /v2/provinces/{provinceId}?include=districts or GET /v2/provinces/{provinceId}/districts
Province neighborhoods via extend=trueGET /v2/provinces/{provinceId}?include=neighborhoods or GET /v2/provinces/{provinceId}/neighborhoods
Province villages via extend=trueGET /v2/provinces/{provinceId}?include=villages or GET /v2/provinces/{provinceId}/villages
GET /districtsGET /v2/districts
GET /districts/:idGET /v2/districts/{districtId}
District neighborhoods embedded by defaultGET /v2/districts/{districtId}?include=neighborhoods or GET /v2/districts/{districtId}/neighborhoods
District villages embedded by defaultGET /v2/districts/{districtId}?include=villages or GET /v2/districts/{districtId}/villages
GET /neighborhoodsGET /v2/neighborhoods
GET /neighborhoods/:idGET /v2/neighborhoods/{neighborhoodId}
GET /villagesGET /v2/villages
GET /villages/:idGET /v2/villages/{villageId}
GET /townsGET /v2/municipalities?type=town
GET /towns/:idGET /v2/municipalities/{municipalityId}
GET /swaggerGET /v2/openapi.json for the OpenAPI document

v2 also adds nested municipality routes:

RoutePurpose
GET /v2/provinces/{provinceId}/municipalitiesList municipalities in a province
GET /v2/districts/{districtId}/municipalitiesList municipalities in a district
GET /v2/municipalities/{municipalityId}/neighborhoodsList neighborhoods in a municipality

Query Parameter Changes ​

v1 used resource-specific name filters such as name, province, and district.

v2 uses search for name matching on list endpoints:

bash
curl "https://api.turkiyeapi.dev/v2/provinces?search=istanbul"
curl "https://api.turkiyeapi.dev/v2/districts?search=kadikoy"

Parent filters should use IDs or nested routes:

bash
curl "https://api.turkiyeapi.dev/v2/districts?provinceId=34"
curl "https://api.turkiyeapi.dev/v2/provinces/34/districts"

Pagination ​

v1 defaults varied by resource. Provinces and districts could effectively return all records, while neighborhoods, villages, and towns defaulted to 1000.

v2 uses the same pagination model across list endpoints:

Parameterv2 behavior
limitDefault 100, minimum 1, maximum 1000
offsetDefault 0, minimum 0
meta.countNumber of records in this response
meta.totalNumber of records matching the filters

Sorting ​

v1 accepted comma-separated multi-field sorts and attempted to support any field.

v2 accepts one sort value:

ValueMeaning
idID ascending
-idID descending
nameName ascending
-nameName descending
populationPopulation ascending
-populationPopulation descending

Field Selection ​

fields is still comma-separated, but the allowed fields changed with the v2 schemas.

Example:

bash
curl "https://api.turkiyeapi.dev/v2/provinces?fields=id,name,population"

Important field changes:

v1v2
area as a numberarea.value and area.unit inside an area object
altitude as a numberaltitude.value and altitude.unit inside an altitude object
areaCodephoneAreaCodes
nutsNot part of the v2 province schema
mapsNot part of the v2 province schema
Computed fields added automaticallyUse include or nested routes

Includes ​

v1 added related data automatically on some endpoints:

  • Province responses included districts.
  • District detail responses included neighborhoods and villages.
  • Province detail used extend=true for deeper children.

v2 requires related data to be requested explicitly:

bash
curl "https://api.turkiyeapi.dev/v2/provinces/34?include=districts,municipalities"
curl "https://api.turkiyeapi.dev/v2/districts/1103?include=province,neighborhoods"

Supported includes:

EndpointSupported include values
/v2/provinces/{provinceId}districts, municipalities, neighborhoods, villages
/v2/districts/{districtId}province, municipalities, neighborhoods, villages
/v2/municipalities/{municipalityId}province, district, neighborhoods
/v2/neighborhoods/{neighborhoodId}province, district, municipality
/v2/villages/{villageId}province, district

Towns Are Now Municipalities ​

v1 had separate towns endpoints. In v2, towns are represented as municipalities with a type field.

v1 town conceptv2 municipality concept
/towns/v2/municipalities?type=town
/towns/:id/v2/municipalities/{municipalityId}
Town recordMunicipality record with type: "town"

The v2 municipality type can be:

TypeMeaning
province_centerProvince center municipality
district_centerDistrict center municipality
townTown municipality

This is the largest model change from v1. If your v1 integration only used towns, filter v2 municipalities by type=town. If your integration needs all local municipalities, use /v2/municipalities without the type filter.

Postal Codes ​

v1 hid province and district postal codes unless activatePostalCodes=true, and exact endpoints had different truthiness behavior.

v2 removes activatePostalCodes. Postal code data is modeled on neighborhood and village records:

Resourcev2 postal code fields
ProvinceNot part of the province schema
DistrictNot part of the district schema
NeighborhoodpostalCode, postalCodeStatus
VillagepostalCode, postalCodeStatus

postalCode is now a required five-digit string. postalCodeStatus is also required; neighborhoods can use official, derived, or estimated, while villages can use official or estimated.

For official-only usage, filter records where postalCodeStatus is official. derived is used only for neighborhoods whose previous village or settlement postal code exists in official PTT data. estimated values improve searchability but should not be treated as official PTT records.

Neighborhood and village list endpoints also support postalCode, postalCodePrefix, and postalCodeStatus query filters. These filters are available on both top-level collection endpoints and nested neighborhood/village routes.

Dataset and Metadata ​

v2 exposes API and dataset metadata through:

bash
curl "https://api.turkiyeapi.dev/v2/meta"

Current v2 dataset metadata includes:

FieldValue
API version2.0.0
Dataset version2025
Last updated2026-05-21
Provinces81
Districts973
Municipalities1377
Neighborhoods32254
Villages18183

The metadata counts are derived from the datasets loaded by the API, so they match the records served by list endpoints and static dataset downloads.

v2 also adds static dataset downloads:

bash
curl "https://api.turkiyeapi.dev/v2/datasets/provinces.json"
curl "https://api.turkiyeapi.dev/v2/datasets/2025/provinces.json"

Common Migration Examples ​

List Provinces ​

v1:

bash
curl "https://api.turkiyeapi.dev/v1/provinces"

v2:

bash
curl "https://api.turkiyeapi.dev/v2/provinces"

Search by Name ​

v1:

bash
curl "https://api.turkiyeapi.dev/v1/provinces?name=istanbul"

v2:

bash
curl "https://api.turkiyeapi.dev/v2/provinces?search=istanbul"

Get Province with Districts ​

v1 returned province districts by default:

bash
curl "https://api.turkiyeapi.dev/v1/provinces/34"

v2:

bash
curl "https://api.turkiyeapi.dev/v2/provinces/34?include=districts"

Or use the nested collection:

bash
curl "https://api.turkiyeapi.dev/v2/provinces/34/districts"

List Towns ​

v1:

bash
curl "https://api.turkiyeapi.dev/v1/towns"

v2:

bash
curl "https://api.turkiyeapi.dev/v2/municipalities?type=town"

Filter Neighborhoods by District ​

v1:

bash
curl "https://api.turkiyeapi.dev/v1/neighborhoods?districtId=1103"

v2:

bash
curl "https://api.turkiyeapi.dev/v2/neighborhoods?districtId=1103"

Or:

bash
curl "https://api.turkiyeapi.dev/v2/districts/1103/neighborhoods"

Compatibility Notes ​

  • v2 does not support /api/v1 style aliases.
  • v2 list responses are paginated by default with limit=100.
  • v2 does not add nested children by default.
  • v2 rejects unknown fields and unknown includes with 400 Bad Request.
  • v2 rejects contradictory range filters with INVALID_RANGE_FILTER.
  • v2 rejects contradictory parent ID filters with INVALID_HIERARCHY_FILTER.
  • v2 has a stricter OpenAPI 3.1 contract available at /v2/openapi.json.
  • v2 normalizes Turkish text for search; v1 name matching had more compatibility quirks.