API-Nutzung
Inhaltsverzeichnis
Diese Anleitung erklärt, wie Sie eine App erstellen, einen API-Token generieren und damit auf die Transit-Daten-API zugreifen.
Überblick
Die API verwendet Bearer-Token-Authentifizierung. Um auf Daten zuzugreifen, benötigen Sie:
Eine App — von einem Admin im CMS angelegt
Einen API-Token — für diese App generiert, mit bestimmten Berechtigungen (Scopes)
Tokens haben das Format trtok_<zufällig> und werden nur einmal bei der
Erstellung angezeigt.
Schritt 1: App erstellen
Nur Benutzer mit der Rolle Admin oder Super-Admin können Apps erstellen.
Melden Sie sich im CMS-Adminbereich an.
Navigieren Sie zu System → Apps in der Seitenleiste.
Klicken Sie auf Neu erstellen.
Geben Sie einen Namen für die App ein (z.B. „Mein Dashboard").
Wählen Sie die Organisation, zu der die App gehört.
Wählen Sie unter Scopes die Datenbereiche aus, auf die die App zugreifen darf (z.B.
elevators:read,stop_places:read). Falls Sie keine Scopes auswählen, werden die Standardwerteelevators:read,status_spans:readundstop_places:readgesetzt.Klicken Sie auf Speichern.
Beim Speichern einer neuen App wird automatisch ein Standard-API-Token erstellt.
Schritt 2: API-Token kopieren
Nach dem Speichern wird automatisch ein Standard-Token erstellt. Sie können auch zusätzliche Tokens anlegen:
Öffnen Sie die App im CMS.
Klicken Sie auf API-Token erstellen.
Geben Sie einen Namen für den Token ein (z.B. „Produktion v1").
Optional: Schränken Sie den Token auf eine Teilmenge der App-Scopes ein.
Klicken Sie auf Erstellen.
Kopieren Sie den Token-Wert sofort — er wird nur einmal angezeigt und kann danach nicht mehr abgerufen werden.
Der Token-Wert sieht beispielsweise so aus:
trtok_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u1V2Schritt 3: Token verwenden
Fügen Sie den Token als Bearer-Token im Authorization-Header Ihrer
HTTP-Anfragen hinzu.
curl
curl -H "Authorization: Bearer trtok_IHR_TOKEN_HIER" \
"https://transit.accessibility.cloud/api/elevators?limit=10"Node.js (fetch)
const response = await fetch(
"https://transit.accessibility.cloud/api/elevators?limit=10",
{
headers: { Authorization: "Bearer trtok_IHR_TOKEN_HIER" },
},
);
const data = await response.json();
console.log(data);Python
import requests
response = requests.get(
"https://transit.accessibility.cloud/api/elevators?limit=10",
headers={"Authorization": "Bearer trtok_IHR_TOKEN_HIER"},
)
data = response.json()
print(data)Verfügbare API-Collections
Die API folgt den REST-Konventionen von Payload CMS. Daten-Collections, die Sie abfragen können:
Collection | Endpunkt | Typischer Scope |
|---|---|---|
Aufzüge |
|
|
Status-Zeiträume |
|
|
Haltestellen |
|
|
Kartenmarker |
|
|
Netzpläne |
|
|
GTFS-Haltestellen |
|
|
GTFS-Linien |
|
|
Abfrageparameter
Alle Collections unterstützen Standard-Payload-Abfrageparameter:
limit— Anzahl der Ergebnisse pro Seite (Standard: 10). Bei größeren Werten bzw. wenn alle Einträge aufgelistet werden sollen, empfiehlt sich eine Anfrage nach einem minimalen Set bestimmter Felder (sieheselectin der folgenden Liste), um die Antworten klein zu halten.page— Seitennummersort— Feldname zum Sortieren (Prefix-für absteigend)where— Filterbedingungen (siehe Beispiele unten)depth— Wie viele Ebenen verknüpfter Dokumente aufgelöst werden (Standard: 1)populate[…][…]=true— Löst verknüpfte Dokumente mit bestimmten Feldnamen aufselect[…][…]=true— Bestimmt eine eigene Menge von Feldern, deren Werte in der Antwort enthalten sein sollen
Filterbeispiel — Aufzüge, die außer Betrieb sind:
/api/elevators?where[operational_status.operational_status][equals]=out_of_service&limit=5Sortierbeispiel — Zuletzt aktualisierte Haltestellen:
/api/stop-places?sort=-updatedAt&limit=20Filtern mit where
Der Parameter where filtert Ergebnisse nach Feldwerten. In der URL steht der
Feldpfad im ersten Klammerpaar und der Operator im zweiten:
where[<Feldpfad>][<Operator>]=<Wert>Feldpfade können mit Punktnotation in Gruppen und verknüpfte Dokumente hineinreichen — siehe Verschachtelte Feldpfade unten.
Vergleichsoperatoren
Operator | Bedeutung |
|---|---|
| Wert muss exakt übereinstimmen |
| Wert darf nicht übereinstimmen |
| Für Zahlen- oder Datumsfelder |
| Für Zahlen- oder Datumsfelder |
| Für Zahlen- oder Datumsfelder |
| Für Zahlen- oder Datumsfelder |
| Ignoriert Groß-/Kleinschreibung. Bei mehreren Wörtern müssen alle Wörter vorkommen, in beliebiger Reihenfolge |
| Gegenteil von |
| Wert muss als Teilzeichenkette enthalten sein, ignoriert Groß-/Kleinschreibung |
| Wert muss in einer kommagetrennten Liste vorkommen |
| Wert darf nicht in einer kommagetrennten Liste vorkommen |
| Feld hat einen Wert ( |
| Nur Point-Felder — siehe Geo-Abfragen |
| Nur Point-Felder — Punkt liegt innerhalb einer GeoJSON-Geometrie |
| Nur Point-Felder — Punkt schneidet eine GeoJSON-Geometrie |
Beispiele:
Aufzüge, deren Status nicht in_service ist (also außer Betrieb oder
unbekannt). Das Feld operational_status ist ein Enum mit genau drei Werten:
in_service, out_of_service und unknown.
/api/elevators?where[operational_status.operational_status][not_equals]=in_serviceAufzüge, die außer Betrieb oder unbekannt sind (in erwartet eine
kommagetrennte Liste):
/api/elevators?where[operational_status.operational_status][in]=out_of_service,unknownHaltestellen, die nach einem bestimmten Datum aktualisiert wurden (ISO 8601):
/api/stop-places?where[updatedAt][greater_than]=2026-01-01T00:00:00.000ZHaltestellen, deren Name eine Zeichenkette enthält (ignoriert Groß-/Kleinschreibung):
/api/stop-places?where[internal_description][contains]=alexanderplatzAufzüge, die eine Geoposition haben:
/api/elevators?where[location.centroid][exists]=trueBedingungen kombinieren: and / or
Mehrere where-Bedingungen auf verschiedenen Feldern werden standardmäßig mit
UND verknüpft:
/api/elevators?where[operational_status.operational_status][equals]=out_of_service&where[updatedAt][greater_than]=2026-01-01Für explizite Bool'sche Logik verwenden Sie and-/or-Arrays mit numerischen
Indizes. Diese Abfrage findet Aufzüge, die außer Betrieb oder unbekannt
sind:
/api/elevators?where[or][0][operational_status.operational_status][equals]=out_of_service&where[or][1][operational_status.operational_status][equals]=unknownand und or können beliebig verschachtelt werden. Der obige Query-String
entspricht dieser JSON-Struktur:
{
"or": [
{ "operational_status.operational_status": { "equals": "out_of_service" } },
{ "operational_status.operational_status": { "equals": "unknown" } }
]
}Verschachtelte Feldpfade
Mit Punktnotation fragen Sie Felder innerhalb von Gruppen oder in verknüpften Dokumenten ab:
operational_status.operational_status— ein Feld innerhalb einer Gruppefeed.name— ein Feld eines verknüpften Dokuments (die API löst die Verknüpfung automatisch auf)
/api/elevators?where[feed.name][contains]=bvgGeo-Abfragen auf Point-Feldern
Collections mit Point-Feldern (z.B. centroid bei Haltestellen,
location.centroid bei Aufzügen) unterstützen geografische Operatoren:
near— Wert ist<Längengrad>,<Breitengrad>,<maxDistanzMeter>,<minDistanzMeter>(die letzten beiden sind optional). Ergebnisse werden nach Entfernung sortiert.within/intersects— Wert ist eine GeoJSON-Geometrie; diese senden Sie am einfachsten über einen generierten Query-String (siehe unten).
Beispiel — Haltestellen im Umkreis von 1 km um den Alexanderplatz:
/api/stop-places?where[centroid][near]=13.4114,52.5219,1000Komplexe Query-Strings in JavaScript erzeugen
Klammer-Syntax von Hand zu schreiben wird bei größeren Abfragen fehleranfällig.
Verwenden Sie das Paket qs-esm, um
ein Abfrageobjekt in eine URL umzuwandeln:
import { stringify } from "qs-esm";
const query = {
or: [
{ "operational_status.operational_status": { equals: "out_of_service" } },
{ "operational_status.operational_status": { equals: "unknown" } },
],
};
const queryString = stringify(
{ where: query, limit: 10, depth: 1 },
{ addQueryPrefix: true },
);
const response = await fetch(
`https://transit.accessibility.cloud/api/elevators${queryString}`,
{ headers: { Authorization: "Bearer trtok_IHR_TOKEN_HIER" } },
);Performance-Tipps
Filtern und sortieren Sie möglichst auf indizierten Feldern (IDs, Slugs, Statusfelder, Zeitstempel).
Verwenden Sie
select[]und eine niedrigedepth, um Antworten klein zu halten.Bevorzugen Sie Paginierung mit
limit+page, statt alles auf einmal abzurufen.
Ausgearbeitete Beispiele für diese Muster — Eingrenzung auf ein Verkehrsnetz, Auswahl nur der von der Oberfläche benötigten Felder und lokales Zwischenspeichern von Antworten — finden Sie unter Beispiele: Die API effizient nutzen.
Nur veröffentlichte Daten abrufen (empfohlene Vorgaben)
Um zuverlässig nur veröffentlichte Datensätze zu erhalten, empfehlen wir, folgende Parameter als Standard zu verwenden:
draft=false&locale=de&trash=false&_status=published
draft=false— nur veröffentlichte Versionen, keine Entwürfelocale=de— Sprache lokalisierter Felder (deoderen)trash=false— gelöschte (Papierkorb-)Datensätze ausschließen_status=published— explizit auf den Veröffentlichungsstatus filtern
Feldauswahl mit select[]
Mit select[] begrenzen Sie die Antwort auf genau die Felder, die Sie benötigen.
Jede Pfadebene wird in eckige Klammern gesetzt; ein Feld wird mit =true
eingeschlossen. Das Feld id wird immer zurückgegeben und muss nicht ausgewählt
werden.
Beispiel — nur id, die Funktion (function.short_visual) und den Status
(operational_status.operational_status) einer Aufzugsliste abrufen:
/api/elevators?draft=false&locale=de&trash=false&_status=published&select[function][short_visual]=true&select[operational_status][operational_status]=true&limit=10Verknüpfte Felder auflösen: depth und populate[]
depth— bestimmt, wie viele Ebenen verknüpfter (Relationship-)Dokumente aufgelöst werden (Standard: 1). Mitdepth=0werden nur die IDs verknüpfter Dokumente geliefert.populate[]— steuert feingranular, welche Felder verknüpfter Dokumente geladen werden. In Kombination mitselect[]laden Sie nur die Felder, die Sie benötigen – auch aus tieferen Ebenen.
Beispiel — StopPlaces schlank abrufen und nur die gewünschten Aufzugsfelder auflösen:
/api/stop-places?draft=false&locale=de&trash=false&_status=published&depth=1&select[main_identifier]=true&select[internal_description]=true&select[normalized_names][tokenized_name_db]=true&select[operational_status][elevators]=true&populate[elevators][operational_status][operational_status]=true&populate[elevators][operational_status][status_spans]=true&populate[elevators][function]=true&populate[elevators][feed]=true&populate[elevators][linked_data][owner_org]=true&populate[elevators][linked_data][original_data]=true&limit=10Join-Felder
Ein Join-Feld ist eine Rückwärtsbeziehung — die Dokumente auf der Gegenseite,
die auf dieses Dokument verweisen. Haltestellen legen ihre Kartenmarker auf diese
Weise über linked_data.map_markers offen. Ein Join liefert ein paginiertes
Objekt, kein flaches Array:
"linked_data": {
"map_markers": {
"docs": [ /* Kartenmarker-Objekte, bei depth=0 nur IDs */ ],
"hasNextPage": false
}
}depth steuert, ob ein Join zu Objekten aufgelöst wird:
depth=0—docsenthält die reinen Marker-IDs (z. B.[107]).depth=1(Standard) oder höher —docsenthält die vollständigen Marker-Objekte (latitude,longitude,map, …).
Beispiel — die Kartenmarker einer Haltestelle mit Koordinaten und Netzplan-ID abrufen:
curl -H "Authorization: Bearer trtok_YOUR_TOKEN_HERE" \
"https://transit.accessibility.cloud/api/stop-places/42?depth=1&select[linked_data][map_markers]=true"{
"id": 42,
"linked_data": {
"map_markers": {
"docs": [
{
"id": 107,
"name": "Alexanderplatz",
"subject": { "relationTo": "stop-places", "value": 42 },
"map": 5,
"coordinate_mode": "world",
"latitude": 52.5219,
"longitude": 13.4114
}
],
"hasNextPage": false
}
}
}Hinweise:
mapist die Netzplan-ID. Über den Join bleibt sie bei jedemdeptheine ID, da Relationships innerhalb von gejointen Dokumenten nicht weiter aufgelöst werden. Für das Netzplan-Objekt fragen Sie die Marker direkt beidepth=1ab —GET /api/map-markers?where[subject.value][equals]=42— dort wirdmapje Marker zum vollständigen Objekt aufgelöst — oder rufen SieGET /api/network-maps/5ab.Eine Relationship wird nur dann zu einem Objekt aufgelöst, wenn
depthhoch genug ist und die Scopes des Tokens Lesezugriff auf die Ziel-Collection gewähren; sonst bleibt die ID stehen.Die Anzahl der von einem Join gelieferten Dokumente ist serverseitig begrenzt;
hasNextPagezeigt eine Abschneidung an. (Der Payload-Parameterjoinshat auf dieser API keine Wirkung.)Um die Haltestelle statt über die numerische ID anhand der DHID zu suchen, nutzen Sie den List-Endpunkt mit
?where[main_identifier][equals]=de:11000:…&limit=1.
Die Feldreferenz zum Kartenmarker finden Sie unter Kartenmarker.
Antwortformat
API-Antworten folgen einer einheitlichen Struktur:
{
"docs": [
{
"id": 1,
"name": "Aufzug Alexanderplatz",
"status": "active",
"updatedAt": "2025-03-15T10:30:00.000Z"
}
],
"totalDocs": 42,
"limit": 10,
"totalPages": 5,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": true,
"prevPage": null,
"nextPage": 2
}Kartenmarker
Ein Kartenmarker platziert ein Subjekt — eine Haltestelle, einen Aufzug, eine Status-Spanne oder eine Organisation — an einer Position auf einem bestimmten Netzplan. Jeder Marker trägt eigene Koordinaten und eine Referenz auf den zugehörigen Netzplan.
Feld | Typ | Beschreibung |
|---|---|---|
| number | Marker-ID |
| string | Automatisch erzeugtes Label (schreibgeschützt) |
| relationship | Polymorph — die Entität, auf die der Marker verweist: |
| relationship | Der Netzplan, zu dem dieser Marker gehört ( |
|
| Bestimmt die Interpretation der Koordinaten (siehe unten) |
| number | Breitengrad (im Modus |
| number | Längengrad (im Modus |
Koordinatenmodi:
world(Standard) —latitude/longitudesind geografische Koordinaten im Koordinatenreferenzsystem des referenzierten Netzplans.display—latitude/longitudestehen für den Y-/X-Pixelversatz innerhalb der Anzeigefläche des Netzplans (dessendisplay_coordinatesBreite × Höhe), skaliert auf das gerenderte Bild.
Endpunkte:
GET /api/map-markers # Liste (paginiert)
GET /api/map-markers/{id} # einzelner MarkerFilterbeispiele:
Alle Marker, die zu einem bestimmten Netzplan gehören:
/api/map-markers?where[map][equals]=5Alle Marker, deren Subjekt eine bestimmte Haltestelle ist. subject ist
polymorph, daher wird auf subject.value (die ID) gefiltert, optional
eingeschränkt über subject.relationTo:
/api/map-markers?where[subject.value][equals]=42&where[subject.relationTo][equals]=stop-places(Ein direkter Filter auf subject — where[subject][equals]=… — wird nicht
unterstützt und liefert einen Fehler.)
Um die Kartenmarker einer Haltestelle zusammen mit der Haltestelle in einer einzigen Anfrage abzurufen, nutzen Sie stattdessen den Join linked_data.map_markers — siehe Join-Felder.
Scope: map_markers:read.
Berechtigungs-Scopes
Jeder API-Token hat einen oder mehrere Scopes, die bestimmen, auf welche Collections er zugreifen kann. Schreib-Scopes beinhalten automatisch Lesezugriff.
Scope | Zugriff |
|---|---|
| Aufzugsdaten lesen |
| Status-Zeiträume lesen |
| Haltestellendaten lesen |
| Kartenmarker lesen |
| Netzpläne lesen |
| Alle GTFS-Collections lesen |
| Feed-Definitionen lesen |
| Organisationsdaten lesen |
Token-Verwaltung
Token neu generieren
Falls ein Token kompromittiert wurde, können Sie ihn neu generieren:
Öffnen Sie den API-Token im CMS.
Klicken Sie auf Token neu erzeugen.
Bestätigen Sie die Aktion — der alte Token funktioniert sofort nicht mehr.
Kopieren Sie den neuen Token-Wert.
Token löschen
Löschen Sie das API-Token-Dokument im CMS. Der Token wird sofort widerrufen.
Fehlerantworten
Statuscode | Bedeutung |
|---|---|
| Fehlender Token, ungültiger Token oder Token ohne die erforderliche Berechtigung |
| Ressource existiert nicht |
| Ungültige Abfrageparameter |
Hinweis: Ein fehlender oder ungültiger Token liefert 403 (nicht 401) — die API behandelt jede nicht authentifizierte Anfrage als „forbidden".
Sicherheitshinweise
Tokens werden vor der Speicherung gehasht (SHA-256) — wir speichern niemals Klartext-Tokens.
Übertragen Sie Tokens immer über HTTPS.
Verwenden Sie die restriktivsten Scopes, die für jede App möglich sind.
Erneuern Sie Tokens, wenn Sie einen Leak vermuten.