Catalog API
Upsked provides normalized course catalog data from supported universities through a simple REST API.
We turn school-specific course, section, and enrollment structures into predictable records: searchable courses, schedulable class options, linked lectures and labs, and bundled classes that must be taken together.
Pick a school and term, search courses, then fetch the valid class options a student can add to a schedule.
Use it to power school and term pickers, course search, enrollment assistants, schedule builders, and other tools that need trustworthy catalog data, without maintaining per-school scrapers. Integrate with REST (below) or MCP for agent workflows.
The model
Courses are searchable. Options are schedulable.
A course tells you what exists in a term. A schedulable option tells you what a student can add to a schedule. Some options are standalone sections. Others are linked lecture-lab pairs or school-defined blocks that must be taken together.
That is why the API separates /courses from /sections.
- Choose a school with
/universities. - Choose a term with
/semesters. - Search courses in that term with
/courses. - Fetch valid class options with
/sections.
IDs to pass between calls:
university_id: school slug (upd,admu,dlsu, …)semester_id: term (required for/coursesand/sections)course_id: from/courses, used by/sectionsclass_code: use instead ofcourse_idwhen you already have CRS codes
Keep public apps safe: call the API from your server when you deploy. The browser console on this page is only for trying requests while signed into Upsked.
Quickstart
Goal: show a student which class sections they can pick for a course.
- Get a key: open Account settings, then Public REST API (
catalog:read). - List schools:
GET /universities - List terms:
GET /semesters?university=upd, then copy asemester_id - Search courses:
GET /courses?semester_id=<id>&query=math, then copy acourse_id - Get sections:
GET /sections?semester_id=<id>&course_id=<course_id>
Every request needs this header:
Authorization: Bearer upsked_api_v1_YOUR_KEY
Base URL: https://upsked.com/api/public/v1
curl -sS 'https://upsked.com/api/public/v1/semesters?university=upd' \ -H 'Authorization: Bearer upsked_api_v1_YOUR_KEY'
- Do the steps in order. You need a
semester_idbefore/coursesor/sections. - If
section_countis0, skip/sectionsfor that course. - For
/sections, sendcourse_idorclass_code, not both.
Authentication
Authorization: Bearer upsked_api_v1_...
Create keys in Account settings, open Public REST API. Keys can expire after 1, 7, or 30 days. You can reveal, refresh, or revoke them from Account settings.
Do not put API keys in public JavaScript, mobile apps, or browser extensions. If classmates can view your source, they can copy the key.
Base URLs
| Base | Notes |
|---|---|
https://upsked.com/api/public/v1 | Default. Always available. |
https://api.upsked.com | Optional subdomain alias (same handlers) |
http://localhost:3000/api/public/v1 | Local development |
Paths: /universities, /semesters, /courses, /sections.
Conventions
- JSON uses snake_case.
- Lists:
{ "object": "list", "type": "<kind>", "results": [ ... ] }. - Section responses use
optionsbecause one class choice can include multiple linked sections. - Errors:
{ "object": "error", "code": "...", "message": "..." }. - Validation 400 may include
errors: [{ "param", "message" }]. - Pagination uses
limit,offset,has_more, andnext_offset. - Times are local school times in
HH:mmformat.
Error codes
| HTTP | Code | When |
|---|---|---|
| 400 | bad_request | Invalid params, unknown semester_id or university |
| 401 | unauthorized | Missing or malformed Authorization header |
| 401 | invalid_api_key | Revoked, expired, or invalid key |
| 404 | not_found | Unknown course_id, or no matching class_code |
| 429 | rate_limited | Per-IP or per-account rate cap |
| 200 | (none) | Partial class_code match returns missing_class_codes |
Rate limits
Responses may include:
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
429: { "object": "error", "code": "rate_limited", "message": "..." }
Use pagination and ETag on /sections instead of tight polling. If you receive a 429, wait until the reset time before retrying.
Pitfalls
| Mistake | Fix |
|---|---|
Calling /courses before choosing a term | Call /semesters first and pass the returned semester_id. |
Treating section_count as a section list | Use section_count only to decide whether to call /sections. |
Calling /sections with both course_id and class_code | Pass exactly one of them. |
| Assuming every school supports every feature | Read capabilities from /universities before search or sections. |
| Caching section pages without query params | Include semester_id, course_id or class_code, limit, and offset in cache keys. |
List universities
Returns the schools available in Upsked. Start here when your app needs a school picker.
Authorizations
Bearer token
Send your API key in the Authorization header as Bearer upsked_api_v1_….
Query parameters
No query parameters.
Response
| Field | Description |
|---|---|
| object | university for each school row. |
| university_id | School slug such as upd, admu, or dlsu. Use it as university in GET /semesters. |
| label | School name to show in a picker. |
| status | active means generally available. pilot means coverage may still be limited. |
| capabilities | Tells you which catalog features work for the school, such as catalog_course_search and catalog_sections. |
| semester_id_hint | Example id shape only. Use GET /semesters for real term ids. |
Do not hard-code the school list. New schools can appear without a docs change.
Call GET /semesters next with the selected university_id.
Request
Set parameters, copy a command, or send a live request.
curl -sS 'https://upsked.com/api/public/v1/universities' \ -H 'Authorization: Bearer YOUR_API_KEY'
Responses
Send a request to see the live response, or browse example payloads by status code.
{
"object": "list",
"type": "university",
"results": [
{
"object": "university",
"university_id": "admu",
"label": "Ateneo de Manila University",
"status": "active",
"capabilities": ["catalog_course_search", "catalog_sections"],
"semester_id_hint": "admu-25262"
}
]
}List semesters
Returns terms for a school. Pick a semester_id before searching courses or sections.
Authorizations
Bearer token
Send your API key in the Authorization header as Bearer upsked_api_v1_….
Query parameters
| Parameter | Required | Description |
|---|---|---|
| university | Optional | A university_id from GET /universities. Omit it only if you are building your own cross-school term list. |
Response
| Field | Description |
|---|---|
| semester_id | Term id required by GET /courses and GET /sections. |
| label | Term name to show in a picker. |
| term | School-specific term code. |
| year_start / year_end | Academic year bounds. |
| is_current | True when this is the recommended term for the school. |
| university_id | School slug for the term. |
| default_semester_id | Recommended default when university is provided. Null on a global list. |
| default_semester_ids_by_university | Map of school slug to default term id. Present on the global list only. |
For a student-facing UI, pass university so the term picker stays school-specific.
Unknown university returns 400.
Request
Set parameters, copy a command, or send a live request.
Catalog context· tap to expand
curl -sS 'https://upsked.com/api/public/v1/semesters' \ -H 'Authorization: Bearer YOUR_API_KEY'
Responses
Send a request to see the live response, or browse example payloads by status code.
{
"object": "list",
"type": "semester",
"default_semester_id": "admu-25262",
"default_semester_scope": "university",
"university_id": "admu",
"results": [
{
"object": "semester",
"semester_id": "admu-25262",
"label": "Second Semester AY 2025-2026",
"term": "2",
"year_start": 2025,
"year_end": 2026,
"is_current": true,
"university_id": "admu"
}
]
}Search courses
Search courses in one term. Use the returned course_id to load class options.
Authorizations
Bearer token
Send your API key in the Authorization header as Bearer upsked_api_v1_….
Query parameters
| Parameter | Required | Description |
|---|---|---|
| query | Required | What the student typed. Matches course code and title. |
| semester_id | Required | A semester_id from GET /semesters. |
| limit | Optional | Number of course matches to return. Minimum 1, maximum 20, default 8. |
| offset | Optional | Pagination offset. Minimum 0, maximum 400, default 0. Use next_offset when has_more is true. |
Response
| Field | Description |
|---|---|
| course_id | Course lookup value. Pass it to GET /sections with the same semester_id. |
| title | Course title as published by the school catalog. |
| section_count | How many class options exist for this course in the selected term. If 0, skip GET /sections. |
| pagination.has_more | True when another page is available. |
| pagination.next_offset | Offset to pass on the next request. Null when there is no next page. |
| pagination.ranking_truncated | True when the ranked candidate pool hit the server cap. Narrow the query for more precise results. |
Request
Set parameters, copy a command, or send a live request.
Catalog context· tap to expand
curl -sS 'https://upsked.com/api/public/v1/courses?query=math&semester_id=&limit=8&offset=0' \ -H 'Authorization: Bearer YOUR_API_KEY'
Responses
Send a request to see the live response, or browse example payloads by status code.
{
"object": "list",
"type": "course",
"semester_id": "120252",
"university_id": "upd",
"results": [
{
"object": "course",
"course_id": "EEE 141",
"title": "Electrical Engineering 141",
"section_count": 4
}
],
"pagination": {
"limit": 8,
"offset": 0,
"has_more": false,
"next_offset": null,
"ranking_truncated": false
}
}List schedulable sections
Returns the actual class options a student can put on a schedule.
Authorizations
Bearer token
Send your API key in the Authorization header as Bearer upsked_api_v1_….
Query parameters
| Parameter | Required | Description |
|---|---|---|
| semester_id | Required | A semester_id from GET /semesters. |
| course_id | One of | A course_id from GET /courses. Pass one course at a time. |
| class_code | One of | Comma-separated class codes when the student already knows exact portal or CRS codes. Maximum 24 per request. |
| limit | Optional | Number of class options to return. Minimum 1, maximum 20, default 20. |
| offset | Optional | Pagination offset. Minimum 0, maximum 500, default 0. |
Response
| Field | Description |
|---|---|
| option_type | standalone, block for same-course bundles, or linked for cross-course bundles. |
| bundle_id | Present when multiple classes must be taken together. |
| bundle.class_codes | Class codes included in the bundle. |
| sections[] | Classes included in this option. Bundles have more than one row. |
| sections[].schedules[] | Meetings with day, time_start, time_end, room, and class_type. |
| sections[].remarks | Free-text catalog notes from the school registrar (e.g. delivery mode or slot wording). null when none. Informational only—not enrollment instructions. |
| missing_class_codes | Class codes not found when using class_code. Partial matches still return 200 with the classes that were found. |
| pagination | limit, offset, has_more, and next_offset for paging options. |
Request
Set parameters, copy a command, or send a live request.
Catalog context· tap to expand
curl -sS 'https://upsked.com/api/public/v1/sections?semester_id=&limit=20&offset=0&course_id=MATH+21' \ -H 'Authorization: Bearer YOUR_API_KEY'
Responses
Send a request to see the live response, or browse example payloads by status code.
{
"object": "list",
"type": "schedulable_option",
"semester_id": "120252",
"university_id": "upd",
"options": [
{
"object": "schedulable_option",
"option_type": "standalone",
"sections": [
{
"object": "section",
"course_id": "EEE 141",
"class_code": "52506",
"section_code": "WXY",
"schedules": [
{
"day": "M",
"time_start": "09:00",
"time_end": "10:30",
"room": "EE Institute",
"class_type": "lec"
}
],
"remarks": null
}
]
}
],
"pagination": {
"limit": 20,
"offset": 0,
"has_more": false,
"next_offset": null
}
}Changelog
The Catalog API is now generally available through the Public REST API. Create an API key from your Upsked account to search courses, load section schedules, and build tools for supported universities.