{"description":"ÖBB's journey planner Scotty: connections between two stations or places in Austria and beyond, departing at or arriving by a time, with departure and arrival times (and live times where known), duration, changes, platforms, and each leg's train or bus line and direction. Cancelled connections are marked. Tickets and prices stay in the ÖBB ticket shop.","effect":"read","id":"oebb.at/search-connections","keywords":["öbb","oebb","scotty","fahrplan","zug","bahn","verbindung","railjet","nightjet","westbahn","wien","salzburg","graz","innsbruck","linz","train","austria","timetable","connections","österreich"],"output":{"schema":{"properties":{"connections":{"items":{"properties":{"arrival_day_offset":{"description":"1 = arrives the next day","type":"integer"},"arrival_platform":{"type":["string","null"]},"arrival_realtime":{"type":["string","null"]},"arrival_time":{"type":"string"},"cancelled":{"type":"boolean"},"changes":{"type":"integer"},"departure_date":{"type":"string"},"departure_platform":{"type":["string","null"]},"departure_realtime":{"description":"HH:MM, live forecast if any","type":["string","null"]},"departure_time":{"description":"HH:MM, scheduled","type":"string"},"duration_min":{"type":"integer"},"from":{"type":["string","null"]},"legs":{"items":{"properties":{"arrival_time":{"type":"string"},"departure_time":{"type":"string"},"direction":{"type":["string","null"]},"from":{"type":["string","null"]},"line":{"description":"e.g. 'RJX 860', 'REX 7'","type":["string","null"]},"platform":{"type":["string","null"]},"to":{"type":["string","null"]}},"type":"object"},"type":"array"},"to":{"type":["string","null"]}},"type":"object"},"type":"array"},"from":{"description":"The station ÖBB matched","type":"string"},"message":{"description":"The planner's own message when there are no connections, e.g. 'Datum außerhalb der Fahrplanperiode (04.09.2026 - 11.12.2027)'","type":["string","null"]},"to":{"type":"string"}},"required":["from","to","connections"],"type":"object"},"value":"{from: steps.stations.from_name, to: steps.stations.to_name, message: steps.trips[0].message || `null`, connections: map(&(let $c = @ in {departure_date: join('-', [$c.date[0:4], $c.date[4:6], $c.date[6:8]]), departure_time: (length($c.dep) == `8` && join(':', [$c.dep[2:4], $c.dep[4:6]]) || join(':', [$c.dep[0:2], $c.dep[2:4]])), departure_realtime: $c.dep_rt && (length($c.dep_rt) == `8` && join(':', [$c.dep_rt[2:4], $c.dep_rt[4:6]]) || join(':', [$c.dep_rt[0:2], $c.dep_rt[2:4]])) || `null`, arrival_time: (length($c.arr) == `8` && join(':', [$c.arr[2:4], $c.arr[4:6]]) || join(':', [$c.arr[0:2], $c.arr[2:4]])), arrival_realtime: $c.arr_rt && (length($c.arr_rt) == `8` && join(':', [$c.arr_rt[2:4], $c.arr_rt[4:6]]) || join(':', [$c.arr_rt[0:2], $c.arr_rt[2:4]])) || `null`, arrival_day_offset: (length($c.arr) == `8` && (to_number(join('', ['1', $c.arr[0:2]])) - `100`) || `0`), duration_min: (length($c.dur) == `8` && (to_number(join('', ['1', $c.dur[0:2]])) - `100`) * `1440` + (to_number(join('', ['1', $c.dur[2:4]])) - `100`) * `60` + (to_number(join('', ['1', $c.dur[4:6]])) - `100`)) || ((to_number(join('', ['1', $c.dur[0:2]])) - `100`) * `60` + (to_number(join('', ['1', $c.dur[2:4]])) - `100`)), changes: $c.changes, from: $c.from, to: $c.to, departure_platform: $c.dep_platform, arrival_platform: $c.arr_platform, cancelled: $c.cancelled, legs: map(&(let $s = @ in {line: $s.line, direction: $s.direction, from: $s.from, to: $s.to, departure_time: (length($s.dep) == `8` && join(':', [$s.dep[2:4], $s.dep[4:6]]) || join(':', [$s.dep[0:2], $s.dep[2:4]])), arrival_time: (length($s.arr) == `8` && join(':', [$s.arr[2:4], $s.arr[4:6]]) || join(':', [$s.arr[0:2], $s.arr[2:4]])), platform: $s.platform}), $c.legs)}), steps.trips[0].connections || `[]`)}"},"params":{"additionalProperties":false,"properties":{"arrive_by":{"default":false,"description":"time is the latest arrival instead of the earliest departure","type":"boolean"},"date":{"description":"Travel date YYYY-MM-DD","pattern":"^\\d{4}-\\d{2}-\\d{2}$","type":"string"},"from":{"description":"Station or place, e.g. 'Wien Hbf', 'Salzburg', 'Innsbruck Hbf'. The first match of ÖBB's station search is used","maxLength":80,"minLength":2,"type":"string"},"results":{"default":5,"maximum":10,"minimum":1,"type":"integer"},"time":{"default":"08:00","description":"HH:MM, Austrian local time","pattern":"^([01][0-9]|2[0-3]):[0-5][0-9]$","type":"string"},"to":{"description":"Station or place, e.g. 'Graz Hbf', 'München Hbf'","maxLength":80,"minLength":2,"type":"string"}},"required":["from","to","date"],"type":"object"},"provenance":{"method":"browser-inspect","notes":"The Scotty web app (fahrplan.oebb.at/webapp/) reads config/webapp.config.json, whose urlMgate is https://fahrplan.oebb.at/gate (the older /bin/mgate.exe answers VERSION errors for ver 1.88). js/hafas_webapp_config.js has the client {id OEBB, type WEB, name webapp, l vs_webapp}, ext OEBB.14, hciVersion 1.88 and the public aid 5vHavmuWPWIfetEe that every visitor's browser sends (a public key shipped to all visitors, not a secret; the numeric client version is left out, the gateway rejects the web app's '2.20.1' string). LocMatch (field S, stations and places) and TripSearch (depLocL, arrLocL, outDate YYYYMMDD, outTime HHMMSS, outFrwd, numF) are HAFAS's standard read requests. Times are HHMMSS or DDHHMMSS with a day offset; lines and stations come from res.common.prodL/locL by index. The gateway allows CORS from any origin; robots.txt allows /gate (it disallows /bin/). Answers servers. Zero-padded numbers ('07') are read as 1xx - 100, because to_number rejects leading zeros. v2 (2026-11-07 22:38): read_only_post on 2 steps only; requests and output unchanged.","recorded_at":"2026-11-07T21:52:00+01:00","recorded_by":"C4"},"requires_login":false,"schema":"https://wield.run/schema/recipe/v1.json","site":{"country":"AT","id":"oebb.at","language":"de","name":"ÖBB Scotty","origin":"https://fahrplan.oebb.at","origins":["https://fahrplan.oebb.at"]},"steps":[{"description":"Read-only POST: ÖBB's own journey planner gateway (HAFAS, https://fahrplan.oebb.at/gate, as configured in the Scotty web app's config/webapp.config.json) with two LocMatch requests in one call, one per end. The aid and client are the public values the web app ships to every visitor.","expect":{"assert":"body.err == 'OK' && length(body.svcResL[0].res.match.locL) > `0` && length(body.svcResL[1].res.match.locL) > `0`","content_type":"json","status":[200]},"extract":{"json":{"from_lid":"svcResL[0].res.match.locL[0].lid","from_name":"svcResL[0].res.match.locL[0].name","to_lid":"svcResL[1].res.match.locL[0].lid","to_name":"svcResL[1].res.match.locL[0].name"}},"id":"stations","read_only_post":true,"request":{"body":{"type":"json","value":{"auth":{"aid":"5vHavmuWPWIfetEe","type":"AID"},"client":{"id":"OEBB","l":"vs_webapp","name":"webapp","type":"WEB"},"ext":"OEBB.14","formatted":false,"id":"wield","lang":"deu","svcReqL":[{"meth":"LocMatch","req":{"input":{"field":"S","loc":{"name":"{{params.from}}","type":"S"},"maxLoc":1}}},{"meth":"LocMatch","req":{"input":{"field":"S","loc":{"name":"{{params.to}}","type":"S"},"maxLoc":1}}}],"ver":"1.88"}},"credentials":"omit","headers":{"Accept":"application/json","Content-Type":"application/json"},"method":"POST","url":"/gate"}},{"description":"Read-only POST: TripSearch on the same gateway (outFrwd false = arrive by). The one-item foreach formats date and time the way HAFAS wants them (YYYYMMDD, HHMMSS). Lines and stations are looked up from the response's common lists by index. A planner error (no connection found, date outside the timetable) comes back as an empty list with HAFAS's own message.","expect":{"assert":"body.err == 'OK'","content_type":"json","status":[200]},"extract":{"json":{"connections":"svcResL[0].err == 'OK' && (let $r = svcResL[0].res in let $P = zip(`[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"20\",\"21\",\"22\",\"23\",\"24\",\"25\",\"26\",\"27\",\"28\",\"29\",\"30\",\"31\",\"32\",\"33\",\"34\",\"35\",\"36\",\"37\",\"38\",\"39\",\"40\",\"41\",\"42\",\"43\",\"44\",\"45\",\"46\",\"47\",\"48\",\"49\",\"50\",\"51\",\"52\",\"53\",\"54\",\"55\",\"56\",\"57\",\"58\",\"59\",\"60\",\"61\",\"62\",\"63\",\"64\",\"65\",\"66\",\"67\",\"68\",\"69\",\"70\",\"71\",\"72\",\"73\",\"74\",\"75\",\"76\",\"77\",\"78\",\"79\",\"80\",\"81\",\"82\",\"83\",\"84\",\"85\",\"86\",\"87\",\"88\",\"89\",\"90\",\"91\",\"92\",\"93\",\"94\",\"95\",\"96\",\"97\",\"98\",\"99\",\"100\",\"101\",\"102\",\"103\",\"104\",\"105\",\"106\",\"107\",\"108\",\"109\",\"110\",\"111\",\"112\",\"113\",\"114\",\"115\",\"116\",\"117\",\"118\",\"119\",\"120\",\"121\",\"122\",\"123\",\"124\",\"125\",\"126\",\"127\",\"128\",\"129\",\"130\",\"131\",\"132\",\"133\",\"134\",\"135\",\"136\",\"137\",\"138\",\"139\",\"140\",\"141\",\"142\",\"143\",\"144\",\"145\",\"146\",\"147\",\"148\",\"149\"]`, $r.common.prodL || `[]`), $L = zip(`[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"20\",\"21\",\"22\",\"23\",\"24\",\"25\",\"26\",\"27\",\"28\",\"29\",\"30\",\"31\",\"32\",\"33\",\"34\",\"35\",\"36\",\"37\",\"38\",\"39\",\"40\",\"41\",\"42\",\"43\",\"44\",\"45\",\"46\",\"47\",\"48\",\"49\",\"50\",\"51\",\"52\",\"53\",\"54\",\"55\",\"56\",\"57\",\"58\",\"59\",\"60\",\"61\",\"62\",\"63\",\"64\",\"65\",\"66\",\"67\",\"68\",\"69\",\"70\",\"71\",\"72\",\"73\",\"74\",\"75\",\"76\",\"77\",\"78\",\"79\",\"80\",\"81\",\"82\",\"83\",\"84\",\"85\",\"86\",\"87\",\"88\",\"89\",\"90\",\"91\",\"92\",\"93\",\"94\",\"95\",\"96\",\"97\",\"98\",\"99\",\"100\",\"101\",\"102\",\"103\",\"104\",\"105\",\"106\",\"107\",\"108\",\"109\",\"110\",\"111\",\"112\",\"113\",\"114\",\"115\",\"116\",\"117\",\"118\",\"119\",\"120\",\"121\",\"122\",\"123\",\"124\",\"125\",\"126\",\"127\",\"128\",\"129\",\"130\",\"131\",\"132\",\"133\",\"134\",\"135\",\"136\",\"137\",\"138\",\"139\",\"140\",\"141\",\"142\",\"143\",\"144\",\"145\",\"146\",\"147\",\"148\",\"149\"]`, $r.common.locL || `[]`) in map(&(let $c = @ in {date: $c.date, dep: $c.dep.dTimeS, dep_rt: $c.dep.dTimeR || `null`, arr: $c.arr.aTimeS, arr_rt: $c.arr.aTimeR || `null`, dep_platform: $c.dep.dPltfR.txt || $c.dep.dPltfS.txt || `null`, arr_platform: $c.arr.aPltfR.txt || $c.arr.aPltfS.txt || `null`, dur: $c.dur, changes: $c.chg, cancelled: $c.dep.dCncl == `true` || $c.arr.aCncl == `true`, from: ($L[?[0] == to_string($c.dep.locX)] | [0][1].name), to: ($L[?[0] == to_string($c.arr.locX)] | [0][1].name), legs: map(&(let $s = @ in {line: ($P[?[0] == to_string($s.jny.prodX)] | [0][1].name) || `null`, direction: $s.jny.dirTxt || `null`, from: ($L[?[0] == to_string($s.dep.locX)] | [0][1].name), to: ($L[?[0] == to_string($s.arr.locX)] | [0][1].name), dep: $s.dep.dTimeS, arr: $s.arr.aTimeS, platform: $s.dep.dPltfR.txt || $s.dep.dPltfS.txt || `null`}), $c.secL[?type == 'JNY'] || `[]`)}), $r.outConL || `[]`)) || `[]`","message":"svcResL[0].err != 'OK' && (svcResL[0].errTxtOut || svcResL[0].errTxt || svcResL[0].err) || `null`"}},"foreach":{"as":"f","in":"[{d: join('', split(params.date, '-')), t: join('', [replace(params.time || '08:00', ':', ''), '00']), fwd: params.arrive_by != `true`}]","max":1},"id":"trips","read_only_post":true,"request":{"body":{"type":"json","value":{"auth":{"aid":"5vHavmuWPWIfetEe","type":"AID"},"client":{"id":"OEBB","l":"vs_webapp","name":"webapp","type":"WEB"},"ext":"OEBB.14","formatted":false,"id":"wield","lang":"deu","svcReqL":[{"meth":"TripSearch","req":{"arrLocL":[{"lid":"{{steps.stations.to_lid}}","type":"S"}],"depLocL":[{"lid":"{{steps.stations.from_lid}}","type":"S"}],"getPasslist":false,"getPolyline":false,"numF":"{{params.results}}","outDate":"{{f.d}}","outFrwd":"{{f.fwd}}","outTime":"{{f.t}}"}}],"ver":"1.88"}},"credentials":"omit","headers":{"Accept":"application/json","Content-Type":"application/json"},"method":"POST","url":"/gate"}}],"task":"Find train and bus connections in Austria (ÖBB Scotty)","tos":{"notes":"Terms not reviewed yet for automated access. One person's search: 2 read-only requests to the journey planner's own gateway, the same ones the Scotty web app makes. No tickets: buying stays in the ÖBB shop.","reviewed":false},"verify":{"checks":["length(output.connections) >= `3`","starts_with(output.from, 'Wien')","starts_with(output.to, 'Salzburg')","length(output.connections[?departure_date == '2027-01-14' && duration_min > `100` && duration_min < `400`]) == length(output.connections)","length(output.connections[?length(legs) > `0` && legs[0].line != `null`]) == length(output.connections)","to_number(join('', ['1', replace(output.connections[0].departure_time, ':', '')])) >= `10730`","output.message == `null`"],"example_params":{"date":"2027-01-14","from":"Wien Hbf","time":"08:00","to":"Salzburg Hbf"},"max_ms":5000},"version":2}