# forsenad.nu > Punctuality statistics for Swedish trains. For a train arriving at a station, forsenad.nu tells how often it has arrived on time, late, very late or been cancelled, based on millions of arrivals at Sweden's larger stations since January 2025 (Trafikverket's open data). Its journey planner shows these statistics for every leg of a journey, so a traveller can pick the departure most likely to arrive on time, before booking. forsenad.nu ("försenad" is Swedish for "delayed") is a free hobby project. The website is in Swedish and needs JavaScript; agents should use the JSON API below rather than the pages. ## Terms of use - **Free and open:** no key, no sign-up. Use it in agents, apps and research. - **Credit is required:** whenever you show or repeat numbers from forsenad.nu, name the source, e.g. "Source: forsenad.nu" with a link to https://www.forsenad.nu/. - **Be gentle with the journey planner:** each new search calls an upstream route planner (Resrobot) that has a rate limit, shared by everyone. Send at most one new search every few seconds. Going faster gets HTTP 429 (Too Many Requests) with a `Retry-After` header: wait that many seconds before retrying. Repeating the same search within an hour is answered from a cache. The statistics endpoints have no such limit. - **Contact:** Bluesky @forsenad.nu ## What the numbers mean - Every figure is about **arrivals**: a train arriving at a station, the leg's destination in the journey planner. A train counts once at each station it arrives at. - **ON_TIME:** at most 5 minutes late. **LESSEQ_THAN_30M:** more than 5 and at most 30 minutes late. **MORE_THAN_30M:** more than 30 minutes late. **CANCELLED:** cancelled. The four add up to 100 %. - It's history, not a forecast: "on time 72 % of the last 90 days" is how it went, not a promise. - Only Sweden's larger stations are covered (see the list below). For a smaller station, use the next larger station along the line. - Periods: the journey planner gives the last 30, 90, 180 and 360 days. Fewer arrivals (`ankomster`) means a less certain figure; 3 months is a good default. ## API Base URL `https://www.forsenad.nu`, JSON, `GET` only. Full description: [OpenAPI](https://www.forsenad.nu/openapi.json). ### Journey planner: journeys with punctuality per leg `GET /api/v2/tidsfilter/reseplanerare?source=Cst&dest=G&datetime=2026-11-03T08:00` - `source`, `dest`: station codes from the list below (Trafikverket signatures, case-sensitive, e.g. `Cst` = Stockholm C). Not names. - `datetime`: earliest departure, Swedish local time, `YYYY-MM-DDTHH:MM`, at most a year before or after today. - Returns about five journeys departing from `datetime` on. An empty list means no train connection. ```json [ { "avgangstid": "08:14", "ankomsttid": "13:30", "restid": "5h 16m", "byten": "", "origin": { "namn": "Stockholm C", "forkortning": "CST", "kod": "740000001" }, "destination": { "namn": "Göteborg C", "forkortning": "G", "kod": "740000002" }, "delresor": [ { "avgangstid": "2026-11-03T08:14:00", "ankomsttid": "2026-11-03T13:30:00", "origin": { "namn": "Stockholm C", "forkortning": "CST", "kod": "740000001" }, "destination": { "namn": "Göteborg C", "forkortning": "G", "kod": "740000002" }, "trainIdent": "165", "nameHumanReadable": "Regional tåg 165", "tagTyp": "SJ Regional", "ankomst30d": { "train": "165", "station": "Göteborg C", "ankomster": 25, "typ": "SJ Regional", "delaytypes": { "ON_TIME": 72, "LESSEQ_THAN_30M": 28, "MORE_THAN_30M": 0, "CANCELLED": 0 } }, "ankomst90d": { "…": "same shape, last 90 days" }, "ankomst180d": { "…": "same shape, last 180 days" }, "ankomst360d": { "…": "same shape, last 360 days" } } ] } ] ``` - A journey: `avgangstid`/`ankomsttid` (departure/arrival, `HH:MM`), `restid` (travel time), `byten` (changes, e.g. "1 byte", empty when direct) and `delresor` (legs, one per train). - A leg: departure and arrival as full timestamps, stations, `trainIdent` (train number), `tagTyp` (operator and train type). - `ankomst30d` … `ankomst360d`: how the train's arrivals at the leg's destination went over that period. `ankomster` is the number of arrivals; `delaytypes` holds **percentages (0–100)**, or is `null` when there's no data. A period can be missing. - Errors: **400** for a search that can't be made: a missing parameter, an unknown station code, a malformed `datetime` or one more than a year away, or the same station as `source` and `dest`. Don't retry it unchanged. **429** when the rate limit is hit: wait as many seconds as the `Retry-After` header says, then retry. Error responses are JSON with a `message` in Swedish saying what was wrong, e.g. `{"status": 400, "error": "Bad Request", "message": "Okänd station: XYZ", …}` ("unknown station"). **500** is a fault on our side. - For a journey with changes, each leg has its own statistics. Their product is only a rough guide to arriving on time overall: delays on the same day go together, and the time to change trains matters. ### Statistics: punctuality grouped and filtered `GET /api/aggregations?groupBy=company&stationName=Sundsvall%20C&from=2026-01-01` - `groupBy` (required): `company`, `stationName`, `advertisedTrainIdent` (train number), `month`, `week` or `date`. - Filters, all optional: `stationName` and `company` (names from the two lists below), `advertisedTrainIdent`, `from` and `to` (`YYYY-MM-DD`, inclusive). - Returns rows `{ "group", "onTime", "lessThan30late", "moreThan30late", "cancelled", "count" }`. Here the shares are **fractions (0–1)**, not percentages. For `month`, `week` and `date`, `group` is the period's start as an ISO timestamp. Grouping by train number returns at most 1000 rows. `GET /api/stations`: the station names used by `stationName`, e.g. `["Alvesta","Arlanda C",…]`. `GET /api/companies`: the operators used by `company`, e.g. `["SJ","Norrtåg","Pågatågen",…]`. ### Links to the website To send a person to the numbers, link to these pages (in Swedish): - Journey planner: https://www.forsenad.nu/ - A table, filtered like `/api/aggregations`: `https://www.forsenad.nu/#/tabeller?groupBy=month&stationName=Sundsvall%20C&advertisedTrainIdent=575` - About the site: https://www.forsenad.nu/#/om ## Station codes For the journey planner's `source` and `dest`. | Station | Code | |---|---| | Alvesta | `Av` | | Arlanda C | `Arnc` | | Arvika | `Ar` | | Avesta Krylbo | `Avky` | | Boden C | `Bdn` | | Bollnäs | `Bn` | | Borlänge C | `Blgc` | | Borås C | `Bsc` | | Bräcke | `Bä` | | Duved | `Du` | | Emmaboda | `Em` | | Eskilstuna C | `Et` | | Falköping C | `F` | | Falun C | `Fln` | | Gällivare C | `Gvc` | | Gävle C | `Gä` | | Göteborg C | `G` | | Hallsberg | `Hpbg` | | Halmstad C | `Hd` | | Haparanda | `Hp` | | Helsingborg C | `Hb` | | Herrljunga C | `Hrc` | | Hudiksvall | `Hkl` | | Härnösand C | `Hsd` | | Hässleholm | `Hm` | | Jönköping C | `Jö` | | Kalmar C | `Kac` | | Karlskrona C | `Ck` | | Karlstad C | `Ksc` | | Katrineholm C | `K` | | Kiruna | `Kmb` | | Laxå | `Lå` | | Linköping C | `Lp` | | Luleå | `Le` | | Lund C | `Lu` | | Malmö C | `Mc` | | Mjölby | `My` | | Mora C | `Mrc` | | Norrköping C | `Nr` | | Nässjö C | `N` | | Riksgränsen | `Rgn` | | Sala | `Sl` | | Stockholm C | `Cst` | | Strömstad | `Smd` | | Sundsvall C | `Suc` | | Söderhamn | `Shv` | | Tranås | `Tns` | | Trollhättan | `Thn` | | Umeå C | `Uå` | | Uppsala C | `U` | | Vetlanda | `Vta` | | Vingåker | `Vr` | | Vännäs | `Vns` | | Västerås C | `Vå` | | Ystad | `Y` | | Ängelholm | `Ä` | | Ånge | `Åg` | | Åre | `Åre` | | Örebro C | `Ör` | | Örnsköldsvik C | `Ök` | | Östersund C | `Ös` | ## Optional - [Blog](https://www.forsenad.nu/#/blogg): how the site is built and what's planned (Swedish) - [Trafikverket's open API](https://www.trafikverket.se/e-tjanster/trafikverkets-oppna-api-for-trafikinformation/): the source of the delay data