Browse docs

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.

  1. Choose a school with /universities.
  2. Choose a term with /semesters.
  3. Search courses in that term with /courses.
  4. Fetch valid class options with /sections.

IDs to pass between calls:

  • university_id: school slug (upd, admu, dlsu, …)
  • semester_id: term (required for /courses and /sections)
  • course_id: from /courses, used by /sections
  • class_code: use instead of course_id when 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.

  1. Get a key: open Account settings, then Public REST API (catalog:read).
  2. List schools: GET /universities
  3. List terms: GET /semesters?university=upd, then copy a semester_id
  4. Search courses: GET /courses?semester_id=<id>&query=math, then copy a course_id
  5. Get sections: GET /sections?semester_id=<id>&course_id=<course_id>

Every request needs this header:

http
Authorization: Bearer upsked_api_v1_YOUR_KEY

Base URL: https://upsked.com/api/public/v1

bash
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_id before /courses or /sections.
  • If section_count is 0, skip /sections for that course.
  • For /sections, send course_id or class_code, not both.

Authentication

http
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

BaseNotes
https://upsked.com/api/public/v1Default. Always available.
https://api.upsked.comOptional subdomain alias (same handlers)
http://localhost:3000/api/public/v1Local development

Paths: /universities, /semesters, /courses, /sections.

Conventions

  • JSON uses snake_case.
  • Lists: { "object": "list", "type": "<kind>", "results": [ ... ] }.
  • Section responses use options because 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, and next_offset.
  • Times are local school times in HH:mm format.

Error codes

HTTPCodeWhen
400bad_requestInvalid params, unknown semester_id or university
401unauthorizedMissing or malformed Authorization header
401invalid_api_keyRevoked, expired, or invalid key
404not_foundUnknown course_id, or no matching class_code
429rate_limitedPer-IP or per-account rate cap
200(none)Partial class_code match returns missing_class_codes

Rate limits

Responses may include:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-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

MistakeFix
Calling /courses before choosing a termCall /semesters first and pass the returned semester_id.
Treating section_count as a section listUse section_count only to decide whether to call /sections.
Calling /sections with both course_id and class_codePass exactly one of them.
Assuming every school supports every featureRead capabilities from /universities before search or sections.
Caching section pages without query paramsInclude semester_id, course_id or class_code, limit, and offset in cache keys.
GET/universities

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

FieldDescription
objectuniversity for each school row.
university_idSchool slug such as upd, admu, or dlsu. Use it as university in GET /semesters.
labelSchool name to show in a picker.
statusactive means generally available. pilot means coverage may still be limited.
capabilitiesTells you which catalog features work for the school, such as catalog_course_search and catalog_sections.
semester_id_hintExample 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.

API key
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"
    }
  ]
}
GET/semesters

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

ParameterRequiredDescription
universityOptionalA university_id from GET /universities. Omit it only if you are building your own cross-school term list.

Response

FieldDescription
semester_idTerm id required by GET /courses and GET /sections.
labelTerm name to show in a picker.
termSchool-specific term code.
year_start / year_endAcademic year bounds.
is_currentTrue when this is the recommended term for the school.
university_idSchool slug for the term.
default_semester_idRecommended default when university is provided. Null on a global list.
default_semester_ids_by_universityMap 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.

API key
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"
    }
  ]
}
GET/courses

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

ParameterRequiredDescription
queryRequiredWhat the student typed. Matches course code and title.
semester_idRequiredA semester_id from GET /semesters.
limitOptionalNumber of course matches to return. Minimum 1, maximum 20, default 8.
offsetOptionalPagination offset. Minimum 0, maximum 400, default 0. Use next_offset when has_more is true.

Response

FieldDescription
course_idCourse lookup value. Pass it to GET /sections with the same semester_id.
titleCourse title as published by the school catalog.
section_countHow many class options exist for this course in the selected term. If 0, skip GET /sections.
pagination.has_moreTrue when another page is available.
pagination.next_offsetOffset to pass on the next request. Null when there is no next page.
pagination.ranking_truncatedTrue 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.

API key
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
  }
}
GET/sections

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

ParameterRequiredDescription
semester_idRequiredA semester_id from GET /semesters.
course_idOne ofA course_id from GET /courses. Pass one course at a time.
class_codeOne ofComma-separated class codes when the student already knows exact portal or CRS codes. Maximum 24 per request.
limitOptionalNumber of class options to return. Minimum 1, maximum 20, default 20.
offsetOptionalPagination offset. Minimum 0, maximum 500, default 0.

Response

FieldDescription
option_typestandalone, block for same-course bundles, or linked for cross-course bundles.
bundle_idPresent when multiple classes must be taken together.
bundle.class_codesClass 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[].remarksFree-text catalog notes from the school registrar (e.g. delivery mode or slot wording). null when none. Informational only—not enrollment instructions.
missing_class_codesClass codes not found when using class_code. Partial matches still return 200 with the classes that were found.
paginationlimit, offset, has_more, and next_offset for paging options.

Request

Set parameters, copy a command, or send a live request.

API key
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.