Skip to content

Instantly share code, notes, and snippets.

@loisaidasam loisaidasam/README.md
Last active Apr 10, 2017

Embed
What would you like to do?
Reverse-engineering NextBus realtime data for the Atlanta Streetcar

Realtime data for the Atlanta Streetcar doesn't seem to be available via an API, and yet this page exists with a realtime map:

https://www.nextbus.com/googleMap/?a=atlanta-sc

I decided that I would reverse-engineer it, for fun.

It seems they utilize a nonce-like concept for validation, although it's not fully baked. They hard-code an initial key in the javascript of the actual pageload, which you can use for all subsequent lookups. On the actual page, they take the response key from each subsequent request and pass the new one each time along with a count variable called cnt, but in practice it seems that isn't actually required. The only limitation seems to be you have to fake the referer header.

Here's how I got it to work:

First scrape the key from the pageload:

$ curl -Ss "https://www.nextbus.com/googleMap/?a=atlanta-sc" | grep keyForNextTime

	  var keyForNextTime="2782100885354";

From there you can either get all of the data about the route:

$ curl -H "Referer: https://www.nextbus.com/googleMap/?a=atlanta-sc" -Ss "https://www.nextbus.com/service/googleMapXMLFeed?command=routeConfig&a=atlanta-sc&r=ASC&key=2782100885354"

<?xml version="1.0" encoding="utf-8" ?> 
<body copyright="All data copyright Atlanta Streetcar - Beta 2017.">
<route tag="ASC" title="A Line King Historic District - Centennial Olympic Park" color="636199" oppositeColor="ffffff" latMin="33.7543" latMax="33.7594999" lonMin="-84.392" lonMax="-84.37468">
<stop tag="4560595" title="Centennial Olympic Park" lat="33.7594199" lon="-84.392" stopId="4560595"/>
<stop tag="4560596" title="Luckie at Cone" lat="33.7580699" lon="-84.3904499" stopId="4560596"/>
<stop tag="4560597" title="Park Place" lat="33.7551299" lon="-84.38827" stopId="4560597"/>
<stop tag="4560586" title="Hurt Park" lat="33.75435" lon="-84.3855099" stopId="4560586"/>
<stop tag="4560587" title="Sweet Auburn Market" lat="33.7544099" lon="-84.3798499" stopId="4560587"/>
<stop tag="4560588" title="Edgewood at Hilliard" lat="33.7543" lon="-84.3763599" stopId="4560588"/>
<stop tag="4560589" title="King Historic District" lat="33.7555599" lon="-84.37468" stopId="4560589"/>
<stop tag="4560590" title="Dobbs Plaza" lat="33.7555899" lon="-84.3784099" stopId="4560590"/>
<stop tag="4560591" title="Auburn at Piedmont" lat="33.7556399" lon="-84.3820299" stopId="4560591"/>
<stop tag="4560592" title="Woodruff Park" lat="33.7560399" lon="-84.3881899" stopId="4560592"/>
<stop tag="4560593" title="Peachtree Center Station" lat="33.75806" lon="-84.38753" stopId="4560593"/>
<stop tag="4560594" title="Carnegie at Spring" lat="33.7594999" lon="-84.3895199" stopId="4560594"/>
<direction tag="ASC_0_var1" title="Centennial Olympic Park" name="Centennial Olympic Park" useForUI="true">
  <stop tag="4560589" />
  <stop tag="4560590" />
  <stop tag="4560591" />
  <stop tag="4560592" />
  <stop tag="4560593" />
  <stop tag="4560594" />
  <stop tag="4560595" />
</direction>
<direction tag="ASC_0_var0" title="King Historic District" name="King Historic District" useForUI="true">
  <stop tag="4560595" />
  <stop tag="4560596" />
  <stop tag="4560597" />
  <stop tag="4560586" />
  <stop tag="4560587" />
  <stop tag="4560588" />
  <stop tag="4560589" />
