{"id":18,"date":"2018-09-26T13:12:00","date_gmt":"2018-09-26T13:12:00","guid":{"rendered":""},"modified":"2023-08-26T13:00:31","modified_gmt":"2023-08-26T13:00:31","slug":"gautrain-api-reverse-engineering","status":"publish","type":"post","link":"https:\/\/sandrock.co.za\/carl\/2018\/09\/gautrain-api-reverse-engineering\/","title":{"rendered":"Gautrain API reverse engineering"},"content":{"rendered":"<h1>\nGautrain API reverse engineering<\/h1>\n<p>The iOS 12 update contained a very cool feature: Siri Shortcuts. This replaces the Workflows app. I was using Workflows to send my wife an SMS with my estimated time of arrival at home when I left work. With the new Shortcuts app I could have this all happen automatically, and it also supports reading from Web APIs.<br \/>\nThe first part, however, is to figure out how to get data from the Gautrain website.<\/p>\n<h2>\nmitmproxy<\/h2>\n<p><a href=\"https:\/\/www.blogger.com\/blogger.g?blogID=7479095831591011602\">mitmproxy<\/a> or Man In The Middle Proxy is a piece of software you can run on your computer which will stand between a device and the internet and allow you to inspect what is going on. I might cover installation in another post, but basically I just followed instructions to get it going and then fired up the Gautrain app to see what it was doing.<\/p>\n<h2>\nSequence of events with Gautrain App<\/h2>\n<p>First endpoint to check service is live<\/p>\n<pre><code>https:\/\/api.gautrain.co.za\/user-service\/api\/0\/mobile\/isLive\/1.0.0<\/code><\/pre>\n<p>Returns <code>true<\/code> or <code>false<\/code>.<br \/>\nThe app then hits<\/p>\n<pre><code>https:\/\/api.gautrain.co.za\/transport-api\/api\/0\/agencies<\/code><\/pre>\n<p>And gets a whole list of agencies, including this one:<\/p>\n<pre><code>[\n....\n{\n        \"culture\": \"en\",\n        \"description\": null,\n        \"href\": \"https:\/\/platform.whereismytransport.com\/api\/agencies\/edObkk6o-0WN3tNZBLqKPg\",\n        \"id\": \"edObkk6o-0WN3tNZBLqKPg\",\n        \"name\": \"Gautrain\"\n},\n...\n]<\/code><\/pre>\n<p>The important part here is <code>\"name\": \"Gautrain\"<\/code> and that <code>id<\/code> which is used later.<br \/>\nAlso interesting is that they are clearly using <a href=\"https:\/\/www.blogger.com\/whereismytransport.com\">whereismytransport.com<\/a>, which is the first time I&#8217;ve heard of this service.<br \/>\nThe app then asks ask for a list of stops:<\/p>\n<pre><code>https:\/\/api.gautrain.co.za\/transport-api\/api\/0\/stops\/gautrain<\/code><\/pre>\n<p>The response is a list of stops like this one:<\/p>\n<pre><code>[\n...\n{\n    \"agency\": {\n        \"culture\": \"en\",\n        \"description\": null,\n        \"href\": \"https:\/\/platform.whereismytransport.com\/api\/agencies\/edObkk6o-0WN3tNZBLqKPg\",\n        \"id\": \"edObkk6o-0WN3tNZBLqKPg\",\n        \"name\": \"Gautrain\"\n    },\n    \"code\": null,\n    \"geometry\": {\n        \"coordinates\": [\n            28.23794,\n            -25.74762\n        ],\n        \"type\": \"Point\"\n    },\n    \"href\": \"https:\/\/platform.whereismytransport.com\/api\/stops\/_rkqSHvRE0Scvbcsuy0EVw\",\n    \"id\": \"_rkqSHvRE0Scvbcsuy0EVw\",\n    \"modes\": [\n        \"Rail\",\n        \"Bus\"\n    ],\n    \"name\": \"Hatfield\"\n},\n...\n    {\n    \"agency\": {\n        \"culture\": \"en\",\n        \"description\": null,\n        \"href\": \"https:\/\/platform.whereismytransport.com\/api\/agencies\/edObkk6o-0WN3tNZBLqKPg\",\n        \"id\": \"edObkk6o-0WN3tNZBLqKPg\",\n        \"name\": \"Gautrain\"\n    },\n    \"code\": null,\n    \"geometry\": {\n        \"coordinates\": [\n            28.05693,\n            -26.10858\n        ],\n        \"type\": \"Point\"\n    },\n    \"href\": \"https:\/\/platform.whereismytransport.com\/api\/stops\/jXU-OlvxukW8wfc7JeVeXw\",\n    \"id\": \"jXU-OlvxukW8wfc7JeVeXw\",\n    \"modes\": [\n        \"Rail\",\n        \"Bus\"\n    ],\n    \"name\": \"Sandton\"\n}\n...\n]<\/code><\/pre>\n<p>I&#8217;ve kept the important ones for me &#8211; I&#8217;m going from Hatfield to Sandton and back, so I need those coordinates.<br \/>\nRight, so the next thing they hit is<\/p>\n<pre><code>https:\/\/api.gautrain.co.za\/transport-api\/api\/0\/journey\/create<\/code><\/pre>\n<p>This is the first request which has parameters. You can see that they are using the coordinates for Hatfield station and Sandton station as the start and end points.<\/p>\n<pre><code>{\n    \"geometry\": {\n        \"coordinates\": [\n            [\n                28.23794,\n                -25.74762\n            ],\n            [\n                28.05693,\n                -26.10858\n            ]\n        ],\n        \"type\": \"MultiPoint\"\n    },\n    \"maxItineraries\": 5,\n    \"omit\": {\n        \"agencies\": [\n            \"A1JHSPIg_kWV5XRHIepCLw\",\n            \"CA3o3RuuGUmoDKfyAPNTsg\",\n            \"NfBxKfzMA0exbwToc-7o2g\",\n            \"XxDwxnin_Eu_T7_wBJIJRA\",\n            \"Hwe1673sC0yuT6fyANnDZQ\",\n            \"DcXaTl-5hkGRHKenAIi_5w\",\n            \"WaasqFZwEEa5VqbIAJKsJQ\",\n            \"wZfSY3o0LUGHiqeoAQDeIQ\",\n            \"W7A63uTwIECgQvU9MxcCIw\",\n            \"Ka2d4V1g3E65VqheANhvVw\",\n            \"FTGTH38Tm0C5O6gAAQifSQ\"\n        ],\n        \"modes\": []\n    },\n    \"only\": {\n        \"agencies\": [\n            \"edObkk6o-0WN3tNZBLqKPg\"\n        ],\n        \"modes\": []\n    },\n    \"profile\": \"ClosestToTime\",\n    \"time\": null,\n    \"timeType\": \"DepartAfter\"\n}<\/code><\/pre>\n<p>So that list of agencies to omit is obviously from the original request. I&#8217;m hoping I don&#8217;t need that. For my purposes I just need to know when the next train will arrive and how long it will take to get to the destination.<br \/>\nThe response from this request is quite huge, but the first bit shows the important parts:<\/p>\n<pre><code>{\n    \"agencies\": null,\n    \"earliestDepartureTime\": null,\n    \"geometry\": {\n        \"coordinates\": [\n            [\n                28.23794,\n                -25.74762\n            ],\n            [\n                28.05693,\n                -26.10858\n            ]\n        ],\n        \"type\": \"MultiPoint\"\n    },\n    \"href\": \"https:\/\/platform.whereismytransport.com\/api\/journeys\/ZPtEXRtgeEq1mqljAE4new\",\n    \"id\": \"ZPtEXRtgeEq1mqljAE4new\",\n    \"itineraries\": [\n        {\n            \"arrivalTime\": \"2018-09-22T05:29:52Z\",\n            \"departureTime\": \"2018-09-22T04:56:20Z\",\n            \"distance\": {\n                \"unit\": \"m\",\n                \"value\": 51662\n            },\n            \"duration\": 2012,\n    ...\n    Lots more output<\/code><\/pre>\n<h2>\nSo when is the next train?<\/h2>\n<p>I&#8217;m only really interested in when the next train will arrive at Sandton and I don&#8217;t want to type all that stuff into a mobile app, I want a minimal version.<\/p>\n<p>&nbsp;Further investigation yields the simplified request:<\/p>\n<pre><code>{\"geometry\":\n {\"coordinates\":\n  [[28.23794,-25.74762],\n  [28.05693,-26.10858]],\n  \"type\":\"MultiPoint\"},\n  \"profile\":\"ClosestToTime\",\n  \"maxItineraries\"5,\n  \"timeType\":\"DepartAfter\",\n  \"time\":null,\n  \"only\":{\"agencies\":[\"edObkk6o-0WN3tNZBLqKPg\"],\n  \"modes\":[]}}<\/code><\/pre>\n<p>This is something I can put into the automation app eally easily. More info on that to follow.<br \/>\nFor posterity, this is that call being checked via curl:<\/p>\n<pre><code>curl -d '{\"geometry\":{\"coordinates\":[[28.23794,-25.74762],[28.05693,-26.10858]],\"type\":\"MultiPoint\"},\"profile\":\"ClosestToTime\",\"maxItineraries\":5,\"timeType\":\"DepartAfter\",\"only\":{\"agencies\":[\"edObkk6o-0WN3tNZBLqKPg\"]}}' -H \"Content-Type: application\/json\" -X POST https:\/\/api.gautrain.co.za\/transport-api\/api\/0\/journey\/create<\/code><\/pre>\n<p>Guess it&#8217;s also time to create an account at whereismytransport.com!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gautrain API reverse engineering The iOS 12 update contained a very cool feature: Siri Shortcuts. This replaces the Workflows app. I was using Workflows to send my wife an SMS with my estimated time of arrival at home when I left work. With the new Shortcuts app I could have this all happen automatically, and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-18","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/18","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/comments?post=18"}],"version-history":[{"count":1,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/18\/revisions"}],"predecessor-version":[{"id":125,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/18\/revisions\/125"}],"wp:attachment":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/media?parent=18"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/categories?post=18"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/tags?post=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}