Skip to main content

nusmods-api

A typed Python wrapper for the NUSMods API.

The client provides access to module lists, detailed module information, venue data, and shared timetable URLs from nusmods.com. Responses are converted into immutable Python dataclasses.

Requirements

  • Python 3.15 or newer
  • Internet access when making API requests

Installation

With uv

uv add nusmods-api

For local development:

uv sync

With pip

pip install nusmods-api

Quick start

from nusmods_api import NUSModsClient

client = NUSModsClient(academic_year="2026-2027")

modules = client.get_module_list()
for module in modules[:5]:
    print(module.moduleCode, module.title)

module = client.get_module("CS2030S")
print(module.title)
print(module.description)

If no academic year is supplied, the client uses the current calendar year and the following year. For example, a client created in 2026 uses 2026-2027.

Client configuration

from nusmods_api import NUSModsClient

client = NUSModsClient(
    academic_year="2026-2027",
    timeout=20.0,
    cache_directory=".nusmods_cache",
    cache_expiration=86_400,
)
Argument Default Description
academic_year Current year and next year Academic year in YYYY-YYYY format
timeout 10.0 HTTP timeout in seconds
cache_directory .nusmods_cache Directory for cached JSON responses
cache_expiration 86400 Cache lifetime in seconds; use None for no expiration

Set cache_directory=None to disable caching:

client = NUSModsClient(cache_directory=None)

API usage

Module list

modules = client.get_module_list()

for module in modules:
    print(module.moduleCode, module.title, module.semesters)

Detailed module information

modules = client.get_module_information()
module = client.get_module(" cs2030s ")

print(module.moduleCode)  # CS2030S
print(module.acadYear)
print(module.semesterData)

Module codes are stripped and normalized to uppercase by get_module.

Venues

venues = client.get_venues(semester=1)
print(venues)

venue_information = client.get_venue_information(semester=1)
for venue, entries in venue_information.items():
    print(venue, entries)

semester must be 1 or 2.

Shared timetables

Pass a NUSMods share URL to get_shared_timetable:

share_url = (
    "https://nusmods.com/timetable/sem-1/share?"
    "CS1231S=TUT:25B,LEC:1&"
    "CS2030S=REC:04,LAB:10B,LEC:1&"
    "GES1034=LEC:1,TUT:E2&"
    "IS1108=TUT:14,LEC:2&"
    "MA1522=LEC:1"
)

timetable = client.get_shared_timetable(share_url)

for lesson in timetable.weekly:
    print(
        lesson.moduleCode,
        lesson.lessonType,
        lesson.classNo,
        lesson.day,
        lesson.startTime,
        lesson.endTime,
        lesson.venue,
    )

for exam in timetable.exams:
    print(exam.moduleCode, exam.examDate, exam.examDuration)

The client parses the module selections from the URL and retrieves the corresponding module data from the NUSMods API. Lesson type codes such as LEC, TUT, LAB, and REC are mapped to their full names.

Timetable also provides convenience methods:

monday = timetable.get_day_schedule("Monday")
exam = timetable.get_exam_for_module("CS2030S")

Data models

The package exposes frozen dataclasses for common API response types:

  • ModuleCondensed — module code, title, and offered semesters
  • Module — detailed module information
  • Lesson — class schedule information
  • LessonWithModule — a lesson including its module code
  • VenueLesson and VenueInformation — venue timetable data
  • WeekRange — date-based lesson ranges
  • Workload — workload components
  • PrerequisiteTree — nested prerequisite expressions
  • ExamTimetable — exam information
  • Timetable — selected weekly lessons and exams

Some model fields retain the NUSMods API naming convention, such as moduleCode, classNo, startTime, lessonType, and examDate. ExamTimetable.examDate is represented as a datetime and preserves the time and timezone supplied by the API.

Error handling

The client raises package-specific exceptions:

from nusmods_api import (
    NUSModsHTTPError,
    NUSModsInvalidResponseError,
    NUSModsNotFoundError,
    NUSModsRequestError,
)

try:
    module = client.get_module("CS2030S")
except NUSModsNotFoundError:
    print("The module or academic year was not found")
except NUSModsHTTPError as error:
    print(error.status_code, error.url)
except NUSModsInvalidResponseError:
    print("The API returned invalid JSON")
except NUSModsRequestError:
    print("The API could not be reached")

Invalid client arguments, malformed share URLs, invalid semesters, and unmatched lesson selections raise ValueError.

Testing

Run the default unit test suite:

uv run pytest

The live integration test makes real requests to the NUSMods API:

uv run pytest tests/test_live_client.py -m live -q

The live test requires internet access and may need its expected timetable dates updated when NUSMods publishes a new academic-year dataset.

License

No license has been specified yet.

Metadata

Release files for nusmods-api 0.3.2

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

Source distribution (sdist)

Source distribution for nusmods-api 0.3.2
File Size Uploaded Provenance Actions
nusmods_api-0.3.2.tar.gz 8.7 kB With provenance Details for nusmods_api-0.3.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nusmods-api 0.3.2
File Interpreter ABI Platform Actions
nusmods_api-0.3.2-py3-none-any.whl Python 3 none any Details for nusmods_api-0.3.2-py3-none-any.whl

Total release size: 19.2 kB

Release files / nusmods_api-0.3.2.tar.gz

Download URL nusmods_api-0.3.2.tar.gz
Size 8.7 kB
Tags Source
SHA-256 checksum
How to use checksums
74812bea898c0c08c6d0fa74809744ed39b97dac76b18c419e5d2881ece8731a
BLAKE2b-256 checksum
How to use checksums
f3191e807e0ef91a5fe5aee5e6db5ca02c62d5aa5f54a1da1db718f0afc1f688
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 26, 2026.

Transparency log

Release files / nusmods_api-0.3.2-py3-none-any.whl

Download URL nusmods_api-0.3.2-py3-none-any.whl
Size 10.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
68972ddbae37342d6f39eb33be0ec2455c02ade76a1835410bfc297e159da460
BLAKE2b-256 checksum
How to use checksums
8ae022ca35fdac3862ae8670413bd42679a97da785313fd24ff531a6913eab95
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 26, 2026.

Transparency log

Security for nusmods-api 0.3.2

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

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for nusmods_api-0.3.2.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 26, 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.

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