BYTETOOLS

Entur JourneyPlanner API

Free Norwegian journey planner API with no key: plan multi-modal trips across every operator in Norway with a single GraphQL query. Tested example and live response included.

No API key requiredCORS enabledHTTPSFree tier

Endpoint tested and returned HTTP 200 on 2026-08-21

What is the Entur JourneyPlanner API?

Entur's JourneyPlanner is a free, key-free GraphQL API covering all public transport in Norway. One query plans an end-to-end journey across trains, buses, trams, ferries, flights and walking legs, returning each leg with its mode, operator, line, distance and expected times.

Norway consolidated every operator's timetable into one national dataset and put a single planner in front of it, which produces itineraries that would be several separate lookups elsewhere. The example below asks for Oslo to Bergen and gets back a genuinely multi-modal answer — airport express train, a walk through the terminal, a domestic flight, another walk and an airport bus — because the planner treats air as just another mode rather than a different product.

There is no API key, but there is an etiquette requirement: every request must carry an `ET-Client-Name` header identifying your application, conventionally as `company-application`. Entur uses it for support and abuse handling and will throttle unidentified traffic. Being GraphQL, you choose your own response shape, which is the main practical advantage here — a departure board can ask for four fields while a full planner asks for fifty, from the same endpoint.

Quick facts

Base URL
https://api.entur.io/journey-planner/v3
Authentication
No API key or account. An `ET-Client-Name` header identifying your application is mandatory; requests without it are throttled.
Rate limit
No published numeric quota for identified clients. Entur asks that you send `ET-Client-Name`, cache aggressively and avoid polling loops.
Pricing
Free. Norwegian national public transport data under an open licence.
CORS
Enabled — callable directly from browser JavaScript
Official docs
Read the docs

How to use the Entur JourneyPlanner API

Every request below was executed against the live API on 2026-08-21, and the response shown is the real body it returned — not an illustration.

1. Plan a multi-modal journey from Oslo to Bergen

POST https://api.entur.io/journey-planner/v3/graphql

curl
curl -X POST 'https://api.entur.io/journey-planner/v3/graphql' \
  -H 'Content-Type: application/json' \
  -H 'ET-Client-Name: bytevancer-bytetools' \
  -d '{"query":"{trip(from:{place:\"NSR:StopPlace:59872\"},to:{place:\"NSR:StopPlace:59983\"},numTripPatterns:1){tripPatterns{expectedStartTime expectedEndTime duration legs{mode distance line{publicCode name} fromPlace{name} toPlace{name}}}}}"}'
JavaScript (fetch)
const res = await fetch("https://api.entur.io/journey-planner/v3/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "ET-Client-Name": "bytevancer-bytetools",
  },
  body: JSON.stringify({"query":"{trip(from:{place:\"NSR:StopPlace:59872\"},to:{place:\"NSR:StopPlace:59983\"},numTripPatterns:1){tripPatterns{expectedStartTime expectedEndTime duration legs{mode distance line{publicCode name} fromPlace{name} toPlace{name}}}}}"}),
});
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const data = await res.json();
console.log(data);
Python (requests)
import requests

headers = {
    "Content-Type": "application/json",
    "ET-Client-Name": "bytevancer-bytetools",
}

payload = {"query":"{trip(from:{place:\"NSR:StopPlace:59872\"},to:{place:\"NSR:StopPlace:59983\"},numTripPatterns:1){tripPatterns{expectedStartTime expectedEndTime duration legs{mode distance line{publicCode name} fromPlace{name} toPlace{name}}}}}"}

