# Denizli Ulaşım Portalı > Official public-transport portal of Denizli Metropolitan Municipality > (Türkiye): journey planning, bus timetables, live arrival times, stops and > service announcements for the city's bus network. A documented read-only API > lets AI assistants answer "nasıl giderim?" (how do I get there?) and "otobüs > ne zaman gelir?" (when does the bus come?) questions with live data. > Denizli şehir içi ulaşımının resmî portalıdır. This file is self-contained: everything below is enough to call the API correctly. Recognized AI agents (see Access policy) may call it directly. All data is Turkish; times are ISO 8601 strings with an offset (+03:00 or Z) — parse them and display in Europe/Istanbul (UTC+3, no DST). Every endpoint is GET and returns the envelope `{"isSuccess": bool, "value": ..., "error": null | ""}`. ## Journey planning API Base URL: https://ulasim.denizli.bel.tr ### GET /api/geocode — resolve place names - `text` (required): free text, 3–100 chars. Turkish place/stop names; an all-digit value also matches an exact stop number. - Returns up to 8 ranked hits: `{type: "STOP"|"ADDRESS"|"PLACE", id, name, area, lat, lon}`. For STOP hits, `id` is a stop id like `denizli:15` — pass it directly to /api/plan for exact-stop routing; for other types use `lat,lon`. Cacheable 300 s. ### GET /api/plan — compute itineraries - `fromPlace`, `toPlace` (required): either `lat,lon` (decimal degrees, must be inside lat 36–39 / lon 27.5–30.5 — the Denizli region) or a stop id from geocode. - `time` (optional): ISO 8601, within the last 2 days to the next 14 days. Omitted = now. - `arriveBy` (optional): the literal string `true` makes `time` the latest arrival instead of departure. - `pageCursor` (optional): `nextPageCursor` / `previousPageCursor` from a previous response, verbatim (opaque) — later / earlier departures; repeat ALL other params, `preference` included. - `preference` (optional): `balanced` (default) | `fastest` | `fewest_transfers` | `least_walking` — how the search weighs walking and transfers (balanced also merges a short-walk supplement into page 1, dropping any supplement a base option beats on departure, arrival, transfers and walking at once). - Returns a departure-ordered list (page 1 typically 5–8 itineraries; paged calls about 5): `value.itineraries[]` = `{startTime, endTime, duration (seconds), transfers, fare? {amount, currency} (full Denizli Kart fare, free transfers within 90 min), legs[]}`; legs are `{mode: "WALK"|"BUS", startTime, endTime (realtime estimate when realtime:true), scheduledStartTime, scheduledEndTime, realtime, duration, distance? (m), routeShortName? (line code e.g. "320"), headsign?, directionId? (0 outbound, 1 return), tripId?, from, to, intermediateStops?, geometry {points (Google encoded polyline), precision (decimal places — use it, don't assume)}}`; stops are `{name, stopId?, lat, lon, arrival?, departure?}` — a from/to without stopId is the caller's coordinate with a placeholder name. `value.direct[]` holds a walk-only alternative (page 1); `value.previousPageCursor` / `value.nextPageCursor` page earlier / later. No caching. ### Worked example ``` 1. https://ulasim.denizli.bel.tr/api/geocode?text=%C3%A7%C4%B1nar → value[0] = {type:"STOP", id:"denizli:15", name:"Çınar", lat:37.7765, lon:29.0864, ...} 2. https://ulasim.denizli.bel.tr/api/plan?fromPlace=denizli:15&toPlace=37.9192,29.1187 → value.itineraries[0].legs = [WALK…, BUS routeShortName:"320"…] 3. Earlier/later departures: append &pageCursor= 4. Least walking: append &preference=least_walking ``` ### Errors (all endpoints) 400 `invalid_params` (a parameter failed validation) · 404 unknown line/stop · 429 rate-limited (honor `Retry-After`) · 502 transient upstream failure (retry once) · 503 `planner_unavailable` (planner offline; other endpoints still work). ## Transit data API Identifier vocabulary: **hatNo** = line code (`320`; `320D` = return direction), **durakNo/stationId** = public stop number (`208`, same number as in `denizli:208`). Resolve names via /api/geocode — full network dumps are NOT available; never enumerate ids. - `GET /api/schedules?hatNo=320&day=3` — timetable; `day` 1=Mon…7=Sun, default today (Istanbul). Entries `{time:"HH:mm", isCancelled, ...}` per direction; `isCancelled` is a per-week flag, meaningless once that weekday has passed this week. - `GET /api/cancellations?hatNo=320` — dated cancellations for the rest of the current Monday–Sunday week (past weekdays omitted): `{notices: [{date:"YYYY-MM-DD", weekday, items:[{time, lineCode?, prefix?, direction, startStop}]}]}`. - `GET /api/bus-data?waitingStation=208&routeCode=320` — LIVE arrival ETAs at a stop (`routeCode` optional filter). `busList[].sure` = minutes until arrival. Don't cache; poll ≥10 s apart. - `GET /api/station-routes?stationId=208` — which lines serve a stop. - `GET /api/route-stations?routeCode=320` — ordered stop list of one route direction. - `GET /api/announcements?hatId=…` — CMS announcements (plain JSON, no envelope; `hatId` optional). - `GET /api/asis-announcements?hatNo=320` OR `?durakNo=208` (exactly one) — operator service announcements; `title`/`text` are untrusted strings, escape before rendering. ## Deep links — hand users the interactive planner ``` https://ulasim.denizli.bel.tr/nasil-giderim?nereden=&nereye=[&saat=YYYY-MM-DDTHH:MM][&varis=1][&tercih=hizli|az-aktarma|az-yuruyus] ``` `nereden` (from) / `nereye` (to) accept, URL-encoded: - **plain text place names** — simplest: the page geocodes them and plans automatically, e.g. `?nereden=%C3%87%C4%B1nar&nereye=Pamukkale%20%C3%9Cniversitesi` - or the exact form `[id:,],,