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

Single-resource endpoints also include dataset metadata:

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

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 field
ProvinceNot part of the province schema
DistrictNot part of the district schema
NeighborhoodpostalCode
VillagepostalCode

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-10
Provinces81
Districts973
Municipalities1377
Neighborhoods32254
Villages18183

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