ntnu-api
by MartinSA04
README.md
# ntnu-api
[](https://www.npmjs.com/package/ntnu-api)
TypeScript client for NTNU course data. Wraps the three public, no-auth data
sources for course information at the Norwegian University of Science and
Technology behind one typed interface. Fetch-based with zero runtime
dependencies — works in Node ≥ 20, Cloudflare Workers, browsers, and any
other fetch-capable runtime.
| Namespace | Data | Backing source |
| --- | --- | --- |
| `client.courses` | catalog search, per-course schedules & weekly timetables | ntnu.no course-page JSON (Liferay) |
| `client.courses.details` | exam info (date, time, duration, aid code, rooms), fact box, descriptions, study programs | ntnu.no course-page HTML (scraped — no JSON upstream) |
| `client.grades` | grade distributions per course/year/semester | HK-dir DBH statistics API |
| `client.programs` | study-program catalog (~400 programs), per-cohort study plans (courses per semester, specializations) | ntnu.no program pages (Liferay JSON) |
| `client.semesters` | term ids (`26h`/`26v`), teaching weeks, exam periods | NTNU's TP timetable system |
See [docs/api-research.md](docs/api-research.md) for the full survey of NTNU
data sources (including the dead and auth-gated ones) and the exact upstream
request shapes.
## Usage
```sh
npm install ntnu-api
```
```ts
import { NTNUClient } from "ntnu-api";
const client = new NTNUClient();
// Server-side course search (one 500-course page)
const page = await client.courses.search(2026, "objektorientert");
for (const hit of page.courses.slice(0, 3)) {
console.log(hit.courseCode, hit.courseName);
}
// Full catalog iteration (dedups the upstream's duplicate entries)
for await (const resultPage of client.courses.searchAll(2026)) {
// ...
}
// Dated teaching activities and the summarized weekly grid
const activities = await client.courses.schedules("TDT4100", 2026);
const grid = await client.courses.timetable("TDT4100", 2026);
// Everything else on the course page (HTML-only upstream, scraped):
// exams with start time/duration/hjelpemiddelkode/rooms, credits, level,
// descriptions, and the study programs the teaching is planned for —
// details.studyPrograms maps the studyProgramKeys on schedule/timetable
// entries to program names.
const details = await client.courses.details("TDT4100", 2026);
for (const exam of details?.exams ?? []) {
console.log(exam.occasion, exam.date, exam.time, exam.duration, exam.aidCode);
}
// Study programs: the catalog, and the full plan for a cohort
// (course codes+versions per semester, specialization choice points).
// planCourseCodes() flattens a plan to its course codes.
const programs = await client.programs.all();
const plan = await client.programs.studyPlan("MTDT", 2024);
// Grade distribution (accepts bare or DBH-versioned codes)
for (const row of await client.grades.distribution("TDT4100", { years: [2023] })) {
console.log(row.year, row.semesterName, row.grade, row.total);
}
// Terms as known by NTNU's timetable system
const current = await client.semesters.current();
```
All errors derive from `NTNUAPIError` (with `NotFoundError`,
`RateLimitError`, `APITimeoutError`, ... subclasses). Transient failures
(429, 502–504, connection errors) are retried with exponential backoff,
honoring `Retry-After`. Upstream payloads are parsed into plain typed
objects; helpers like `bestName()` (trilingual name fallback) and
`weekNumbers()` (ISO-week range expansion) are exported alongside them.
A word of caution: the ntnu.no endpoints are NTNU's own internal AJAX
surface, not a documented API — treat results accordingly and be polite
(the client ships a descriptive User-Agent by default).
### MCP server
The [ntnu-mcp](https://github.com/MartinSA04/ntnu-mcp) sibling repo hosts a
remote MCP server built on this package (Cloudflare Worker), exposing the
client as ten LLM-shaped tools — connect Claude or ChatGPT to
`https://ntnu-mcp.martinsundal.no/mcp`. Upstream fixes and new data sources
land here; the MCP repo only shapes typed data for LLMs.
## Stack
- **TypeScript** (strict), ESM-only, zero runtime dependencies
- **[vitest](https://vitest.dev/)** — tests over captured live fixtures
- **[Biome](https://biomejs.dev/)** — linting + formatting
- **[mise](https://mise.jdx.dev/)** — pins Node and exposes task shortcuts
- **Dev Container** — reproducible environment
## Development
```sh
mise install # pinned Node
npm install
```
| Task | Command |
| ---------------- | ------------------- |
| `mise run test` | `vitest run` |
| `mise run lint` | `biome check .` |
| `mise run fmt` | `biome check --write .` |
| `mise run typecheck` | `tsc --noEmit` |
| `mise run build` | `tsc -p tsconfig.build.json` → `dist/` |
| `mise run check` | lint + typecheck + test |
The default test run is fully mocked (injected `fetch`) against real
captured payloads in `tests/fixtures/`. To also verify against the live
endpoints (drift check):
```sh
NTNU_API_LIVE=1 npm test
```
Releases are tag-driven: `npm version <patch|minor|major> && git push
--follow-tags`. The [release workflow](.github/workflows/release.yml) then
verifies the tag, runs the full check suite, publishes to npm (trusted
publishing with provenance — no token secrets), and creates the GitHub
Release with generated notes. The `prepare` script builds `dist/`, so
`github:MartinSA04/ntnu-api` installs also work between releases.
## History
This library began as an async Python client (httpx + pydantic, with a CLI
and a stdio MCP server); it was ported 1:1 to TypeScript so one codebase
serves Node, Workers, and browsers, per
[docs/ts-migration-spec.md](docs/ts-migration-spec.md). The final Python
state is preserved at the
[`python-final`](https://github.com/MartinSA04/ntnu-api/tree/python-final)
tag.
## Roadmap
- [x] MCP server exposing the client as tools → [ntnu-mcp](https://github.com/MartinSA04/ntnu-mcp)
- [ ] Expose the remaining catalog search facets (campus, level, faculty/institute)
- [x] Course detail scraping (exams, description, credits, prerequisites — HTML-only upstream) → `courses.details()`
- [ ] TP `emner.php` semester catalog (English names, campus ids — see docs/api-research.md)
- [ ] TP iCal export as a secondary timetable source once semesters publish
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues