Skip to main content

TfNSW Trip Planner — Python Client

A clean, idiomatic Python library for the Transport for NSW Trip Planning APIs.


Installation

pip install requests          # only external dependency

Copy the tfnsw_trip_planner/ package into your project, then import it:

from tfnsw_trip_planner import TripPlannerClient

Getting an API Key

The API key is free. You get one from the TfNSW Open Data portal:

  1. Create an account at https://opendata.transport.nsw.gov.au/data/user/register (or log in if you already have one).
  2. Create an Application: from your account menu open Applications → Create Application, give it a name, and select the API products you need. At minimum add Trip Planner. For live vehicle positions (example 11) also add Public Transport – Realtime Vehicle Positions.
  3. Copy the API key shown for that application. The same key authenticates every endpoint in this library.

Note: Each API product must be added to your application separately. If a call returns 401/403, the most common cause is that the relevant product (e.g. Realtime Vehicle Positions) isn't enabled on your key yet.

Pass the key when constructing the client:

client = TripPlannerClient(api_key="YOUR_API_KEY")

Avoid hard-coding the key in source — read it from the environment instead:

import os
client = TripPlannerClient(api_key=os.environ["TFNSW_API_KEY"])

Quick Start

from tfnsw_trip_planner import TripPlannerClient

client = TripPlannerClient(api_key="YOUR_API_KEY")

Don't have a key yet? See Getting an API Key above.


Examples

1. Find a Stop

locations = client.find_stop("Circular Quay")
for loc in locations:
    print(loc.name, loc.id, loc.coord)

# Get the best single match
best = client.best_stop("Domestic Airport")
print(best.id, best.name)

2. Plan a Trip

from datetime import datetime

journeys = client.plan_trip(
    origin_id="10101331",       # Domestic Airport Station
    destination_id="10102027",  # Manly Wharf
)

for journey in journeys:
    print(journey)
    # Journey(legs=3, duration=61min, summary='Train → Walk → Bus')

    print(f"  Depart : {journey.departure_time}")
    print(f"  Arrive : {journey.arrival_time}")
    print(f"  Route  : {journey.summary}")

    fare = journey.fare_summary
    if fare:
        print(f"  Cost   : ${fare.price_total:.2f} ({fare.status.value})")

3. Arrive By a Specific Time

from datetime import datetime

# Plan a trip that arrives by 6 PM today
arrive_by = datetime.now().replace(hour=18, minute=0)
journeys = client.plan_trip(
    origin_id="10101331",
    destination_id="10102027",
    when=arrive_by,
    arrive_by=True,
)

4. Directions from GPS Location

journeys = client.plan_trip_from_coordinate(
    latitude=-33.884080,
    longitude=151.206290,
    destination_id="10102027",
)

5. Wheelchair-Accessible Trips Only

journeys = client.plan_trip(
    origin_id="10101331",
    destination_id="10102027",
    wheelchair=True,
)

for journey in journeys:
    for leg in journey.legs:
        print(f"  {leg.transportation.number}")
        print(f"    Low-floor vehicle     : {leg.low_floor_vehicle}")
        print(f"    Wheelchair accessible : {leg.wheelchair_accessible_vehicle}")
        print(f"    Origin accessible     : {leg.origin.wheelchair_access}")
        print(f"    Destination accessible: {leg.destination.wheelchair_access}")

6. Cycling Trip

from tfnsw_trip_planner import CyclingProfile

journeys = client.plan_cycling_trip(
    origin_id="10101331",
    destination_id="10102027",
    profile=CyclingProfile.MODERATE,
    bike_only=True,
)

7. Upcoming Departures (Departure Board)

departures = client.get_departures("10101331")  # Domestic Airport

for event in departures:
    mins = event.minutes_until_departure
    rt   = "⚡" if event.is_realtime else "🕐"
    print(f"{rt} {mins:>3}m  {event.transportation.number:>8}{event.transportation.destination_name}")

# From a specific platform only
departures = client.get_departures("10101331", platform_id="202091")

8. Travel in Cars (Train Car Guidance)

departures = client.get_departures("10101331")
for event in departures:
    for tic in event.travel_in_cars():
        print(
            f"Train has {tic.number_of_cars} cars. "
            f"Board cars {tic.from_car}{tic.to_car} ({tic.message})"
        )

9. Service Alerts

alerts = client.get_alerts()
for alert in alerts:
    print(alert.subtitle)
    print(f"  Affected stops: {len(alert.affected_stops)}")
    print(f"  Affected lines: {len(alert.affected_lines)}")

# Alerts for a specific stop
alerts = client.get_alerts(stop_id="10111010")  # Central Station

10. Nearby Stops / Opal Resellers

# Stops within 500m
nearby = client.find_nearby(latitude=-33.884080, longitude=151.206290, radius_m=500)
for loc in nearby:
    print(loc.name, loc.properties.get("distance"), "m")

# Opal resellers within 1km
resellers = client.find_opal_resellers(latitude=-33.884080, longitude=151.206290)
for r in resellers:
    print(r.name, r.coord)

11. Live Vehicle Positions (GTFS-Realtime)

Get the exact GPS location of vehicles in real time (latitude/longitude, bearing, speed) — as opposed to the real-time timing estimates returned by the trip/departure endpoints.

