Skip to main content

LTADataMallpy

A Python wrapper for the LTA DataMall API, providing typed, easy-to-use access to Singapore's public transport and traffic data: buses, trains, taxis, roads, active mobility, and geospatial layers.

Built on httpx, it ships with automatic pagination for endpoints returning more than 500 records, a persistent connection pool, and descriptive custom exceptions.

Features

  • Single entry point: DataMall with transport, traffic, and geospatial namespaces
  • Typed responses: every endpoint returns validated pydantic models (LtaResult[T] for lists), not raw dicts
  • Automatic pagination ($skip handling) for long endpoints
  • Persistent httpx.Client with a 30s timeout and explicit close_client()
  • Custom exceptions with response status code, message, and timestamp
  • Python >=3.10 (X | None syntax, built-in generics), fully typed (py.typed-style annotations throughout)

Installation

From PyPI

pip install lta-api-py

Getting an API key

  1. Register at the LTA DataMall portal.
  2. Create an account and request access to an API key.
  3. Use the key as the api_key argument (or load it from an environment variable / .env file).

Quick start

from LTADataMallpy import DataMall
import os

dm = DataMall(api_key=os.getenv("LTA_API_KEY"))

# Transport data — typed pydantic models, not dicts
arrivals = dm.transport.bus.arrival.get_bus_arrival(stopcode="83111")
for service in arrivals.services:
    print(service.service_no, service.next_bus.estimated_arrival)

# Traffic data
speed_bands = dm.traffic.roads.get_traffic_speed_bands()
for band in speed_bands.value:
    print(band.road_name, band.speed_band)

Always close the client when you are done to release the connection pool:

from LTADataMallpy.helpers import close_client
# ... your requests ...
close_client()

API reference

Everything is imported from the top-level package LTADataMallpy.

Main entry point: DataMall

from LTADataMallpy import DataMall

dm = DataMall(api_key: str, accept: str | None = None)

Raises ValueError if api_key is empty or missing. Exposes three namespaces:

Attribute Type Description
dm.transport Transport Bus, train, taxi, active mobility
dm.traffic Traffic Roads and infrastructure
dm.geospatial Geospatial Geospatial whole-island layers

Transport

from LTADataMallpy import DataMall
dm = DataMall(API_KEY)
t = dm.transport
Attribute Class Description
t.bus Bus Bus services/routes
t.train Train Train stations/services
t.taxi Taxi Taxi availability
t.active_mobility ActiveMobility Bicycle parking

Traffic

dm = DataMall(API_KEY)
tr = dm.traffic
Attribute Class Description
tr.roads Roads Road/traffic data
tr.infra Infra Infrastructure data

Geospatial

dm = DataMall(API_KEY)
G = dm.geospatial
G.get_geo_layer(id="LayerName")

LTA DataMall geospatial layers require a Token in the request header in addition to AccountKey. Pass the token as the accept argument (it is forwarded as the accept header value):

dm = DataMall(api_key=API_KEY, accept=YOUR_GEOSPATIAL_TOKEN)
data = dm.geospatial.get_geo_layer(id="factoryarea")

Classes to import directly

Each underlying class is importable on its own, taking (api_key, accept=None) unless noted:

from LTADataMallpy.Bus import Bus
from LTADataMallpy.Train import Train
from LTADataMallpy.Taxi import Taxi
from LTADataMallpy.Roads import Roads
from LTADataMallpy.Infra import Infra
from LTADataMallpy.ActiveMobility import ActiveMobility
from LTADataMallpy.Geo import Geospatial
from LTADataMallpy.subclasses import Transport, Traffic

Methods

Bus

Class Method Notes
Bus — Aggregates the four sub-objects
Bus.arrival (BusArrival)
BusArrival get_bus_arrival(stopcode, serviceno=None) BusStopCode required
Bus.services (BusServices)
BusServices get_bus_services(serviceno=None) Paginated
Bus.routes (BusRoutes)
BusRoutes get_bus_routes() Paginated
BusRoutes get_planned_routes()
Bus.stops (BusStops)
BusStops get_bus_stops(stopcode=None) Paginated
BusStops get_pvolume_bus_stop(date=None, origin_destination=None) Bus or OD-Bus volume

Train

Class Method Notes
Train station, service sub-objects
TrainStation get_pvolume_train_station(date=None, origin_destination=None) Train or OD-Train volume
TrainStation get_maintenance()
TrainService get_train_service_alerts()
TrainService get_crowd_density(line) TrainLine required
TrainService get_crowd_density_forecast(line) TrainLine required
TrainService get_gtfs_train_schedule()
TrainService get_gtfs_train_service_real_time()
TrainService get_gtfs_train_trip_update()

Taxi

Class Method Notes
Taxi get_taxi_availability() Paginated
Taxi get_taxi_stands()

Roads (traffic)

Class Method Notes
Roads get_est_travel_times()
Roads get_carpark_availability() Paginated
Roads get_road_openings()
Roads get_road_works() Paginated
Roads get_traffic_incidents()
Roads get_traffic_speed_bands()
Roads get_traffic_flows()
Roads get_flood_alerts()

