Sign inTry it

Find train and bus connections in Austria (ÖBB Scotty).

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

No reports yetRead-only, no login
Site
ÖBB Scotty, Austria
Version
v2 of 2
Runs, last 7 days
No reports yet
sha256
af56b1d791c2…

Run it with your agent

after installing Wield

Ask in plain words. Your agent finds this recipe and calls it:

“Find train and bus connections in Austria (ÖBB Scotty).”

Read-only: it changes nothing on the site. No login needed.

{
  "tool": "run",
  "arguments": {
    "recipe_id": "oebb.at/search-connections",
    "params": {
      "date": "2027-01-14",
      "from": "Wien Hbf",
      "time": "08:00",
      "to": "Salzburg Hbf"
    }
  }
}

Requests

sent from your browser, in order
  1. 1POST/gateRead-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.json bodyno cookies
  2. 2POST/gateRead-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.json bodyno cookiesrepeats, max 1

Parameters

from your agent
arrive_bybooleantime is the latest arrival instead of the earliest departure = false
datestringrequiredTravel date YYYY-MM-DD
fromstringrequiredStation or place, e.g. 'Wien Hbf', 'Salzburg', 'Innsbruck Hbf'. The first match of ÖBB's station search is used
resultsinteger = 5
timestringHH:MM, Austrian local time = "08:00"
tostringrequiredStation or place, e.g. 'Graz Hbf', 'München Hbf'

Returns

to your agent
connectionsobject[]arrival_day_offset, arrival_platform, arrival_realtime, arrival_time, cancelled, changes, departure_date, departure_platform, departure_realtime, departure_time, duration_min, from, legs, to
fromstringThe station ÖBB matched
messagestringThe planner's own message when there are no connections, e.g. 'Datum außerhalb der Fahrplanperiode (04.09.2026 - 11.12.2027)'
tostring

Where it can send

  • fahrplan.oebb.at

Anything else is blocked by the extension.

Reads from your session

Nothing. Your browser attaches the site's cookies by itself.

Used only in requests to the addresses on the left. Never sent to wield.run or your agent.

Checks

a run passes when
  • 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`

Raw recipe

Served as canonical JSON from /api/v1/recipes/oebb.at/search-connections, so its sha256 matches the ETag and the index.

Check it yourself
curl -s https://wield.run/api/v1/recipes/oebb.at/search-connections | shasum -a 256
# af56b1d791c23fe306c13ea69eddabea2798274f25056a6651de5ffebe8026f8
oebb.at/search-connections.json, v2
{
  "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
}