This uses the separate GTFS-Realtime Vehicle Positions feed. You must subscribe to that API product on the Open Data portal (the same API key is used), and install the optional dependency:

pip install tfnsw-trip-planner[realtime]
# mode is appended to the vehiclepos/ endpoint — see client.VEHICLE_POSITION_MODES
buses = client.vehicle_positions("buses")

for v in buses[:5]:
    print(f"{v.vehicle_id}  route={v.route_id}  "
          f"{v.latitude:.5f},{v.longitude:.5f}  "
          f"bearing={v.bearing}  @ {v.timestamp}")

# Other feeds: "nswtrains", "metro", "ferries/sydneyferries",
#              "lightrail/cbdandsoutheast", "lightrail/newcastle", ...
ferries = client.vehicle_positions("ferries/sydneyferries")

Filtering by route. Each feed returns every vehicle for that mode (the buses feed is ~1,000 vehicles), so filter client-side. route_id is formatted <prefix>_<routeShortName> (e.g. 2510_992), so match on the suffix:

buses = client.vehicle_positions("buses")
on_992 = [b for b in buses if b.route_id and b.route_id.split("_")[-1] == "992"]

Vehicles without a current GPS fix report latitude/longitude of 0.0 — filter those out if you're plotting positions.


Using as a Context Manager

with TripPlannerClient(api_key="YOUR_KEY") as client:
    journeys = client.plan_trip("10101331", "10102027")

API Reference

TripPlannerClient

Method Description
find_stop(query, ...) Search stops/POIs by name
find_stop_by_id(stop_id) Look up a stop by its ID
best_stop(query) Return the top-matching stop
plan_trip(origin_id, destination_id, ...) Plan a journey
plan_trip_from_coordinate(lat, lon, dest_id, ...) Trip from GPS coordinate
plan_cycling_trip(origin_id, dest_id, ...) Cycling trip
get_departures(stop_id, ...) Upcoming departures from a stop
get_alerts(...) Service alerts
find_nearby(lat, lon, ...) POIs near a coordinate
find_opal_resellers(lat, lon, ...) Opal resellers near a coordinate
vehicle_positions(mode) Live vehicle GPS positions (GTFS-Realtime)

Key Models

Model Key Attributes
Location id, name, type, coord, modes, is_best
Journey legs, departure_time, arrival_time, total_duration, summary, fare_summary
Leg mode, origin, destination, duration, stop_sequence, coords, infos
Stop id, name, departure_time, arrival_time, wheelchair_access
Transport number, mode, destination_name
StopEvent transportation, departure_time, is_realtime, minutes_until_departure
Fare person, price_total, station_access_fee, status
ServiceAlert subtitle, url, affected_stops, affected_lines
TravelInCars number_of_cars, from_car, to_car, message
VehiclePosition vehicle_id, route_id, trip_id, latitude, longitude, bearing, speed, timestamp, coord

CyclingProfile Enum

Value Description
CyclingProfile.EASIER Avoids hills and busy roads
CyclingProfile.MODERATE Intermediate — occasional hills
CyclingProfile.MORE_DIRECT Fastest route, steeper hills allowed

TransportMode Enum

TRAIN, LIGHT_RAIL, BUS, COACH, FERRY, SCHOOL_BUS, WALK, CYCLE, ON_DEMAND


Error Handling

from tfnsw_trip_planner import APIError, NetworkError

try:
    journeys = client.plan_trip("10101331", "10102027")
except NetworkError as e:
    print("Network problem:", e)
except APIError as e:
    print(f"API error {e.status_code}:", e)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tfnsw_trip_planner-1.3.1.tar.gz (25.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tfnsw_trip_planner-1.3.1-py3-none-any.whl (23.3 kB view details)

Uploaded Python 3

File details

Details for the file tfnsw_trip_planner-1.3.1.tar.gz.

File metadata

  • Download URL: tfnsw_trip_planner-1.3.1.tar.gz
  • Upload date:
  • Size: 25.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tfnsw_trip_planner-1.3.1.tar.gz
Algorithm Hash digest
SHA256 8b14d7502c5ca85c0b4b942e2600a6dddd7fee59d969edf1bb0b489b927e0741
MD5 b486b9b45af20de853911402a37d4bae
BLAKE2b-256 93f0ab6575064be7bde898df4b7981c0f2d2ce671ea3d8da1758f747dc9d90fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for tfnsw_trip_planner-1.3.1.tar.gz:

Publisher: publish.yml on maxim75/tfnsw_trip_planner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tfnsw_trip_planner-1.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for tfnsw_trip_planner-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0f9f6624e311c0c45763c4b5d4987d2c13e1076cbd4a5d1435604d836d440bb7
MD5 c49818be9928e3a985bc86814395f3da
BLAKE2b-256 7e13826d444f47fd1564dfd49dfbe7ff2ae7eca9efe4395077c8464e7379e486

See more details on using hashes here.

Provenance

The following attestation bundles were made for tfnsw_trip_planner-1.3.1-py3-none-any.whl:

Publisher: publish.yml on maxim75/tfnsw_trip_planner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.3.1 This release

2 files

1.3.0

2 files

1.2.0

2 files

1.0.6

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.1

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page