API Documentation
Complete reference for the Malaysia Transit API. Build powerful transit applications with real-time data.
Note: This documentation is free to read and explore. However, accessing the actual API endpoints requires an API key subscription.
Last updated: 11 May 2026
Authentication
API Key Required
The Malaysia Transit API requires authentication via API key. Subscribe through our Developer Portal to get your unique API key and start building.
How to Authenticate
Include your API key using one of these methods:
# Option 1: X-API-Key header (recommended)
X-API-Key: mtk_live_your_api_key_here
# Option 2: Authorization Bearer header
Authorization: Bearer mtk_live_your_api_key_here
# Option 3: Query parameter (fallback)
?api_key=mtk_live_your_api_key_hereAPI Key Formats
mtk_pub_ + 32 hex (public)mtk_sec_ + 32 hex (secret)mtk_live_ + 32 hex (legacy)The middleware accepts all three prefixes. Legacy keys auto-classify based on whether allowed origins are set.
Security
- • Keys are hashed (SHA-256) in storage
- • Full key shown only once upon generation
- • Keys invalidated when subscription ends
- • Public and secret keys can be regenerated independently
Public vs Secret Keys (Stripe-style pair)
Every subscriber gets two keys with distinct roles. You mint them independently from the dashboard, and regenerating one does not affect the other.
mtk_pub_*PublicBrowser-safe. Pair with an Allowed Origins allowlist so a scraped key cannot be reused from any other site.
- Use in SPAs, mobile webviews, static sites
- Origin allowlist enforced server-side
- Reject counters surfaced in the dashboard
mtk_sec_*SecretServer-side only. No origin restriction, since back-end clients cannot send a trustworthy Origin header.
- Use in ChatGPT custom GPTs, Zapier, n8n
- Use in your backend or scheduled jobs
- Never embed in browser bundles
Allowed Origins (public keys)
Configure an allowlist on your public key from the Developer Portal dashboard. The middleware then rejects browser requests whose Origin header (or Referer fallback) does not match. Empty allowlist means no restriction (backwards-compatible).
Pattern syntax
https://example.comExact host matchhttps://*.example.comSingle-label subdomain wildcardhttps://*--myapp.netlify.appMid-label wildcard (Netlify deploy previews)http://localhost:5173Local dev with explicit portDefault ports (:443, :80) are normalised away. The matcher rejects overly-broad patterns like https://*, https://*.com, and any wildcard with fewer than 3 labels. The literal Origin: null (file://, sandboxed iframes) is always rejected.
Threat model (honest framing)
- Protects against: a key scraped from a SPA bundle and reused on another website in a browser context. The browser sends a true Origin header that the middleware verifies.
- Does NOT protect against: a scripted client (curl, server-side scraper, malicious automation) that forges Origin: https://yoursite.com. For full theft prevention, keep keys out of public bundles and proxy through a backend (or use a secret key on the server).
The dashboard surfaces a lifetime + last-7-days reject counter, backed by a sharded Firestore subcollection so high-traffic abuse bursts increment atomically.
Authentication Errors
401403429Quick Start
// Initialize API client
const API_BASE = 'https://malaysiatransit.techmavie.digital/api';
const API_KEY = 'mtk_live_your_api_key_here'; // Get from Developer Portal
// Helper function with authentication
async function apiRequest(endpoint) {
const response = await fetch(`${API_BASE}${endpoint}`, {
headers: { 'X-API-Key': API_KEY }
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
}
// Get real-time vehicles in Penang
async function getVehicles(area = 'penang') {
const data = await apiRequest(`/realtime?area=${area}`);
console.log(`Found ${data.vehicleCount} vehicles`);
return data;
}
// Get arrivals for a stop
async function getArrivals(stopId, area = 'penang') {
const data = await apiRequest(`/stops/${stopId}/arrivals?area=${area}`);
data.arrivals.forEach(arrival => {
console.log(`Route ${arrival.routeShortName}: ${arrival.etaMinutes} min`);
});
return data;
}
// Search for stops
async function searchStops(query, area = 'penang') {
return apiRequest(`/stops/search?q=${encodeURIComponent(query)}&area=${area}`);
}Service Areas Reference
Important: Most API endpoints require an area parameter. Use the exact Area ID values from the table below. The default area is penang if not specified.
| Area ID | Name | State | Providers | Types | Status |
|---|---|---|---|---|---|
klang-valley | Klang Valley | Kuala Lumpur & Selangor | Rapid Rail KL (LRT/MRT/Monorail/BRT), Rapid Bus KL, MRT Feeder, KTM Komuter Klang Valley, KLIA Ekspres, KLIA Transit | BusRail | Live |
penang | Penang | Penang Island & Seberang Perai | Rapid Penang, Penang Ferry, KTM Komuter Utara, SRT International Shuttle | BusFerryRail | Live |
kangar | Kangar | Perlis | BAS.MY Kangar, KTM Komuter Utara, SRT International Shuttle | BusRail | Live |
alor-setar | Alor Setar | Kedah | BAS.MY Alor Setar, KTM Komuter Utara, SRT International Shuttle | BusRail | Live |
kota-bharu | Kota Bharu | Kelantan | BAS.MY Kota Bharu, KTM Intercity | BusRail | Live |
kuala-terengganu | Kuala Terengganu | Terengganu | BAS.MY Kuala Terengganu | Bus | Live |
melaka | Melaka | Melaka | BAS.MY Melaka, KTM Komuter Klang Valley (Seremban Line) | BusRail | Live |
johor | Johor Bahru | Johor | BAS.MY Johor Bahru, Bas Muafakat Johor (BMJ), KTM Shuttle Tebrau | BusRail | Live |
kuching | Kuching | Sarawak | BAS.MY Kuching, City Public Link S19 | Bus | Live |
ipoh | Ipoh | Perak | BAS.MY Ipoh, KTM Komuter Utara, SRT International Shuttle | BusRail | Live |
seremban | Seremban | Negeri Sembilan | BAS.MY Seremban (2 operators), KTM Komuter Klang Valley (Seremban Line), KTM Intercity | BusRail | Live |
kuantan | Kuantan | Pahang | BAS.MY Kuantan (pending) | Bus | Maintenance |
kota-kinabalu | Kota Kinabalu | Sabah | Coming Soon | Bus | Soon |
Transit Types
busBus services onlyrailRail services onlyferryFerry services onlySchedule Endpoints
Supported areas:
ipohserembanpenangkangaralor-setarkota-bharukuala-terengganumelakajohorkuchingDirection values:
outboundAway from origininboundTowards originloopCircular routesFare Calculator Areas
For BAS.MY & Rapid Penang buses:
ipohserembanpenangkangaralor-setarkota-bharukuala-terengganumelakajohorkuchingKTM Komuter, KTM Intercity & Penang Ferry have separate endpoints.
Note: KTM Intercity fare API returns 501 (not implemented yet).
API Endpoints
Arrival Predictions (v2.0)
The API calculates bus arrival times using shape-based distance calculation instead of straight-line distance, resulting in 40-60% more accurate predictions.
Calculation Methods
- shape-based: Uses route geometry (most accurate)
- straight-line: Fallback when shape data unavailable
Confidence Levels
- high: Shape-based, vehicle within 50m of route
- medium: Shape-based, vehicle 50-200m from route
- low: Straight-line fallback
Time-of-Day Factors
Example Response
{
"routeShortName": "301",
"destination": "Komtar",
"vehicleId": "PKX1234",
"etaMinutes": 18,
"distance": 6500,
"calculationMethod": "shape-based",
"confidence": "high",
"timeFactor": 0.6
}Best Practices
Caching Strategy
- • Static Data: Cache for 24 hours (routes, stops, schedules)
- • Real-time Data: Cache for max 30 seconds
- • Arrivals: Cache for 15-30 seconds maximum
Error Handling
- • Handle 429 (rate limit) with exponential backoff
- • Use cached data when 503 (service unavailable)
- • Implement graceful degradation for failures
Polling Strategy
- • Poll real-time data every 10-30 seconds
- • Adjust polling based on data freshness
- • Reduce frequency when data is stale
Batch Requests
- • Batch multiple stop arrivals in parallel
- • Limit batch size to 5 concurrent requests
- • Add small delays between batches
API Analytics Dashboard
The middleware includes a built-in analytics dashboard to track API usage, monitor performance, and identify popular endpoints and service areas.
Access the dashboard:
Dashboard Features
- Cumulative Statistics: Track total API calls across all time
- Real-time Metrics: Requests/hour, avg response time, error rate
- Endpoint Breakdown: See which API endpoints are most used
- Service Area Analytics: Track usage per area
Dashboard Metrics
Client Tracking
The dashboard tracks which apps and websites use the API. Apps can voluntarily identify themselves:
X-App-Name: MyTransitAppApps using X-App-Name header
Requests from websites (Referer/Origin)
Direct API calls (curl, Postman)
Ready to Start Building?
Get your API key from the Developer Portal and start integrating today.
Powered by Malaysia Transit
This API is brought to you by Malaysia Transit. Try the web app to see the data in action, or learn more about the project.