Testing the PTV transport data API using PHP

Last week on March 7 Public Transport Victoria finally released something many Melbourne coders and app developers have been waiting for – a public API to query their timetable data.

Passengers depart the train on platform 10

Unfortunately the new API isn’t perfect – it doesn’t meet the Google-defined GTFS standard that many existing apps support, and no real-time arrival data is available – for trams you have to turn to TramTracker, and for trains and buses you’re on your own.

Despite the above limitations, I jumped into playing around with the API. The full set of documentation can be found at www.data.vic.gov.au, and after I made my API key request on the evening of March 6, I received a reply yesterday – March 11.

API key received from PTV

With my developer ID and security key received, the first thing to try out is the ‘healthcheck’ endpoint. Each API request needs to be digitally signed with a HMAC-SHA1 hash – this ensures that each request to the PTV servers is coming from a registered developer – and the healthcheck page allows a developer to test if their requests are being built up correctly.

For some people, working out the HMAC-SHA1 signature of each request is easy – the API documentation includes sample code in C#, Java and Objective-C to do just that. Unfortunately for me, my random hacking about is done in PHP, so I needed to roll my own version.

(jump to the end of my sample code)

My initial attempt generated something that looked like a HMAC-SHA1 signature, but it didn’t pass validation – I screwed something up! An hour or so of bashing my head against the wall followed, as I played around with Base64 encoding and byte arrays. but it still wasn’t working

I then tried a different tack – getting the sample C# code working in Powershell. For such an odd tactic it was simple enough to achieve, and I was able to get the code to run and generating a valid signature, which I used to make my first valid API hit. From there I was able to hack about my PHP code until it generated the same signature as the C# code generated – the bit I missed was the upper casing.

The end result from PTV – a small snippet of JSON.

Valid response received from the PTV API

Sample code

Here is the method I wrote to take an API endpoint URL (for example “/v2/healthcheck”) and build up a valid signed URL to request data from.

function generateURLWithDevIDAndKey($apiEndpoint, $developerId, $key)
{
	// append developer ID to API endpoint URL
	if (strpos($apiEndpoint, '?') > 0)
	{
		$apiEndpoint .= "&";
	}
	else
	{
		$apiEndpoint .= "?";
	}
	$apiEndpoint .= "devid=" . $developerId;
 
	// hash the endpoint URL
	$signature = strtoupper(hash_hmac("sha1", $apiEndpoint, $key, false));
 
	// add API endpoint, base URL and signature together
	return "http://timetableapi.ptv.vic.gov.au" . $apiEndpoint . "&signature=" . $signature;
}

And here is a sample usage of the above method. (note the sample data for key and developer ID!)

$key = "9c132d31-6a30-4cac-8d8b-8a1970834799"; // supplied by PTV
$developerId = 2; // supplied by PTV
 
$date = gmdate('Y-m-d\TH:i:s\Z');
$healthcheckEndpoint = "/v2/healthcheck?timestamp=" . $date;
 
$signedUrl = generateURLWithDevIDAndKey($healthcheckEndpoint, $developerId, $key);

Now all I need to do is build the wonderful app I have in mind…

Code in GitHub

You can find the above sample code on GitHub at https://github.com/wongm/ptv-api-php-test-harness – all you need to do is update the $key and $developerId values with your own credentials, and it should work on your own server.

Footnote

It looks like plenty of other people are interested in the API – here is a media release from Data.Vic, who host it on behalf of PTV.

The recent release of the PTV Timetable API has, not surprisingly, created the highest level of interest and activity for a single data release on Data.Vic.

In the 7 days since the release of the API data record we have seen:

– Over 3500 data record page views
– More than 700 downloads of the API document, and
– Close to 100 API key requests

You can leave a response, or trackback from your own site.

