Podnik

Federation of transport cooperatives of the Republic of Fluid

Open data

Every vehicle in traffic reports its position, and the whole of it is published without an account, a key or a quota. Apps use it with OpenStreetMap to show where a tram is and how long a rider waits at the stop.

The interface

The base is https://api.podnik.rof. Everything answers JSON over HTTPS and everything answers to GET.

  • GET /v1/positions

    Where everything is, now

    One record for every vehicle in traffic, 12,400 of them at the morning peak and around 900 at three in the morning. Refreshed every ten seconds, and every second for the Lindoma pods. The whole document is about 2.4 MB uncompressed and 260 kB on HTTPS.

    Filter it down with ?mode=tram, ?town=lindoma or ?line=14, in any combination.

  • GET /v1/lines

    What runs where

    Every line, its stops in order, the OpenStreetMap way and node identifiers it follows, the models normally allocated to it, and the timetable in force. Changes rarely. Cache this one.

  • GET /v1/journeys

    What is coming, and when

    For a stop or a line, the journeys due, each with a scheduled time, a predicted time and a margin of uncertainty in seconds. The prediction comes from the vehicle position, the running time observed on that section over the last twenty minutes, and the time spent waiting at the stops between here and there.

    ?stop= takes either a Podnik stop identifier or an OpenStreetMap node.

  • GET /v1/vehicle/{id}

    One vehicle

    Position, line, journey, model code, depot of allocation, and for a pod the identifier of the controller supervising it. The model code links straight into the catalogue, so an app can show a rider which vehicle is arriving before it arrives.

Shape of a record

One vehicle from the tram feed on line 14 at Lindoma, on a weekday morning.

GET https://api.podnik.rof/v1/positions?line=14&mode=tram

{
  "generated": "2026-07-31T08:14:20Z",
  "next_in": 10,
  "vehicles": [
    {
      "id": "LIN-T-04417",
      "model": "FER T4",
      "line": "14",
      "journey": "14-0812-E",
      "lat": 52.1174,
      "lon": 9.4402,
      "bearing": 214,
      "speed_kmh": 21.4,
      "occupancy": "some_seats",
      "next_stop": {
        "podnik": "LIN-0412",
        "osm_node": 3184402117,
        "eta": "2026-07-31T08:15:48Z",
        "uncertainty_s": 20
      },
      "depot": "Lindoma North",
      "accessible": true
    }
  ]
}

Method

  1. Ask the stop, not the vehicle

    Call /v1/journeys?stop=LIN-0412. The reply already accounts for vehicles that have not yet started their journey, which a position feed alone cannot show.

  2. Take the difference, then widen it

    The waiting time is the predicted time minus the current time. Show the margin of uncertainty with it. A margin of 20 seconds at three stops out becomes 90 seconds at fifteen stops out.

  3. Refresh at the rate of the mode

    Ten seconds for everything except the pods, which move on a one-second feed. Polling faster returns the same document with the same generated stamp.

  4. Fall back to the timetable

    When predicted is absent the vehicle has not reported for more than two minutes, show the scheduled time and state that it is scheduled. The field stale_s gives the length of the silence.

The map underneath

Podnik publishes identifiers, and OpenStreetMap helps place them. Every stop in the feed names the node it matches, and every line gives the ordered ways it runs over. Drawing a live map needs the two together: the feed for the moving points and the map for everything standing still.

The tagging is as follows: stops are public_transport=stop_position with network=Podnik and the Podnik identifier in ref:podnik. Routes use route=tram, route=bus, route=train, route=ferry and, for the Lindoma lanes, route=bus with podnik:pod=yes. Corrections go to OpenStreetMap directly, and the federation surveys the whole network against it twice a year.

The two are published under different terms. The Podnik feed is in the public domain, since it was made with public resources and patents are prohibited here. OpenStreetMap data comes under the Open Database Licence, and an application mixing them stays subject to that licence for the map part.

Other

  • No account, no key, no quota

    There is no registration and no agreement to sign. The feed answers the same to a resident with a phone, a commune with a departure board and a company abroad.

  • Mirrors at every regional exchange

    Twenty-two mirrors serve the same document. The nearest one answers on api.podnik.rof, and each is reachable directly as api-01.podnik.rof through api-22.podnik.rof if the nearest is down.

  • Cache the slow things

    Lines and stops change a few times a year. Positions change every ten seconds. Fetching the first as often as the second wastes energy at the endpoint.

  • Faults are published

    Feed interruptions go on the status page with the cause and the duration, alongside the figure for how much of the year it was running. That has been 99.94 per cent since 2021.