Comprehensive API documentation for the Moovit backend API
Version: 5.151.2/V567 Last Updated: February 2026
- Overview
- Base URL
- Authentication & Headers
- Testing with cURL
- Endpoints
- Data Types
- Error Handling
- Examples
The Moovit API provides access to public transit information, route planning, real-time arrivals, and location search functionality. The API uses a combination of REST endpoints with JSON and Protocol Buffers (protobuf) data formats.
- Multi-modal route planning (transit, walking, biking, taxi)
- Real-time transit arrivals and vehicle locations
- Transit line and stop information
- Location search and geocoding
- Service alerts and disruptions
- Shared mobility integration (scooters, bikes)
https://moovitapp.com/api
All endpoints are relative to this base URL.
All API requests must include the following headers:
| Header | Value | Description |
|---|---|---|
moovit_app_type |
WEB_TRIP_PLANNER |
Application type identifier |
moovit_client_version |
5.151.2/V567 |
Client version string |
moovit_customer_id |
4908 |
Customer identifier |
moovit_metro_id |
1 |
Metro area ID (1 = Israel) |
moovit_phone_type |
2 |
Phone type identifier |
moovit_user_key |
String | User identifier (e.g., F36627) |
moovit_gtfs_language |
EN |
Language code (EN, HE, etc.) |
x-aws-waf-token |
String | AWS WAF token (required for direct API access) |
| Header | Value | Description |
|---|---|---|
protobuf-version |
V3 |
Required for protobuf endpoints |
accept |
application/json or application/x-protobuf |
Response format |
The API is protected by AWS WAF. Direct API access requires a valid x-aws-waf-token obtained by completing the browser JavaScript challenge. This token is time-limited and must be refreshed periodically.
Note: The WAF token is automatically generated when accessing the API through a browser. For programmatic access, you'll need to implement the AWS WAF challenge flow or use browser automation.
The API uses AWS WAF protection. To authenticate with cURL, you need to pass the session cookies from a browser session:
- Open https://moovitapp.com/tripplan in your browser
- Open DevTools → Application → Cookies
- Copy all cookies, especially
aws-waf-token
The following examples have been tested and verified working.
curl -s "https://moovitapp.com/api/alert" \
-H "accept: application/json" \
-H "moovit_app_type: WEB_TRIP_PLANNER" \
-H "moovit_client_version: 5.151.2/V567" \
-H "moovit_customer_id: 4908" \
-H "moovit_metro_id: 1" \
-H "moovit_phone_type: 2" \
-H "moovit_user_key: F36627" \
-H "moovit_gtfs_language: EN" \
-H "Cookie: aws-waf-token=YOUR_TOKEN; mv_metro=1; mv_lang=en" \
-H "Referer: https://moovitapp.com/tripplan/"
# Response: {"data":[]}curl -s "https://moovitapp.com/api/image?ids=13560,291729" \
-H "accept: application/json" \
-H "moovit_app_type: WEB_TRIP_PLANNER" \
-H "moovit_client_version: 5.151.2/V567" \
-H "moovit_customer_id: 4908" \
-H "moovit_metro_id: 1" \
-H "moovit_phone_type: 2" \
-H "moovit_user_key: F36627" \
-H "moovit_gtfs_language: EN" \
-H "Cookie: aws-waf-token=YOUR_TOKEN"
# Response: [{"entity":{"image":{"imageId":13560,"imageData":"iVBORw0KGgo...","imageType":1}}}]# Step 1: Get routing token
curl -s "https://moovitapp.com/api/route/search?tripPlanPref=2&time=$(date +%s)000&timeType=2&isCurrentTime=true&routeTypes=3,5,4,7,6,2,1,0&routeTransportOptions=1,5&fromLocation_id=0&fromLocation_type=6&fromLocation_latitude=31789130&fromLocation_longitude=35203210&fromLocation_caption=Jerusalem&toLocation_id=0&toLocation_type=6&toLocation_latitude=32075369&toLocation_longitude=34775131&toLocation_caption=TelAviv" \
-H "accept: application/json" \
-H "moovit_app_type: WEB_TRIP_PLANNER" \
-H "moovit_client_version: 5.151.2/V567" \
-H "moovit_customer_id: 4908" \
-H "moovit_metro_id: 1" \
-H "moovit_phone_type: 2" \
-H "moovit_user_key: F36627" \
-H "moovit_gtfs_language: EN" \
-H "Cookie: aws-waf-token=YOUR_TOKEN"
# Response: {"token":"1770209050880,0c13331e-feda-4355-9d5c-5a13c2f60a1b"}
# Step 2: Get route results
curl -s "https://moovitapp.com/api/route/result?token=YOUR_TOKEN_FROM_STEP1&offset=0" \
-H "accept: application/json" \
-H "moovit_app_type: WEB_TRIP_PLANNER" \
-H "moovit_client_version: 5.151.2/V567" \
-H "moovit_customer_id: 4908" \
-H "moovit_metro_id: 1" \
-H "moovit_phone_type: 2" \
-H "moovit_user_key: F36627" \
-H "moovit_gtfs_language: EN" \
-H "Cookie: aws-waf-token=YOUR_TOKEN"
# Response includes: tripPlanSections, itineraries with legs (transit, taxi, walking, biking)curl -s -X POST "https://moovitapp.com/api/lines/linesarrival" \
-H "accept: application/json" \
-H "content-type: application/json" \
-H "moovit_app_type: WEB_TRIP_PLANNER" \
-H "moovit_client_version: 5.151.2/V567" \
-H "moovit_customer_id: 4908" \
-H "moovit_metro_id: 1" \
-H "moovit_phone_type: 2" \
-H "moovit_user_key: F36627" \
-H "moovit_gtfs_language: EN" \
-H "Cookie: aws-waf-token=YOUR_TOKEN" \
-d '{"params":{"lineStopPairs":[{"lineId":6623410,"stopId":72956}]}}'
# Response: [{"stopId":72956,"epochDay":20488,"lineArrivals":{"lineId":6623410,"arrivals":[...]},"nextPollingIntervalSecs":20}]| Endpoint | Method | Status |
|---|---|---|
/api/alert |
GET | ✅ Working |
/api/image |
GET | ✅ Working |
/api/route/search |
GET | ✅ Working |
/api/route/result |
GET | ✅ Working |
/api/lines/linesarrival |
POST | ✅ Working |
/api/lines/linearrival |
POST | ✅ Working |
/api/nearby/stops |
GET | |
/api/location |
POST | |
/api/lines/search |
POST |
Note: Protobuf endpoints (marked fetch() for these endpoints.
Endpoint: GET /api/route/search
Initiates a route planning search between two locations. Returns a token for polling results.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
tripPlanPref |
integer | Yes | Trip planning preference (2 = balanced) |
time |
long | Yes | Unix timestamp in milliseconds |
timeType |
integer | Yes | Time type (2 = departure) |
isCurrentTime |
boolean | Yes | Whether using current time |
routeTypes |
string | Yes | Comma-separated route types (e.g., 3,5,4,7,6,2,1,0) |
routeTransportOptions |
string | Yes | Transport options (e.g., 1,5) |
fromLocation_id |
integer | Yes | Origin location ID (0 for coordinates) |
fromLocation_type |
integer | Yes | Origin type (6 = coordinate, 4 = stop) |
fromLocation_latitude |
integer | Yes | Origin latitude × 1,000,000 |
fromLocation_longitude |
integer | Yes | Origin longitude × 1,000,000 |
fromLocation_caption |
string | Yes | URL-encoded origin name |
toLocation_id |
integer | Yes | Destination location ID |
toLocation_type |
integer | Yes | Destination type |
toLocation_latitude |
integer | Yes | Destination latitude × 1,000,000 |
toLocation_longitude |
integer | Yes | Destination longitude × 1,000,000 |
toLocation_caption |
string | Yes | URL-encoded destination name |
Example Request:
GET /api/route/search?tripPlanPref=2&time=1770206919097&timeType=2&isCurrentTime=true&routeTypes=3,5,4,7,6,2,1,0&routeTransportOptions=1,5&fromLocation_id=0&fromLocation_type=6&fromLocation_latitude=32228394&fromLocation_longitude=35246259&fromLocation_caption=Yaffa%20Street%2030,%20jerusalem&toLocation_id=0&toLocation_type=6&toLocation_latitude=32075487&toLocation_longitude=34775489&toLocation_caption=Dizengoff%20CenterResponse:
{
"token": "1770206919266,8176e233-1a58-412e-9c7e-aa904dbf2624"
}Response Format: application/x-protobuf
Endpoint: GET /api/route/result
Retrieves route planning results using the token from the search endpoint.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
string | Yes | Token from /route/search |
offset |
integer | Yes | Pagination offset (start with 0) |
Example Request:
GET /api/route/result?token=1770206919266,8176e233-1a58-412e-9c7e-aa904dbf2624&offset=0Response:
{
"results": [
{
"result": {
"tripPlanSections": {
"tripPlanSections": [
{
"name": "Suggested Routes",
"sectionId": 1,
"maxItemsToDisplay": 2,
"sectionType": 16
},
{
"name": "Taxi & Ride Hailing",
"sectionId": 1561,
"maxItemsToDisplay": 1,
"sectionType": 1
}
]
}
}
},
{
"result": {
"itinerary": {
"guid": "202602043C741EF912FD4CBD94B90C3F7E517C50:0",
"sectionId": 1561,
"groupType": 4,
"legs": [
{
"waitToTaxiLeg": {
"time": {
"startTimeUtc": 1770206919000,
"endTimeUtc": 1770207159000,
"isRealTime": 0
},
"waitAtLocation": {
"caption": "Yaffa Street 30, jerusalem",
"latlon": {
"latitude": 32228394,
"longitude": 35246259
}
},
"approxWaitingSecFromOrdering": 240,
"taxiId": 11
}
},
{
"taxiLeg": {
"time": {
"startTimeUtc": 1770207159000,
"endTimeUtc": 1770212051000
},
"journey": {
"origin": {
"caption": "Yaffa Street 30, jerusalem",
"latlon": {
"latitude": 32228394,
"longitude": 35246259
}
},
"dest": {
"caption": "Dizengoff Center",
"latlon": {
"latitude": 32075487,
"longitude": 34775489
}
}
},
"shape": {
"distanceInMeters": 64577.23564752191,
"polyline": "mrucEc`cvEaEbDqAdA..."
},
"taxiProviderName": "GetTaxi",
"deepLinks": {
"androidDeepLink": "https://...",
"iosDeepLink": "https://..."
}
}
}
]
}
}
}
],
"completed": 0
}Response Format: application/json
Notes:
- Poll this endpoint with increasing
offsetvalues to get all results completed: 0means more results may be available- Results include transit routes, walking routes, taxi options, and bike routes
Endpoint: POST /api/lines/search
Search for transit lines by name or number.
Request Body:
{
"query": "base64_encoded_protobuf_data"
}Headers Required:
Content-Type: application/jsonAccept: application/x-protobufprotobuf-version: V3
Response: Protocol Buffers format containing line search results
Endpoint: POST /api/lines/linearrival
Get real-time arrivals for a specific line at a stop.
Request Body:
{
"stopId": 46034316,
"lineIds": "{\"ids\":[7286381]}"
}Response:
[
{
"stopId": 46034316,
"lineId": 7286381,
"arrivals": [
{
"tripId": 3713592875,
"staticEtdUTC": 1770207479000,
"rtEtdUTC": 1770207503850,
"durationInSeconds": 1225,
"vehicleLocation": {
"latlon": {
"latitude": 32103446,
"longitude": 35189738
},
"vehicleId": "29775_2026-02-04T14:00:00+02:00_74768502",
"vehicleSampleTimeUtc": 1770206887000,
"vehicleStatus": 1
},
"arrivalCertainty": 1
}
]
}
]Response Format: application/json
Endpoint: POST /api/lines/linesarrival
Get real-time arrivals for multiple line/stop pairs in a single request.
Request Body:
{
"params": {
"lineStopPairs": [
{
"lineId": 6623410,
"stopId": 72956
},
{
"lineId": 6007876,
"stopId": 73017
},
{
"lineId": 6007820,
"stopId": 73017
}
]
}
}Response:
[
{
"stopId": 72956,
"epochDay": 20488,
"lineArrivals": {
"lineId": 6623410,
"arrivals": [
{
"patternId": 15761671,
"tripId": 3713592875,
"staticEtdUTC": 1770207479000,
"rtEtdUTC": 1770207503850,
"isLastArrival": 0,
"durationInSeconds": 1225,
"vehicleLocation": {
"latlon": {
"latitude": 32103446,
"longitude": 35189738
},
"progress": {
"nextStopIndex": 8,
"progress": 56
},
"vehicleId": "29775_2026-02-04T14:00:00+02:00_74768502",
"vehicleSampleTimeUtc": 1770206887000,
"vehicleStatus": 1,
"locationSource": 1
},
"stopIndex": 18,
"patternStopsSize": 36,
"arrivalCertainty": 1,
"trafficStatus": 1
}
]
},
"nextPollingIntervalSecs": 20
}
]Response Format: application/json
Fields:
staticEtdUTC: Scheduled departure time (Unix timestamp in ms)rtEtdUTC: Real-time estimated departure time (Unix timestamp in ms)durationInSeconds: Trip duration in secondsarrivalCertainty: 1 = high certainty, 2 = medium certaintytrafficStatus: 1 = normal, 2 = slow, 3 = heavy trafficvehicleStatus: 1 = in service, 2 = stoppednextPollingIntervalSecs: Recommended polling interval
Endpoint: GET /api/lines/agency_order
Get the ordering of transit agencies for display purposes.
Response: JSON array of agency information
Endpoint: GET /api/lines/agency
Get detailed information about transit agencies.
Response: JSON with agency details
Endpoint: POST /api/lines/grouptrips
Get grouped trips for a line (useful for schedule display).
Request Body:
{
"query": "base64_encoded_protobuf_data"
}Response: Protocol Buffers format
Endpoint: GET /api/nearby/stops
Get transit stops in a tile area.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
tilesLatLngs |
string | Yes | Tile coordinates (e.g., 3520,3178) |
revision |
long | Yes | Revision timestamp |
metroId |
integer | Yes | Metro area ID |
Example Request:
GET /api/nearby/stops?tilesLatLngs=3517,3169&revision=1770106762961&metroId=1Headers Required:
Accept: application/x-protobufprotobuf-version: V3
Response: Protocol Buffers format containing stop information
Tile Coordinate System:
- The API uses a tile-based coordinate system
- Tiles are calculated from latitude/longitude
- Multiple tiles can be requested in a single call (comma-separated)
Endpoint: GET /api/nearby/microMobility
Get micro-mobility options (scooters, bikes) in a tile area.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
metroId |
integer | Yes | Metro area ID |
tilesLatLngs |
string | Yes | Tile coordinates |
customerKey |
string | Yes | Customer key (e.g., moovit) |
Example Request:
GET /api/nearby/microMobility?metroId=1&tilesLatLngs=3517,3169&customerKey=moovitResponse: JSON with available scooters and bikes
Endpoint: GET /api/nearby/stopsAdditionalData
Get additional data for specific stops (lines, departures, etc.).
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
stopsIds |
string | Yes | Comma-separated stop IDs |
metroId |
integer | Yes | Metro area ID |
getLineDepartures |
boolean | No | Include line departures |
Example Request:
GET /api/nearby/stopsAdditionalData?stopsIds=72956,73017,289862597&metroId=1&getLineDepartures=trueResponse: JSON with stop details
Endpoint: GET /api/alert
Get active service alerts for the metro area.
Response:
{
"data": []
}Response Format: application/json
Returns an array of active alerts. Empty array when no alerts.
Endpoint: GET /api/alert/entities
Get alert entities (affected stops, lines, etc.).
Response: JSON with alert entity mappings
Endpoint: GET /api/alert/metro
Get metro-level service alerts.
Response: JSON with metro-wide alerts
Endpoint: GET /api/alert/getAlertDetails/{alertId}/{lang}
Get detailed information for a specific alert.
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
alertId |
integer | Yes | Alert identifier |
lang |
string | Yes | Language code (e.g., en, he) |
Example Request:
GET /api/alert/getAlertDetails/137156680/enResponse: JSON with alert details including title, description, affected routes, and time range
Endpoint: POST /api/location
Search for locations, addresses, and POIs (autocomplete).
Request Body:
{
"query": "base64_encoded_protobuf_data"
}Headers Required:
Content-Type: application/jsonAccept: application/x-protobufprotobuf-version: V3
Response: Protocol Buffers format with location suggestions
Notes:
- Used for autocomplete in search boxes
- Returns POIs, addresses, transit stops, and landmarks
- The query is encoded as base64 protobuf data
Endpoint: GET /api/image
Get images for transit lines, agencies, and other entities.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
ids |
string | Yes | Comma-separated image IDs |
Example Request:
GET /api/image?ids=13560,291729,13796Response:
[
{
"id": 13560,
"imageData": "base64_encoded_image_data",
"mimeType": "image/png"
},
{
"id": 291729,
"imageData": "base64_encoded_image_data",
"mimeType": "image/png"
}
]Response Format: application/json
Notes:
- Images are returned as base64-encoded data
- Common image types: line icons, agency logos, POI images
Endpoint: POST /api/share/itinerary
Share a route/itinerary and get a shareable link.
Request Body:
{
"itinerary": {
"guid": "202602043C741EF912FD4CBD94B90C3F7E517C50:0",
"legs": [...],
"from": {...},
"to": {...}
}
}Response:
{
"shareUrl": "https://moovitapp.com/share/...",
"shareId": "abc123"
}Response Format: application/json
Notes:
- Request body can be large (20-30KB) as it includes full itinerary data
- Returns a short URL for sharing via social media, SMS, etc.
Endpoint: POST /api/debug/dump
Client-side debug logging endpoint.
Request Body:
{
"logs": [
{
"when": "2026-02-04T12:08:40.000Z",
"message": "Log message"
}
],
"times": {}
}Response: 200 OK
Notes:
- Used by the Moovit web client for error reporting
- Not typically used by third-party integrations
| Value | Type | Description |
|---|---|---|
| 0 | Unknown | Unknown location type |
| 4 | Stop | Transit stop/station |
| 6 | Coordinate | Latitude/longitude coordinate or POI |
| Value | Type | Description |
|---|---|---|
| 0 | Bus | Bus routes |
| 1 | Light Rail | Light rail/tram |
| 2 | Train | Heavy rail/train |
| 3 | Walking | Walking routes |
| 4 | Biking | Bicycle routes |
| 5 | Taxi | Taxi/ride-hailing |
| 6 | Ferry | Ferry routes |
| 7 | Scooter | E-scooter/micro-mobility |
| Value | Type | Description |
|---|---|---|
| 1 | Arrival | Arrive by time |
| 2 | Departure | Depart at time |
| Value | Level | Description |
|---|---|---|
| 1 | High | High confidence in ETA |
| 2 | Medium | Medium confidence in ETA |
| 3 | Low | Low confidence (schedule-based) |
| Value | Status | Description |
|---|---|---|
| 1 | Normal | Normal traffic conditions |
| 2 | Slow | Slow traffic |
| 3 | Heavy | Heavy traffic/delays |
Coordinates in the API are represented as integers:
API Value = Real Value × 1,000,000
Example:
- Real latitude:
32.228394 - API latitude:
32228394
To convert:
// To API format
const apiLat = Math.round(realLat * 1000000);
// From API format
const realLat = apiLat / 1000000;| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 400 | Bad Request | Invalid parameters |
| 401 | Unauthorized | Missing or invalid WAF token |
| 403 | Forbidden | Access denied |
| 404 | Not Found | Endpoint or resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
}
}-
WAF Token Expired
- Status: 401
- Solution: Refresh the WAF token
-
Invalid Metro ID
- Status: 400
- Solution: Use a valid metro ID (1 for Israel)
-
Invalid Coordinates
- Status: 400
- Solution: Ensure coordinates are multiplied by 1,000,000
-
Rate Limiting
- Status: 429
- Solution: Implement exponential backoff
// Step 1: Search for routes
const searchParams = new URLSearchParams({
tripPlanPref: '2',
time: Date.now().toString(),
timeType: '2',
isCurrentTime: 'true',
routeTypes: '3,5,4,7,6,2,1,0',
routeTransportOptions: '1,5',
fromLocation_id: '0',
fromLocation_type: '6',
fromLocation_latitude: '32228394',
fromLocation_longitude: '35246259',
fromLocation_caption: encodeURIComponent('Jerusalem Central Bus Station'),
toLocation_id: '0',
toLocation_type: '6',
toLocation_latitude: '32075487',
toLocation_longitude: '34775489',
toLocation_caption: encodeURIComponent('Dizengoff Center')
});
const searchResponse = await fetch(
`https://moovitapp.com/api/route/search?${searchParams}`,
{
headers: {
'moovit_app_type': 'WEB_TRIP_PLANNER',
'moovit_client_version': '5.151.2/V567',
'moovit_customer_id': '4908',
'moovit_metro_id': '1',
'moovit_phone_type': '2',
'moovit_user_key': 'YOUR_USER_KEY',
'moovit_gtfs_language': 'EN',
'x-aws-waf-token': 'YOUR_WAF_TOKEN',
'accept': 'application/x-protobuf',
'protobuf-version': 'V3'
}
}
);
// Response: { "token": "1770206919266,8176e233-1a58-412e-9c7e-aa904dbf2624" }
// Step 2: Poll for results
const { token } = await searchResponse.json();
let offset = 0;
let allResults = [];
let completed = false;
while (!completed) {
const resultResponse = await fetch(
`https://moovitapp.com/api/route/result?token=${token}&offset=${offset}`,
{
headers: {
'moovit_app_type': 'WEB_TRIP_PLANNER',
'moovit_client_version': '5.151.2/V567',
'moovit_customer_id': '4908',
'moovit_metro_id': '1',
'moovit_phone_type': '2',
'moovit_user_key': 'YOUR_USER_KEY',
'moovit_gtfs_language': 'EN',
'x-aws-waf-token': 'YOUR_WAF_TOKEN',
'accept': 'application/json'
}
}
);
const data = await resultResponse.json();
allResults = allResults.concat(data.results);
completed = data.completed === 1;
offset += data.results.length;
// Wait before next poll if not completed
if (!completed) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
console.log('All routes:', allResults);// Get arrivals for multiple line/stop pairs
const response = await fetch(
'https://moovitapp.com/api/lines/linesarrival',
{
method: 'POST',
headers: {
'content-type': 'application/json',
'accept': 'application/json',
'moovit_app_type': 'WEB_TRIP_PLANNER',
'moovit_client_version': '5.151.2/V567',
'moovit_customer_id': '4908',
'moovit_metro_id': '1',
'moovit_phone_type': '2',
'moovit_user_key': 'YOUR_USER_KEY',
'moovit_gtfs_language': 'EN',
'x-aws-waf-token': 'YOUR_WAF_TOKEN'
},
body: JSON.stringify({
params: {
lineStopPairs: [
{ lineId: 6623410, stopId: 72956 },
{ lineId: 6007876, stopId: 73017 }
]
}
})
}
);
const arrivals = await response.json();
arrivals.forEach(stop => {
console.log(`Stop ${stop.stopId}:`);
stop.lineArrivals.arrivals.forEach(arrival => {
const etaMinutes = Math.round(
(arrival.rtEtdUTC - Date.now()) / 60000
);
console.log(` Line ${stop.lineArrivals.lineId}: ${etaMinutes} minutes`);
if (arrival.vehicleLocation) {
console.log(` Vehicle at: ${arrival.vehicleLocation.latlon.latitude / 1000000}, ${arrival.vehicleLocation.latlon.longitude / 1000000}`);
}
});
});// Calculate tile coordinates from lat/lng
function latLngToTile(lat, lng) {
const tileX = Math.floor((lng + 180) * 100);
const tileY = Math.floor((lat + 90) * 100);
return `${tileY},${tileX}`;
}
const lat = 31.7683;
const lng = 35.2137;
const tile = latLngToTile(lat, lng);
const response = await fetch(
`https://moovitapp.com/api/nearby/stops?tilesLatLngs=${tile}&revision=${Date.now()}&metroId=1`,
{
headers: {
'accept': 'application/x-protobuf',
'moovit_app_type': 'WEB_TRIP_PLANNER',
'moovit_client_version': '5.151.2/V567',
'moovit_customer_id': '4908',
'moovit_metro_id': '1',
'moovit_phone_type': '2',
'moovit_user_key': 'YOUR_USER_KEY',
'moovit_gtfs_language': 'EN',
'protobuf-version': 'V3',
'x-aws-waf-token': 'YOUR_WAF_TOKEN'
}
}
);
// Response is in protobuf format - requires protobuf decoder// Helper functions for coordinate conversion
function toApiCoordinate(realValue) {
return Math.round(realValue * 1000000);
}
function fromApiCoordinate(apiValue) {
return apiValue / 1000000;
}
// Example usage
const jerusalem = {
lat: 31.7683,
lng: 35.2137
};
const apiCoordinates = {
latitude: toApiCoordinate(jerusalem.lat), // 31768300
longitude: toApiCoordinate(jerusalem.lng) // 35213700
};
// Convert back
const realCoordinates = {
lat: fromApiCoordinate(apiCoordinates.latitude),
lng: fromApiCoordinate(apiCoordinates.longitude)
};async function pollArrivalWithBackoff(lineId, stopId, maxAttempts = 5) {
let attempt = 0;
let delay = 1000; // Start with 1 second
while (attempt < maxAttempts) {
try {
const response = await fetch(
'https://moovitapp.com/api/lines/linesarrival',
{
method: 'POST',
headers: {
'content-type': 'application/json',
'accept': 'application/json',
'moovit_app_type': 'WEB_TRIP_PLANNER',
'moovit_client_version': '5.151.2/V567',
'moovit_customer_id': '4908',
'moovit_metro_id': '1',
'moovit_phone_type': '2',
'moovit_user_key': 'YOUR_USER_KEY',
'moovit_gtfs_language': 'EN',
'x-aws-waf-token': 'YOUR_WAF_TOKEN'
},
body: JSON.stringify({
params: {
lineStopPairs: [{ lineId, stopId }]
}
})
}
);
if (response.status === 429) {
// Rate limited - wait and retry
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
attempt++;
continue;
}
return await response.json();
} catch (error) {
console.error('Error fetching arrivals:', error);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2;
attempt++;
}
}
throw new Error('Max retry attempts reached');
}
// Usage
const arrivals = await pollArrivalWithBackoff(6623410, 72956);Metro IDs are extracted from Moovit URLs: moovitapp.com/index/en/public_transit-{city}-{metroId}
Common metro area IDs:
1- Israel (Tel Aviv, Jerusalem, Haifa, etc.)662- Paris, France3483- Lyon, France1562- Marseille, France924- Bordeaux, France1024- Toulouse, France3260- Nice, France121- New York - New Jersey, USA2122- London, UK
Supported language codes:
EN- EnglishHE- HebrewAR- ArabicFR- FrenchES- Spanish- And many more...
The API implements rate limiting. Best practices:
- Implement exponential backoff
- Cache results when appropriate
- Use polling intervals suggested by the API (e.g.,
nextPollingIntervalSecs) - Avoid unnecessary duplicate requests
Some endpoints use Protocol Buffers for request/response:
/api/location- Location search/api/nearby/stops- Nearby stops/api/lines/search- Line search/api/lines/grouptrips- Grouped trips
You'll need:
- Protobuf schema definitions (
.protofiles) - Protobuf encoder/decoder library
- The
protobuf-version: V3header
Real-time features:
- Vehicle locations updated every 10-30 seconds
- Arrival predictions based on GPS tracking
- Traffic status updates
- Service alerts
Polling recommendations:
- Arrivals: Every 20-30 seconds (use
nextPollingIntervalSecs) - Vehicle locations: Every 15-30 seconds
- Alerts: Every 5 minutes
This documentation is for educational and development purposes. Use of the Moovit API must comply with Moovit's Terms of Service. This is unofficial documentation created through analysis of network traffic.
For official API access, contact Moovit directly at: https://moovit.com/
Last Updated: February 4, 2026 API Version: 5.151.2/V567 Documentation Version: 1.0.0