Why TransitKit?

What TransitKit handles for you. These are real findings from building against the TfNSW Trip Planner API.


1

The API cannot be called from a browser

Every OPTIONS preflight to the TfNSW API returns HTTP 500 with a SOAP XML fault: "Policy Falsified" from their API Gateway. There is no Access-Control-Allow-Origin header on any response. This means any fetch() from a web app, React app, or Next.js frontend will fail instantly at the preflight check. Without TransitKit, you must build and maintain your own backend proxy just to access the data.

2

No isLive boolean

realtimeStatus: ["MONITORED"] sounds like it means a trip is live — it just means the stop is monitored, not the specific trip. Real-time data is inferred by comparing departureTimeEstimated vs departureTimePlanned, handling null, missing, and identical values. Real-time coverage varies wildly: at Circular Quay, 0/40 events had real-time data during testing. At Newtown, 10/30 did. TransitKit infers this correctly and returns a clean is_live boolean.

3

All transport modes mixed together — 40 events hard cap

The API returns exactly 40 departure events regardless of what you request — limit=5 and maxResults=100 both return 40. At Central Station those 40 events cover trains, metro, light rail, and buses combined. Filter client-side to buses and you might end up with 8 results covering the next 51 minutes. TransitKit handles the filtering and returns only buses.

4

Ghost stops in nearby results

The nearby stops API returns decommissioned stops alongside active ones. Stop G200096 (Bridge St at Gresham St) appears in nearby results with modes: [5] indicating it serves buses — but querying it for departures returns zero results with error code -4050. There is no flag to distinguish ghost stops from active ones. TransitKit filters these silently.

5

Stop search returns 47 junk results for every 1 real stop

Searching for "Newtown" with anyObjFilter_sf=2 (supposedly "stops only") returns 48 results: streets, suburbs, restaurants, hotels, and parks — and exactly 1 actual stop at position 48. type_sf=stop returns an error. TransitKit's search returns only stops.

6

Two coordinate systems, one API

Query parameters use longitude:latitude:EPSG:4326 order. Response coord arrays are [latitude, longitude]. The radius parameter for nearby stops is completely ignored — the API has its own internal ~1.3km cap regardless of what you pass. TransitKit uses lat, lng everywhere and exposes a radius parameter that actually works.

7

Timezone mismatch between input and output

The itdDate/itdTime parameters for future departures accept local Sydney time (AEDT/AEST). The response times are always UTC. Send 0800 for 8am, get back 21:00:00Z (UTC day before during AEDT). TransitKit accepts UTC for both input and output.

8

HTTP 200 for errors

No departures at this stop? HTTP 200 with systemMessages: [{type: "error", code: -4050, text: "no serving lines found"}]. Ambiguous query? HTTP 200 with a partial result and an error in systemMessages. Your HTTP client treats all of these as successful responses. TransitKit returns proper 4xx/5xx codes.

9

Non-standard auth

Authorization: apikey <jwt> — not Bearer, not Basic. TransitKit uses standard Bearer auth.


Known TfNSW Limitations

We believe in documenting what doesn't work as clearly as what does. These are upstream TfNSW constraints that TransitKit surfaces transparently.

Real-time predictions are snapshots, not live streams

TfNSW serves a frozen GTFS-RT prediction for each departure. The realtime_at value does not update between calls — minutes_away counts down against the original snapshot. This is an upstream constraint no wrapper can solve. TransitKit surfaces this honestly via is_live so you always know what you're working with.

Real-time coverage varies by operator and route

Private bus operators (Transit Systems, Transdev) have 25–60% real-time coverage. Some routes like 20T4 and 339 are 100% tracked. Government heavy rail (Sydney Trains) has 0% real-time in TfNSW departure data — but TransitKit filters those out entirely. For bus departure boards, is_live: true is meaningful and reliable on well-tracked routes.

Stop search is name-matching only

Searching by street address (e.g. "42 King St Newtown") will not find nearby stops. The TfNSW stop finder matches against canonical stop names, not geocoded locations. Use /v1/nearby with lat/lng coordinates for address-based lookups.

NightRide real-time coverage is sparse

Night bus routes (N10, N30, N40 etc.) have significantly lower real-time tracking coverage than daytime services. During testing, N10 had zero real-time data across all departures. is_live: false accurately reflects this — TransitKit never fabricates real-time data.