# Migrating to Geocoder v3

Geocoder v3 is a new version of Entur's geocoding API with cleaner parameter names, structured response objects, and unambiguous IDs. This guide maps every v2 parameter and response field to its v3 equivalent.

See also the [Geocoder v3 documentation](/docs/open-services/geocoder).

## Parameter changes

### Common (autocomplete + reverse)

| v2                                                        | v3                    | Notes                                                                                                                                                                                                                                                                          |
|-----------------------------------------------------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `size`                                                    | `limit`               |                                                                                                                                                                                                                                                                                |
| `lang`                                                    | `lang`                | unchanged; also accepted on `/place`                                                                                                                                                                                                                                           |
| `layers`                                                  | `layers`              | see [layers](#layers)                                                                                                                                                                                                                                                          |
| `sources`                                                 | `sources`             | see [sources](#sources)                                                                                                                                                                                                                                                        |
| `boundary.country`                                        | `countries`           | comma-separated                                                                                                                                                                                                                                                                |
| `boundary.county_ids`                                     | `counties`            | comma-separated                                                                                                                                                                                                                                                                |
| `boundary.locality_ids`                                   | `localities`          | comma-separated                                                                                                                                                                                                                                                                |
| `tariff_zone_ids`                                         | _(removed)_           | use `fareZones` instead                                                                                                                                                                                                                                                        |
| _(new in v3)_                                            | `fareZones`           | comma-separated; refs must be FareZone-shaped (`AUTH:FareZone:ID`).                                                                                                                                                                                                            |
| `tariff_zone_authorities`                                 | _(removed)_           | use `fareZoneAuthorities` instead                                                                                                                                                                                                                                              |
| `fare_zone_authorities`                                   | `fareZoneAuthorities` | comma-separated                                                                                                                                                                                                                                                                |
| `multiModal`                                              | `multimodal`          | values: `parent` (default), `child`, `all`; see [multimodal](#multimodal)                                                                                                                                                                                                      |
| `categories` (v2 reverse only) | `stopPlaceTypes` | NeTEx stop place types; see below |

`stopPlaceTypes` takes NeTEx stop place types (`railStation`, `airport`, ...). On its own it restricts results to stop places of those types and excludes other layers - same semantics as v2 `categories`. The v2 layer-like category values (`street`, `vegadresse`, `GroupOfStopPlaces`, `poi`) map to [layers](#layers) instead.

Combine `stopPlaceTypes` with an explicit `layers` filter to mix mode-filtered stop places with whole layers in one request. The two compose as a union: the type filter constrains only the `stopPlace` layer, while any other requested layer is returned additively. To get rail stations together with groups of stop places (v2's `categories` OR behaviour, in a single request):

```
?q=oslo&layers=stopPlace,groupOfStopPlaces&stopPlaceTypes=railStation
```

If `layers` is given but does not include `stopPlace`, the type filter has no stopPlace layer to constrain and is ignored.

### Autocomplete (`/v3/autocomplete`)

| v2                | v3          | Notes                                                                                                              |
|-------------------|-------------|--------------------------------------------------------------------------------------------------------------------|
| `text`            | `q`         | optional in v3: omitting `q` with at least one filter lists all matching places                                    |
| `focus.point.lat` | `lat`       | focus point latitude                                                                                               |
| `focus.point.lon` | `lon`       | focus point longitude                                                                                              |
| `focus.scale`     | `radius`    | focus radius in km (default 50, decimals accepted); see [default differs from v2](#default-radius-differs-from-v2) |
| `focus.weight`    | `weight`    | 0-1 (default 0.5); see [weight semantics](#weight-semantics-changed)                                               |
| `focus.function`  | _(removed)_ | v2's `focus.function` (linear, exp, etc.) is removed; v3 behaves roughly like v2's `exp`                           |
| _(new in v3)_    | `bbox`      | restrict results to `minLon,minLat,maxLon,maxLat` - a hard filter, unlike the focus point's soft bias              |

### Reverse (`/v3/reverse`)

| v2                       | v3             | Notes                                                                               |
|--------------------------|----------------|-------------------------------------------------------------------------------------|
| `point.lat`              | `lat`          | query point latitude                                                                |
| `point.lon`              | `lon`          | query point longitude                                                               |
| `boundary.circle.radius` | `radius`       | km in both v2 and v3 (decimals accepted)                                            |
| _(new in v3)_           | `distanceSort` | sort by distance from the query point (default `true`) or by relevance when `false` |

### Place (`/v3/place`)

| v2 | v3 | Notes |
|----|----|-------|
| `ids` | `ids` | comma-separated; ID shape differs between v2/v3 (see [ID format](#id-format)) |

### `layers`

The v3 layer values are the same indexed data v2 already returned, just labelled precisely - in v2 `venue` held NSR stop places and everything else fell under `address`:

| v2 `layers` | v3 `layers`         | data origin                              |
|-------------|---------------------|------------------------------------------|
| `address`   | `address`           | matrikkel addresses                      |
| `address`   | `street`            | matrikkel streets                        |
| `venue`     | `stopPlace`         | NSR stop places                          |
| `address`   | `groupOfStopPlaces` | NSR group of stop places                 |
| `address`   | `poi`               | OSM POIs; `custom-poi` (new in v3)       |
| `address`   | `place`             | kartverket-stedsnavn (cities, districts) |

**Default behaviour (no `layers` filter):** same result set as v2 - only the `layer` value changed. All layers are eligible, with one exclusion: addresses are hidden from autocomplete unless the query text contains a digit or `sources` includes `kartverket-matrikkelenadresse`. Reverse hides addresses unless the caller opts in via `sources=kartverket-matrikkelenadresse` or `layers=address`.

### `sources`

| v2                | v3                              |
|-------------------|---------------------------------|
| `openstreetmap`   | `openstreetmap`                 |
| `openaddresses`   | `kartverket-matrikkelenadresse` |
| _(new in v3)_ | `kartverket-stedsnavn`          |
| _(new in v3)_ | `nsr`                           |
| _(new in v3)_ | `custom-poi`                    |

### Weight semantics (changed)

`weight` is renamed straight across from `focus.weight`, but the math is different. In v2 it was an open-ended scalar fed through a curve (default ~15). In v3 it is a linear blend between importance ranking (0) and pure location preference (1) - avoid the extremes unless that is what you want:

- `weight=0` - no focus bias, results ranked purely on text relevance and importance.
- `weight=1` - pure location preference: importance is ignored entirely, only proximity counts.
- `weight=0.5` (default) - balanced: importance and proximity contribute roughly equally to ranking.

The v3 default tilts toward importance more than v2 does, so far-away major cities can still win against near-focus same-prefix streets (e.g. "Bergen" from Oslo still returns Bergen, not Bergensgata). If you tuned `focus.weight` in v2 do not just copy the number across.

### Default radius differs from v2

Do not copy your v2 `focus.scale` value into v3 `radius`. v2 saturates large values (anything past ~300 km behaves the same, so the v2 default of 2500 was effectively ~120 km); v3 treats the number as a literal focus radius, so `radius=2500` disables the bias entirely. If you used v2's default or any large `focus.scale`, use `radius` in the 50-150 km range.

### Focus parameters are a bundle

`lat`, `lon`, `radius`, and `weight` on `/v3/autocomplete` belong together. Sending any of them without both `lat` and `lon` is a 400 error.

### `multimodal`

Applies to `/v3/autocomplete` and `/v3/reverse`.

An NSR multimodal stop groups several stop places (e.g. a bus terminal + train station hub) under a parent. Most NSR stop places are *monomodal* - standalone, not part of any such group - and those always appear in results regardless of this parameter. `multimodal` only decides whether multimodal parents, children, or both come through alongside them.

- `parent` (default) - monomodal stops + multimodal parents; multimodal children hidden.
- `child` - monomodal stops + multimodal children; multimodal parents hidden.
- `all` - monomodal stops + multimodal parents + multimodal children.

The role of each returned stop is reported per feature in the [`stopPlaceRole`](#property-mapping-properties) response field (`parent` / `child` / `standalone`), so you no longer need to infer it from `source` as in v2.

## ID format

v3 uses canonical, fully-qualified IDs. v2 uses legacy/abbreviated forms for backwards compatibility.

| Entity                               | v2 ID                             | v3 ID                                                                                     |
|--------------------------------------|-----------------------------------|-------------------------------------------------------------------------------------------|
| Stop place                           | `NSR:StopPlace:N`                 | `NSR:StopPlace:N`                                                                         |
| Group of stop places                 | `NSR:GroupOfStopPlaces:N`         | `NSR:GroupOfStopPlaces:N`                                                                 |
| OSM POI                              | `OSM:TopographicPlace:N`          | `OSM:TopographicPlace:N`                                                                  |
| Matrikkel address                    | bare numeric (e.g. `225678815`)   | `KVE:PostalAddress:N`                                                                     |
| Matrikkel street                     | `KVE:TopographicPlace:KOMNR-NAME` | `KVE:TopographicPlace:KOMNR-NAME` (unchanged)                                             |
| Stedsnavn place                      | bare numeric (e.g. `434810`)      | `KVE:PlaceName:N`                                                                         |
| Boundary filter (kommune/fylke code) | bare numeric (e.g. `0301`, `03`)  | `KVE:TopographicPlace:N`                                                                  |
| Grunnkrets (in `address.boroughId`)  | `whosonfirst:borough:N`           | `KVE:Borough:N` (N = 8-digit grunnkretsnummer: KOMNR + 4-digit sequence, e.g. `34200205`) |

In v2 **bare numerics on the wire are ambiguous** - they can be stedsnavn places or postal addresses. v3 removes the ambiguity by giving each entity a fully-qualified namespace.

## Response format

Both v2 and v3 return a GeoJSON `FeatureCollection`. High-level shape changes:

- Feature `properties` use structured objects (`names`, `address`, `transportModes`) instead of v2's flat fields.
- A `metadata` object at the top level replaces v2's `geocoding` block. It carries the echoed query, `resultCount`, and an ISO 8601 `timestamp`. Errors come back as HTTP 4xx with an `application/problem+json` body rather than under `geocoding.errors`.
- `/v3/reverse` responses include `distance` on each feature, in kilometres with 3-decimal precision (same units as v2).
- Features with a real extent (streets, groups of stop places) carry a GeoJSON `bbox` (`[minLon, minLat, maxLon, maxLat]`); point features omit it.

The per-property table below is authoritative; see [layers](#layers) and [sources](#sources) for value-set changes on `layer` and `source`.

### Property mapping (`properties.*`)

Field-by-field migration. Anything not listed is unchanged; anything marked `_(removed)_` has no v3 equivalent.

| v2                          | v3                              | notes                                                                                                                                                                                                                                                                                                              |
|-----------------------------|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id`                        | `id`                            | ID shape differs - see [ID format](#id-format)                                                                                                                                                                                                                                                                     |
| `name`                      | `names.default`                 |                                                                                                                                                                                                                                                                                                                    |
| `label`                     | `names.display`                 | formatted display name with locality context                                                                                                                                                                                                                                                                       |
| `popular_name`              | `names.label`                   | colloquial name; rarely populated today                                                                                                                                                                                                                                                                            |
| `housenumber`               | `address.houseNumber`           |                                                                                                                                                                                                                                                                                                                    |
| `street`                    | `address.streetName`            |                                                                                                                                                                                                                                                                                                                    |
| `postalcode`                | `address.postalCode`            |                                                                                                                                                                                                                                                                                                                    |
| `country_a` / `countrycode` | `address.countryCode`           | v2 was ISO 3166-1 alpha-3 (`"NOR"`) on `country_a`; `countrycode` was declared but never populated. v3 collapses both into `countryCode` as alpha-2 lowercase (`"no"`). Case-sensitive client comparisons will break silently.                                                                                     |
| `county`                    | `address.county`                |                                                                                                                                                                                                                                                                                                                    |
| `county_gid`                | `address.countyId`              | v2 prefixed `whosonfirst:county:`; v3 returns the raw indexed ID (e.g. `KVE:TopographicPlace:03`)                                                                                                                                                                                                                  |
| `locality`                  | `address.locality`              |                                                                                                                                                                                                                                                                                                                    |
| `locality_gid`              | `address.localityId`            | v2 prefixed `whosonfirst:locality:`; v3 returns the raw indexed ID                                                                                                                                                                                                                                                 |
| `borough`                   | `address.borough`               |                                                                                                                                                                                                                                                                                                                    |
| `borough_gid`               | `address.boroughId`             | v2 returns `whosonfirst:borough:N`; v3 returns `KVE:Borough:N` where N is the grunnkretsnummer (Norwegian sub-municipal statistical unit)                                                                                                                                                                          |
| `city`                      | `address.locality`              | v2 carried both `city` and `locality` for the same value; v3 keeps only `locality`                                                                                                                                                                                                                                 |
| `layer`                     | `layer`                         | new enum values; see [layers](#layers)                                                                                                                                                                                                                                                                             |
| `source`                    | `source`                        | NSR stops normalised: v2 returned `"openstreetmap"` (multimodal parent), `"geonames"` (child), or `"whosonfirst"` (standalone); v3 always returns `"nsr"`. The parent/child/standalone role v2 encoded here is now exposed explicitly in the new `stopPlaceRole` field (next row). `openaddresses` -> `kartverket-matrikkelenadresse`. See [sources](#sources).                                                                            |
| _(new in v3)_               | `stopPlaceRole`                 | Stop place hierarchy role: `parent` / `child` / `standalone`. Replaces inferring it from the v2 `source` (openstreetmap/geonames/whosonfirst). Set on `stopPlace` features; `parent`/`child` both mean multimodal. |
| `category`                  | `stopPlaceTypes` + `categories` | NeTEx stop place types (e.g. `railStation`) split out from OSM tags (e.g. `restaurant`)                                                                                                                                                                                                                            |
| `mode`                      | `transportModes`                | array of `{ mode, subMode }` objects instead of pair-encoded JSON                                                                                                                                                                                                                                                  |
| `tariff_zones` | `fareZones` | **not a straight rename**; see below |
| `distance`                  | `distance`                      | reverse only; kilometres with 3-decimal precision in both v2 and v3                                                                                                                                                                                                                                                |
| `description`               | `description`                   | per-language description. v2 shape: `[{lang: text}, ...]` (array of singleton maps). v3 shape: `{lang: text, ...}` (flat object keyed by ISO 639-2 alpha-3 code)                                                                                                                                                   |
| `accuracy`                  | _(removed)_                     | Pelias-ism (`point`/`centroid`); not meaningful here                                                                                                                                                                                                                                                               |
| `gid`                       | _(removed)_                     | the Pelias `source:layer:id` triple; use `id`                                                                                                                                                                                                                                                                      |
| `source_id`                 | _(removed)_                     | duplicate of `id`                                                                                                                                                                                                                                                                                                  |
| `type` (in properties)      | _(removed)_                     | always `"Feature"` on the feature itself; the duplicate inside `properties` is gone                                                                                                                                                                                                                                |

`fareZones` is not a straight rename of `tariff_zones`. v3 `fareZones` carries only `AUTH:FareZone:ID`-shaped refs, while v2's `tariff_zones` merged both `TariffZone` and `FareZone` refs for backwards compatibility. `TariffZone` refs are no longer surfaced in v3 responses, and `TariffZone`-shaped values sent as filter input do not match anything.

### Autocomplete example

**v2 request:**

```
GET /v2/autocomplete?text=Nationaltheatret&lang=no&size=1&layers=venue
```

**v2 response:**

```json
{
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "geometry": { "type": "Point", "coordinates": [10.7335, 59.9144] },
    "properties": {
      "id": "NSR:StopPlace:337",
      "layer": "venue",
      "source": "openstreetmap",
      "name": "Nationaltheatret",
      "label": "Nationaltheatret, Oslo",
      "category": ["railStation", "metroStation"]
    }
  }]
}
```

**v3 request:**

```
GET /v3/autocomplete?q=Nationaltheatret&lang=no&limit=1&layers=stopPlace
```

**v3 response:**

```json
{
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "geometry": { "type": "Point", "coordinates": [10.73350, 59.91440] },
    "properties": {
      "id": "NSR:StopPlace:337",
      "names": { "default": "Nationaltheatret", "display": "Nationaltheatret, Oslo" },
      "layer": "stopPlace",
      "address": { "locality": "Oslo", "county": "Oslo", "countryCode": "no" },
      "transportModes": [{ "mode": "rail" }, { "mode": "metro", "subMode": "metro" }],
      "stopPlaceTypes": ["railStation", "metroStation"],
      "source": "nsr"
    }
  }],
  "bbox": [10.73350, 59.91440, 10.73350, 59.91440],
  "metadata": {
    "query": { "text": "Nationaltheatret", "limit": 1, "lang": "no", "filters": { "layers": ["stopPlace"] } },
    "resultCount": 1,
    "timestamp": "2025-03-04T11:00:00Z"
  }
}
```

### Reverse geocode example

**v2 request:**

```
GET /v2/reverse?point.lat=59.9110&point.lon=10.7522&boundary.circle.radius=0.5&size=1
```

**v3 request:**

```
GET /v3/reverse?lat=59.9110&lon=10.7522&radius=0.5&limit=1
```

**v3 response (abbreviated):**

```json
{
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "geometry": { "type": "Point", "coordinates": [10.75220, 59.91100] },
    "properties": {
      "id": "NSR:StopPlace:337",
      "names": { "default": "Nationaltheatret", "display": "Nationaltheatret, Oslo" },
      "layer": "stopPlace",
      "source": "nsr",
      "distance": 0.012
    }
  }],
  "metadata": {
    "query": { "lat": 59.9110, "lon": 10.7522, "limit": 1, "lang": "no" },
    "resultCount": 1,
    "timestamp": "2025-03-04T11:00:00Z"
  }
}
```

