Skip to content

Search box autocomplete

GET /v1/places/autocomplete?q= is the endpoint behind a search box: one call per keystroke, answered from a prefix index built into the data version. It needs an API key with the route scope; from a browser, use a publishable key.

Terminal window
curl -H "Authorization: Bearer $RAIL_API_KEY" \
"https://api.routing.gleisklar.de/v1/places/autocomplete?q=kiel%20h&focus=54.32,10.13&limit=5"
{
"results": [
{ "id": "node/4711", "name": "Kiel Hauptbahnhof", "kind": "station", "ds100": "AK", "uopid": "DE000AK",
"uic": "8000199", "country": "DE", "lat": 54.3135, "lon": 10.131, "distance_m": 750 },
{ "id": "node/4712", "name": "Kiel-Hassee CAU", "kind": "halt", "country": "DE", "lat": 54.3301,
"lon": 10.1102, "distance_m": 2211 }
],
"next_cursor": null
}

(Illustrative values.) id is the place’s ID as in /v1/places; kind is station, halt or operational_point.

  • Every word of q must start a word of the name, in any order: kiel h, frankf main, main frank and st peter all work. Each keystroke narrows the list.
  • Codes: q may also be the start of a DS100 code, a RINF operational point ID or a UIC code (AK, DE000A, 80001). Written in capitals, an exact code ranks first.
  • Umlauts and diacritics: lüb, lueb and lub all find Lübeck. Diacritics you type are meant: Fürth doesn’t suggest Furth im Wald.
  • Abbreviations work both ways: kiel hbf and kiel hauptb find “Kiel Hauptbahnhof” and “Kiel Hbf” alike (also Bf, Rbf, Gbf, St.).
  • No typos, no substrings. That keeps suggestions predictable while typing. When nothing starts with the text, fall back to GET /v1/places?q=, which corrects typos.

Suggestions are ordered by how well they match (the whole name, then names starting with the query, then names with the words further on), then stations before halts before operational points, more railway lines nearby and shorter names.

Two optional parameters move nearby places up without filtering anything out:

ParameterEffect
focus=lat,lonCloser places rank higher, e.g. the map centre. Each result then has distance_m.
focus_scale_kmDistance from focus at which its bonus halves (km, default 50). Smaller keeps suggestions local.
bbox=minLon,minLat,maxLon,maxLatPlaces inside rank higher, e.g. the visible map.

This is the difference from GET /v1/places, where bbox is a filter: in autocomplete a good match far away still shows up, so typing “Berlin” on a map of Munich still suggests Berlin. To restrict results, use kinds (station, halt, operational_point, comma-separated) and country (ISO 3166-1 alpha-2; places of unknown country are left out), which filter as in /v1/places. limit is 1 to 20, default 8; more pages come with cursor (see pagination).

Suggestions are deliberately lean: ID, name, kind, codes, country and coordinates. Use the code in a route ({"station": "AK"}), or fetch the whole place with GET /v1/places/{id} (its codes, other names, lines nearby, its OSM stations and RINF operational points, and its platforms). GET /v1/places/lookup?code= finds places by an exact code.

Autocomplete is billed at 0.2 requests per call: 5 calls = 1 request, so a user typing a station name letter by letter costs a request or two. Usage shows the calls, and the billable requests next to them; the included requests, the monthly cap and statements count billable requests (the month’s total rounded once). Rate limits count every call.

import { createClient } from "@quebi/rail-api"
const api = createClient({ apiKey: "rr_pk_…" })
let pending: AbortController | undefined
let timer: ReturnType<typeof setTimeout>
input.addEventListener("input", () => {
clearTimeout(timer)
timer = setTimeout(async () => {
pending?.abort() // drop the previous keystroke's request
const ctrl = new AbortController()
pending = ctrl
try {
const { results } = await api.autocomplete(input.value, {
focus: map.getCenter(), // MapLibre's { lat, lng } works
kinds: ["station", "halt"],
signal: ctrl.signal,
})
render(results)
} catch (err) {
if (!ctrl.signal.aborted) throw err // an aborted call rejects with NetworkError
}
}, 100)
})

A short debounce (100–150 ms) skips most intermediate keystrokes without feeling slow.