Infra (traffic)

Class Method Notes
Infra get_faulty_lights()
Infra get_traffic_images() Paginated
Infra get_vms_emas()
Infra get_ev_charge_points(postalcode) PostalCode required
Infra get_ev_charge_points_batch()

ActiveMobility

Class Method Notes
ActiveMobility get_bicycle_parking(lat, long, dist=None) Lat/Long required; Paginated

Geospatial

Class Method Notes
Geospatial get_geo_layer(id) ID required; needs accept token

Helpers

Low-level helpers live in LTADataMallpy.helpers and can be used for custom endpoint calls:

from LTADataMallpy.helpers import (
    build_headers,        # build the AccountKey/accept header dict from an API key
    build_url,            # join an endpoint to the DataMall base URL
    make_request,         # GET one page; raises custom errors in the helpers module
    make_paginated_request,  # fetch all pages, combining "value" lists
    create_client,        # lazily create/reuse the persistent httpx.Client
    close_client,         # close the persistent client (self-heals on next use)
)

Example of a custom call:

from LTADataMallpy.helpers import build_headers, build_url, make_request

headers = build_headers(API_KEY)
url = build_url("SomeEndpoint")
data = make_request(headers, url, params={"SomeParam": "value"})

Persistent client

  • The shared httpx.Client is created lazily on the first request (with timeout=30.0) and reused for every call, keeping TCP/TLS connections alive.
  • Call close_client() at the end of a script to free resources. It is safe to call mid-run: the next request transparently recreates the client.
  • httpx.Client is thread-safe, so the client can be shared across threads.

Pagination

Endpoints marked Paginated above go through make_paginated_request, which automatically walks $skip in 500-record pages and merges every page's value list into a single response before it is validated into the model.

Error handling and debugging

make_request maps known HTTP statuses to descriptive exceptions (all in LTADataMallpy.helpers, subclasses of DataMallError):

HTTP status Exception Meaning
401 / 403 DataMallPermissionError Invalid/missing API key
404 DataMallNotFoundError Requested data not available for this account
500 / 502 / 503 DataMallBackendError LTA backend server error
429 DataMallRateLimitError Rate limit hit; slow your request frequency

Every DataMallError exposes .status_code and .detail (the API's error message when parseable) and stringifies as "{status_code} {detail}". Any other status passes through httpx.Response.raise_for_status().

from LTADataMallpy import DataMall
from LTADataMallpy.helpers import DataMallPermissionError, DataMallRateLimitError

dm = DataMall(api_key="invalid-key")

try:
    dm.transport.bus.arrival.get_bus_arrival("83111")
except DataMallPermissionError as e:
    print("Bad key:", e.status_code, e.detail)
except DataMallRateLimitError as e:
    print("Slow down:", e.status_code, e.detail)

Debugging tips

  • 429 / rate limit: LTA enforces per-minute request quotas. Add backoff, or cache responses you fetch repeatedly.
  • Timeout: the client uses a 30-second timeout; paginated calls to big datasets (e.g. BusRoutes, BusServices) can be slow on a cold connection.
  • Check connectivity: confirm the key is active in the DataMall portal and that your network reaches datamall2.mytransport.sg.
  • .env / secrets: load your key from an environment variable rather than hard-coding it. The .env file is git-ignored.

Development

uv sync --group dev       # install with dev extras (pytest, python-dotenv)
uv run pytest tests/      # full suite; hits the live API, needs LTADATAMALL_API_KEY in .env
uv build                  # build sdist + wheel into dist/
uvx twine check dist/*    # validate package metadata

Tests read LTADATAMALL_API_KEY from a .env file (git-ignored) or the environment; without it they skip. Requests are paced ~3s apart to respect LTA rate limits.

License

Apache License 2.0. See LICENSE.

Metadata

Release files for lta-api-py 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lta-api-py 0.2.0
File Size Uploaded Provenance Actions
lta_api_py-0.2.0.tar.gz 14.1 kB With provenance Details for lta_api_py-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for lta-api-py 0.2.0
File Interpreter ABI Platform Actions
lta_api_py-0.2.0-py3-none-any.whl Python 3 none any Details for lta_api_py-0.2.0-py3-none-any.whl

Total release size: 32.2 kB

Release files / lta_api_py-0.2.0.tar.gz

Download URL lta_api_py-0.2.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
daba981eef29ea92a3277869904c3900ce0ae771de2cefc12afbd8108849ed6d
BLAKE2b-256 checksum
How to use checksums
c2977827fa9819bd1a69995022e0fc19abfc15bfe0b7f0f1bcc9945823a32c20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / lta_api_py-0.2.0-py3-none-any.whl

Download URL lta_api_py-0.2.0-py3-none-any.whl
Size 18.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8c32eb74849e168cbbb1c26d05d6e366a82d906c74908ddae5f4937c65ae4c0c
BLAKE2b-256 checksum
How to use checksums
dd87fea334f300ec5c19fa20247fce015121181e6b1e9c21ac8cd70f5c5852d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for lta-api-py 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for lta_api_py-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page