</direction>
<path>
<point lat="33.75556" lon="-84.37468"/>
<point lat="33.75559" lon="-84.37841"/>
<point lat="33.75564" lon="-84.38203"/>
<point lat="33.75561" lon="-84.38755"/>
<point lat="33.75583" lon="-84.38808"/>
<point lat="33.75604" lon="-84.38819"/>
<point lat="33.75626" lon="-84.38849"/>
<point lat="33.75767" lon="-84.38768"/>
<point lat="33.75806" lon="-84.38753"/>
<point lat="33.75842" lon="-84.38764"/>
<point lat="33.75842" lon="-84.38821"/>
<point lat="33.75836" lon="-84.38852"/>
<point lat="33.75893" lon="-84.3891"/>
<point lat="33.7595" lon="-84.38952"/>
<point lat="33.75959" lon="-84.38978"/>
<point lat="33.75973" lon="-84.39207"/>
<point lat="33.75942" lon="-84.392"/>
</path>
<path>
<point lat="33.75559" lon="-84.37841"/>
<point lat="33.75564" lon="-84.38203"/>
<point lat="33.75561" lon="-84.38755"/>
<point lat="33.75583" lon="-84.38808"/>
<point lat="33.75604" lon="-84.38819"/>
<point lat="33.75626" lon="-84.38849"/>
<point lat="33.75767" lon="-84.38768"/>
<point lat="33.75806" lon="-84.38753"/>
<point lat="33.75842" lon="-84.38764"/>
<point lat="33.75842" lon="-84.38821"/>
<point lat="33.75836" lon="-84.38852"/>
<point lat="33.75893" lon="-84.3891"/>
<point lat="33.7595" lon="-84.38952"/>
<point lat="33.75959" lon="-84.38978"/>
<point lat="33.75973" lon="-84.39207"/>
<point lat="33.75942" lon="-84.392"/>
</path>
<path>
<point lat="33.75942" lon="-84.392"/>
<point lat="33.75897" lon="-84.3914099"/>
<point lat="33.7580699" lon="-84.39045"/>
<point lat="33.75591" lon="-84.38817"/>
<point lat="33.7557" lon="-84.38786"/>
<point lat="33.75513" lon="-84.38827"/>
<point lat="33.7545" lon="-84.38854"/>
<point lat="33.75435" lon="-84.38551"/>
<point lat="33.75441" lon="-84.37985"/>
<point lat="33.7543" lon="-84.37636"/>
<point lat="33.75434" lon="-84.37439"/>
<point lat="33.75548" lon="-84.37438"/>
<point lat="33.75556" lon="-84.37468"/>
</path>
</route>
<keyForNextTime value="2943442794060" />
</body>

Or just get the latest vehicle location data:

$ curl -H "Referer: https://www.nextbus.com/googleMap/?a=atlanta-sc" -Ss "https://www.nextbus.com/service/googleMapXMLFeed?command=vehicleLocations&a=atlanta-sc&r=ASC&t=1491845711300&key=2782100885354&cnt=1"

<?xml version="1.0" encoding="utf-8" ?> 
<body copyright="All data copyright Atlanta Streetcar - Beta 2017.">
<vehicle id="1001" routeTag="ASC" dirTag="ASC_0_var1" lat="33.756019" lon="-84.388097" secsSinceReport="10" predictable="true" heading="313"/>
<vehicle id="1004" routeTag="ASC" dirTag="ASC_0_var0" lat="33.755012" lon="-84.38825" secsSinceReport="11" predictable="true" heading="214"/>
<lastTime time="1491846572892"/>
<keyForNextTime value="2733251236551" />
</body>

I imagine they'd like you to update key, t, and cnt variables for subsequent requests, but in reality it seems that you don't have to.

It seems you can also get next arrival information using the following call:

(note the stop ID being the last part of the pipe-delimited stops parameter)

$ curl -H "Referer: https://www.nextbus.com/googleMap/?a=atlanta-sc" -Ss "https://www.nextbus.com/service/googleMapXMLFeed?command=predictionsForMultiStops&a=atlanta-sc&stops=ASC|null|4560597&key=2782100885354"

<?xml version="1.0" encoding="utf-8" ?> 
<body copyright="All data copyright Atlanta Streetcar - Beta 2017.">
<predictions agencyTitle="Atlanta Streetcar - Beta" routeTitle="A Line King Historic District - Centennial Olympic Park" routeTag="ASC" stopTitle="Park Place" stopTag="4560597">
  <direction title="King Historic District">
  <prediction epochTime="1491847319149" seconds="158" minutes="2" isDeparture="false" affectedByLayover="true" dirTag="ASC_0_var0" vehicle="1001" block="101" tripTag="4963ca5d" />
  <prediction epochTime="1491848219149" seconds="1058" minutes="17" isDeparture="false" affectedByLayover="true" dirTag="ASC_0_var0" vehicle="1004" block="102" tripTag="4a206b8e" />
  <prediction epochTime="1491849119149" seconds="1958" minutes="32" isDeparture="false" affectedByLayover="true" dirTag="ASC_1_var0" vehicle="1001" block="101" tripTag="4d48b96d" />
  <prediction epochTime="1491850019149" seconds="2858" minutes="47" isDeparture="false" affectedByLayover="true" dirTag="ASC_0_var0" vehicle="1004" block="102" tripTag="507f4c36" />
  <prediction epochTime="1491850919149" seconds="3758" minutes="62" isDeparture="false" affectedByLayover="true" dirTag="ASC_0_var0" vehicle="1001" block="101" tripTag="510bd67f" />
  </direction>
</predictions>
<keyForNextTime value="2003892449179" />
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.