19 Responses to “Testing the PTV transport data API using PHP”

  1. Jezza says:

    Given the rather limited queries that can be performed through the API, I’m struggling to come up with a good idea for an app beyond the boring examples in the doc provided. Will be interesting to see if anyone can use it in a creative way.

    • Marcus Wong says:

      Transport information and journey planning is a pretty well solved problem – plenty of incredibly smart people around the world have built amazing apps that consume General Transit Feed Specification (GTFS) feeds and present it in different ways.

      The only app ideas I’ve had so far are for displaying data in interesting ways, but not actually indispensable in everyday life.

  2. Joel says:

    Hey mate,

    Just wanted to say that I had the exact same problem, and you saved me presumably hours of angst. Thanks for writing this up, and I look forward to seeing your app.

    • Marcus Wong says:

      I’ve also had someone else on Twitter point out they had the same issue – getting caught out by the uppercase signature requirement! I would have never discovered it if I didn’t compare the results from my PHP code to PTV’s C# reference implementation.

  3. Thank you thank you thank you. I have exactly the same problem, and I have been racking my brains and bleeding from the eyes for two days trying to get this to work. There is no technical assistance from PTV at all, but your solution worked for me straight off.

    • Marcus Wong says:

      Glad that my post helped you out – creating a valid signature seems to be the hardest bit of using the API!

      I’m guessing the reason they don’t give technical assistance is they don’t want to be swamped with people asking questions like “how do I build an iPhone app” which have nothing to do with their API.

  4. Your help solving the access problem has meant I’ve been able to develop and publish a simple extension to our website that draws data from the PTV API.

    See this link http://www.railmaps.com.au/stationdetails.php?StationSelect=15

    The “Next Trains” panel below the map uses the API. At the moment it’s enabled only for stations on the Craigieburn line, and it shows only Metro trains. But I’ll extend it to all stations, lines and modes in coming days.

    Thanks again for your help cracking this one.

  5. […] a result, I took inspiration from my recent experiments with PTV’s new API, and came up with a simple website to give access to TramTracker […]

  6. Mike says:

    Have you managed to get the ‘Specific Next Departures’ working?

    I keep getting the message:

    {“Error”:”Forbidden (403): Supplied signature is invalid for request.”}

    Although my signature/devId combo works for all other requests.

    • Marcus Wong says:

      Sounds like an rather odd issue to have it fail on just one method call. I just tested a request to the same method with my PHP implementation of the signature generation, with no issues.

      This was the URL I hit:
      http://timetableapi.ptv.vic.gov.au/v2/mode/1/line/887/stop/3164/directionid/19/departures/all/limit/1?devid=ID&signature=SIG

      If you leave out the “1” after the “limit” then the result is a HTTP 404, and if you input an incorrect “directionid” value, then you get an empty JSON result set.

      Possibly there is a bug in your signature generation code that is getting tripped up the length of the URL? Using one of the reference implementations with your own developer key would allow you to isolate the issue to an issue with your account, or the signature generation code.

  7. Mike says:

    Thanks. I will try once more later in the day.

    Regarding the directionid, it is mentioned in the api doc that directionid towards the city is 0, how do we determine the directionid in the opposite direction? That would vary for each line.

    • Marcus Wong says:

      The ‘directionid’ parameter isn’t a simple 0/1 value – it is an integer that depends on the current route you are requesting data for.

      It appears chaining of API calls will be required to get the relevant ID – from the documentation:

      The Stops Nearby API requires inputs that are not specific to public transport (i.e. latitude and longitude coordinates). The output, however, includes stop_id and transport_type data. You can then pass that data through the Broad Next Departures API to obtain line_id, direction_id, run_id and timetable data. You can then use these outputs as inputs into other APIs.

      To find out the value for my above example, I called the ‘Broad Next Departures’ method first to see which directions were available at the stop, and then I reused the value in the ‘Specific Next Departures’ call.

  8. Mike says:

    One more thing if you can please try for me… Can you please confirm if the ‘Specific Next Departures’ works for Metropolitan Bus.

  9. Mike says:

    Thanks Marcus.

    Makes sense what you said, however, this chaining of API calls just to get the specific departures, seems like double work.

  10. Ash says:

    Hi Mike,
    I used the signature code that has given by PTV and I get the same error as yours – {“Error”:”Forbidden (403): Supplied signature is invalid for request.”}

    Have you able to sort that out?

Leave a Reply