res = requests.post("https://api.entur.io/journey-planner/v3/graphql", headers=headers, json=payload, timeout=20)
res.raise_for_status()
print(res.json())
Response — HTTP 200
{
  "data": {
    "trip": {
      "tripPatterns": [
        {
          "expectedStartTime": "2026-08-21T10:30:00+02:00",
          "expectedEndTime": "2026-08-21T13:19:19+02:00",
          "duration": 10159,
          "legs": [
            {
              "mode": "rail",
              "distance": 47631.0,
              "line": {
                "publicCode": "FLY2",
                "name": "FLY2"
              },
              "fromPlace": {
                "name": "Oslo S"
              },
              "toPlace": {
                "name": "Oslo lufthavn stasjon"
              }
            },
            {
              "mode": "foot",
              "distance": 246.46,
              "line": null,
              "fromPlace": {
                "name": "Oslo lufthavn stasjon"
              },
              "toPlace": {
                "name": "Oslo lufthavn"
              }
            },
            {
              "mode": "air",
              "distance": 324009.0,
              "line": {
                "publicCode": null,
                "name": "Oslo-Bergen"
              },
              "fromPlace": {
                "name": "Oslo lufthavn"
              },
              "toPlace": {
                "name": "Bergen lufthavn"
              }
            },
            {
              "mode": "foot",
              "distance": 192.43,
              "line": null,
              "fromPlace": {
                "name": "Bergen lufthavn"
              },
              "toPlace": {
                "name": "Bergen lufthavn"
              }
            },
            {

Parameters

ParameterTypeRequiredDescription
ET-Client-NameheaderRequiredIdentifies your application, conventionally `company-application`. Required. bytevancer-bytetools
querygraphqlRequiredThe GraphQL query. `trip(from:, to:)` is the journey planning entry point. {trip(from:{place:"NSR:StopPlace:59872"}...)}
from / tographqlRequiredOrigin and destination as `{place: "NSR:StopPlace:id"}` or `{coordinates: {latitude, longitude}}`. NSR:StopPlace:59872
numTripPatternsgraphqlOptionalHow many alternative itineraries to return. 3
dateTimegraphqlOptionalISO 8601 departure time. Defaults to now. 2026-08-21T10:00:00+02:00
modesgraphqlOptionalRestrict the transport modes considered, for example rail and bus only. {transportModes:[{transportMode:rail}]}
(geocoder)pathOptional`/geocoder/v1/autocomplete?text=` resolves a place name to the `NSR:StopPlace:` id the planner needs.

Response fields

data.trip.tripPatternsarray
The alternative itineraries found, best first.
tripPatterns[].expectedStartTime / expectedEndTimestring
ISO 8601 with a Norwegian offset. These are expected rather than scheduled — real-time data is already applied.
tripPatterns[].durationinteger
Total journey time in seconds, including waiting and interchange.
tripPatterns[].legsarray
The individual legs, in order.
legs[].modestring
`rail`, `bus`, `tram`, `water`, `air`, `metro` or `foot`. Walking legs are included explicitly.
legs[].distancefloat
Leg distance in metres. The air leg in the example is 324 kilometres.
legs[].lineobject
Service line with `publicCode` and `name`. Null on walking legs — check before dereferencing.
legs[].fromPlace / toPlaceobject
Named endpoints of each leg, which is what makes the itinerary readable.

What you can build with the Entur JourneyPlanner API

  • Plan door-to-door journeys anywhere in Norway
  • Build a departure board for a Norwegian stop
  • Show travel time and interchange count between two towns
  • Include ferries and domestic flights in a single itinerary
  • Request exactly the fields a screen needs, and nothing more

Common errors and how to fix them

Throttled or rejected requests

The `ET-Client-Name` header is missing.

Fix: Send it on every request, in the form `company-application`. It is not authentication, but Entur treats unidentified traffic as abusable.

Null pointer on leg.line

Walking legs have no line.

Fix: Guard every access to `line`. A typical Norwegian itinerary has more foot legs than transit legs.

GraphQL errors array with HTTP 200

GraphQL reports query errors in the body, not the status code.

Fix: Check for an `errors` key on every response. A malformed field name returns 200 with the error inside.

Unknown place id

The `NSR:StopPlace:` identifier is wrong or does not exist.

Fix: Resolve names through the geocoder endpoint first, or pass coordinates instead of an id.

Entur JourneyPlanner API — frequently asked questions

Is the Entur API free?

Yes, free with no key or account. The only requirement is an `ET-Client-Name` header identifying your application, which Entur uses for support and to manage abuse.

Does it cover every operator in Norway?

Yes. Entur aggregates the national timetable across all public transport operators — rail, bus, tram, metro and ferry — and includes domestic flights as an air mode in journey planning.

Why GraphQL rather than REST?

Because journey data is deeply nested and different clients need wildly different slices of it. A departure board can request four fields and a full planner fifty, from the same endpoint, without either over-fetching.

How do I find a stop place id?

Use the Entur geocoder at `/geocoder/v1/autocomplete?text=`, which returns `NSR:StopPlace:` identifiers for matching places. The planner also accepts raw coordinates if you would rather skip the lookup.

Tools that pair with this API

Entur JourneyPlanner is an independent third-party service and is not affiliated with ByteTools or ByteVancer. Details on this page were verified on 2026-08-21; always check the official documentation before relying on this API in production, as terms and limits can change.