Die Drive-Now API und was macht der Ford Transit da eigentlich?

Im Gegensatz zu car2go bietet drive-now momentan leider (und unverständlicherweise) keine offene API für seine Fahrzeugdaten an. Nach meinem ersten Wurf für eine car2go Java Library wollte ich mich davon aber nicht stoppen lassen und habe mir einmal genauer angesehen, woher denn die Webseite ihre Daten bezieht. Erfreulicherweise findet sich dort recht schöne JSON-API, so dass dem Datenabruf (für mich war ohnehin nur der lesende Zugriff interessant) kein Problem zu sein schien.

Die Städte-Liste lädt die Webseite z.b. über https://api2.drive-now.com/cities?expand=cities – Doch ein GET auf diesen URI bringt erstmal Ernüchterung:

{"code":403,"message":"Permission denied."}

Doch nach etwas scharfem Hinsehen wird schnell klar was die Website anders macht. Das Javascript dort sendet zusätzlich einen „X-Api-Key“-Header im Request. Schickt man diesen mit, dann sieht das Ergebnis schon anders aus:

{
 "count": 8,
 "items": [
 {
 "businessAreaUrl": "https:\/\/api2.drive-now.com\/geodata\/6099\/6099.kml",
 "callCenterPhoneNumber": "+49 \/ 800 \/ 7234070",
 "carTypes": {
 "count": 0,
 "items": []
 },
 "cars": {
 "count": 0,
 "items": []
 },
 "chargingStations": {
 "count": 0,
 "items": []
 },
 "cityImageBaseUrl": "https:\/\/prod.drive-now-content.com\/fileadmin\/user_upload_global\/assets\/city\/{city}\/{color}\/{density}\/city.png",
 "countryLabel": "Germany",
 "fuelTypes": {
 "count": 0,
 "items": []
 },
 "id": "6099",
 "isoCountryCode": "DE",
 "latitude": 52.506629,
 "longitude": 13.381492,
 "mobileBusinessAreaUrl": "https:\/\/api2.drive-now.com\/geodata\/6099\/6099_mobile.kml",
 "name": "Berlin",
 "parkingSpaces": {
 "count": 0,
 "items": []
 },
 "petrolStations": {
 "count": 0,
 "items": []
 },
 "prolongAvailable": true,
 "registrationStations": {
 "count": 0,
 "items": []
 },
 "routingCityName": "berlin",
 "routingCountryName": "germany",
 "showBusinessAreaByDefault": false,
 "showChargingStationVisible": true,
 "showPetrolStationVisible": true,
 "showRegistrationStationVisible": true,
 "transmissionTypes": {
 "count": 0,
 "items": []
 }
 },
...
}

Die Struktur sieht auf den ersten Blick etwas merkwürdig aus, aber auf jeden Fall erhält man Infos über alle aktiven Städte. Die Attribute sind dabei recht selbsterklärend, so dass nicht im Detail darauf eingehen werde. Lediglich die numerische „id“ (= CityId) ist von hervorgehobener Bedeutung, die wird später nämlich noch benötigt. Auffällig sind auch die diversen leeren Arrays „mit“ fehlenden Infos: „carTypes“, „cars“, „fuelTypes“, „parkingSpaces“, …

Also die URL nochmal genauer betrachtet und am „expand=cities“ hängen geblieben. Was wenn ich „carTypes“ sehen möchte?

https://api2.drive-now.com/cities?expand=carTypes liefert die Antwort:

{
 "count": 8,
 "items": [
 {
 "businessAreaUrl": "https:\/\/dev.service.drive-now.com\/geodata\/6099\/6099.kml",
 "callCenterPhoneNumber": "+49 \/ 800 \/ 7234070",
 "carTypes": {
 "count": 6,
 "items": [
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/mini.png",
 "group": "MINI",
 "make": "BMW",
 "modelIdentifier": "mini",
 "modelName": "MINI",
 "series": "MINI",
 "variant": ""
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/mini_clubman.png",
 "group": "MINI",
 "make": "BMW",
 "modelIdentifier": "mini_clubman",
 "modelName": "MINI Clubman",
 "series": "MINI",
 "variant": "Clubman"
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/mini_countryman.png",
 "group": "MINI",
 "make": "BMW",
 "modelIdentifier": "mini_countryman",
 "modelName": "MINI Countryman",
 "series": "MINI",
 "variant": "Countryman"
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/bmw_1er.png",
 "group": "BMW",
 "make": "BMW",
 "modelIdentifier": "bmw_1er",
 "modelName": "BMW 1er",
 "series": "1er",
 "variant": ""
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/bmw_activee.png",
 "group": "BMW",
 "make": "BMW",
 "modelIdentifier": "bmw_activee",
 "modelName": "BMW ActiveE",
 "series": "ActiveE",
 "variant": ""
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/bmw_active_e.png",
 "group": "BMW",
 "make": "BMW",
 "modelIdentifier": "bmw_active_e",
 "modelName": "BMW ACTIVE E",
 "series": "ACTIVE E",
 "variant": ""
 }
 ]
 },
 "cars": {
 "count": 0,
 "items": []
 },
 "chargingStations": {
 "count": 0,
 "items": []
 },
...

Siehe da: Fahrzeugtypen! Und auch für die anderen Arrays funktioniert das wunderbar. Auch ein „expand=full“ ist verfügbar und liefert die Inhalte aller Arrays, wobei die Ladezeiten hier vermuten lassen, dass man sich damit bei den Sys-Admins von Drive-Now keine Freunde macht.

Etwas fokussierter wird das ganze aber mit folgender URI: https://api2.drive-now.com/cities/4604?expand=full Das Ergebnis sind alle Daten für die CityId 4604 (München). Auch die anderen expand-Werte funktionieren hier wieder.

Aber es geht noch spezifischer: https://api2.drive-now.com/cities/4604/cars liefert ausschließlich die Autos für München, ohne die City Informationen als Wrapper-Objekt.

Im Ergebnis des „expand=cities“-Aufrufes kann man außerdem sehen, dass unter https://api2.drive-now.com/geodata/4604/4604.kml bzw. https://api2.drive-now.com/geodata/4604/4604_mobile.kml ein KML des jeweiligen Geschäftsgebietes zu finden ist (wobei ich noch keine Muse hatte den Unterschied der _mobile Version näher zu betrachten. Infos dazu gerne in die Kommentare). Im Gegensatz zu den anderen URIs, ist für die KMLs kein API-Key notwendig.

Wirklich erheiternd wird das ganze allerdings, wenn man sich das Javascript der Webseite etwas genauer ansieht, weil man z.B. auf der Suche nach einem funktionierenden API-Key zum testen ist. Dort finden sich nämlich solche Schmankerl (URLs leicht verfremdet):

 getNApiUrl: function() {
 return this.napiUrl = "entw" === this.getEnv() || "dev" === this.getEnv() ? "https://xxx.service.drive-now.com/" : "stage" === this.getEnv() ? "https://other-xxx.service.drive-now.com/" : "https://api2.drive-now.com/", this.napiUrl

Nicht weiter schlimm, schließlich fehlt der API-Key? Nunja:

 return r.getEnv() === "entw" || r.getEnv() === "dev" ? t = "ichBinDerDevAPIKey" : r.getEnv() === "stage" ? t = "ichBinDerStageAPIKey" : t = "ichBinDerDevAPIKey", $("html").hasClass("phmode") ? $.extend(e, {

Aber Entwicklungssystem sind doch bestimmt nicht von extern erreichbar! Nunja²:

{
 "businessAreaUrl": "https:\/\/dev.service.drive-now.com\/geodata\/1774\/1774.kml",
 "callCenterPhoneNumber": "+49 \/ 800 \/ 7234070",
 "carTypes": {
 "count": 4,
 "items": [
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/mini.png",
 "group": "MINI",
 "make": "BMW",
 "modelIdentifier": "mini",
 "modelName": "MINI",
 "series": "MINI",
 "variant": ""
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/bmw_1er.png",
 "group": "BMW",
 "make": "BMW",
 "modelIdentifier": "bmw_1er",
 "modelName": "BMW 1er",
 "series": "1er",
 "variant": ""
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/for_transit_100_d.png",
 "group": "FOR",
 "make": "FOR",
 "modelIdentifier": "for_transit_100_d",
 "modelName": "FOR TRANSIT 100 D",
 "series": "TRANSIT 100",
 "variant": "D"
 },
 {
 "carImageUrl": "https:\/\/de.drive-now.com\/static\/drivenow\/img\/cars\/for_transit_80_d.png",
 "group": "FOR",
 "make": "FOR",
 "modelIdentifier": "for_transit_80_d",
 "modelName": "FOR TRANSIT 80 D",
 "series": "TRANSIT 80",
 "variant": "D"
...

Doch, ist es! Und es ist spannend, denn die letzten beiden Fahrzeugtypen kommen doch eher überraschend. Ob das als Ankündigung zu verstehen ist oder die Entwickler eine merkwürdige Auswahl an Test-Daten getroffen haben bleibt aber natürlich offen.

Der Fairness halber habe ich Drive-Now vor > 2 Wochen auf ihr eigenes „Überangebot“ an APIs hingewiesen. Trotz Versprechen das an die Technik weiterzuleiten, ist (trotz zwischenzeitlichem Javascript-Code-Update auf der Seite) aber bisher alles weiterhin frei zugänglich.

Meine Anfrage nach einem eigenen API-Key wurde außerdem leider ohne weitere Begründung abgelehnt.