Developers
Everything PantherWatch shows about Georgia State courses comes from a small JSON API. It’s public: no key, no sign-in. If you’re building a script or a browser extension, use it instead of scraping the results pages. To connect an AI assistant, use the MCP connector.
Connect an AI assistant (MCP)
PantherWatch is a remote MCP server. Any MCP-capable assistant or agent can connect, for example Claude, ChatGPT or Cursor. Once connected, it searches classes and reads a student’s tracked classes and schedule, and, if the student allows it, tracks sections and edits the schedule for them. This is the preferred way for an assistant to use PantherWatch.
Students: the AI Assistant page has step-by-step setup for Claude, ChatGPT and Cursor. This page is the reference for developers.
https://www.pantherwatch.app/api/mcp- In the assistant’s settings, add a custom connector (MCP server) and paste the URL. In Claude, for example: Customize → Connectors → + Add → Add custom connector.
- Sign in to PantherWatch if asked, then select Allow. Students can untick the write permissions there.
- Manage or disconnect it anytime in Settings → Connected apps.
| Scope | Allows |
|---|---|
read | Search courses; read tracked classes and schedule. Always granted. |
watches:write | Start and stop seat alerts. The student can leave it off. |
schedule:write | Add and remove schedule entries. The student can leave it off. |
| Tool | Scope | What it does |
|---|---|---|
list_terms | read | Terms in GoSolar, registrable ones first. |
search_subjects | read | Subject codes matching a word ("psych" → PSYC). |
find_courses | read | Course name or nickname to subject and course number from the GSU catalog ("calculus 2" → MATH 2212), with a confidence and the catalog description. |
search_sections | read | Live sections of a subject or course at one level (the level given, else the student's saved campus, else bachelor's): seats, waitlist, meetings, delivery mode. |
get_section_details | read | Grade history, RateMyProfessors rating and syllabus for one section. |
get_course_requirements | read | Prerequisites, corequisites, registration restrictions and catalog description of one section. |
get_syllabus_summary | read | An AI summary of one section's posted syllabus: grading weights, exams, attendance, textbook, workload. |
list_watches | read | The student's tracked sections, optionally with live seats. |
list_schedule | read | The student's PantherWatch schedule, by term. |
check_conflicts | read | Time clashes between CRNs, from live meeting times. |
add_watch / remove_watch | watches:write | Start or stop a seat alert (email when a seat opens). |
add_to_schedule / remove_from_schedule | schedule:write | Edit the schedule; adding reports any clashes. |
For client developers: Streamable HTTP, OAuth 2.1 per the MCP authorization spec. Discovery starts at /.well-known/oauth-protected-resource/api/mcp (also at the root location) and /.well-known/oauth-authorization-server. Clients identify themselves with a Client ID Metadata Document URL or register at /api/oauth/register; PKCE (S256) is required, redirect URIs must be https, localhost or an app’s own scheme (like cursor:), access tokens last an hour and refresh tokens rotate. At the token endpoint, metadata-document clients authenticate with none or private_key_jwt (RS256, ES256 or PS256, keys from their jwks_uri). The server allows about 30 tool calls a minute per student.
Overview
- Base URL:
https://www.pantherwatch.app. All responses are JSON (application/json). - The
GETendpoints sendAccess-Control-Allow-Origin: *, so browser code on any site can call them. They answerOPTIONSpreflights too. - A typical flow: list terms, then search a subject (optionally one course number) for that term, then fetch grades and ratings for the sections you care about.
- Term codes are
YYYYMM:01spring,05summer,08fall (202701is Spring 2027). Read them from/api/courses/termsrather than guessing.
Fair use
Free for personal and student use. Be gentle: at most 1 request per second. Searches go straight to GSU’s registration system (GoSolar), and ratings to RateMyProfessors, so a burst from one script slows things down for every student using the site. Cache what you can, fetch meta only for the sections you will actually show, and don’t poll seat counts in a loop (sign in and use Track instead; PantherWatch emails you when a seat opens).
PantherWatch is not affiliated with Georgia State University. Data can be incomplete or out of date; GoSolar is the source of truth for registration.
List terms
GET/api/courses/terms
Terms you can register for come first (newest first), then view-only past terms with viewOnly: true. No parameters.
curl https://www.pantherwatch.app/api/courses/terms{
"terms": [
{ "code": "202701", "description": "Spring Semester 2027", "viewOnly": false },
{ "code": "202608", "description": "Fall Semester 2026", "viewOnly": false },
{ "code": "202605", "description": "Summer Semester 2026 (View Only)", "viewOnly": true },
…
]
}Find subjects
GET/api/courses/subjects?term&q
| Parameter | Required | Description |
|---|---|---|
term | Yes | 6-digit term code. |
q | No | Text to match against the code or name (up to 64 characters). Blank returns the first 50 subjects. |
Returns at most 50 subjects whose code or description contains q.
curl "https://www.pantherwatch.app/api/courses/subjects?term=202701&q=comp"{
"subjects": [
{ "code": "APCP", "description": "APPLIED COMPOSITION" },
{ "code": "CIS", "description": "COMPUTER INFORMATION SYSTEMS" },
{ "code": "CSC", "description": "COMPUTER SCIENCE" }
]
}Find a course by name
GET/api/courses/catalog?q&level&limit
| Parameter | Required | Description |
|---|---|---|
q | Yes | A course name, nickname or code, 2–120 characters: calculus 2, intro to programming, data structures, CSC 1301. |
level | No | US undergraduate (bachelor’s), UA courses taught at Perimeter College (associate), GS graduate, LW law. Leave it out for all levels. |
limit | No | Courses to return, 1–20. Default 5. |
- Searches GSU’s course catalog (courses, not sections), copied nightly from GoSolar’s course catalog for the terms open for registration. Whether a course runs in a term is Search sections’ job.
coursesare ranked byscore(0–1): a course code inqor a common nickname scores 1, title words up to 0.95, description words lower.confidenceishighwhen the first course is a clear winner,ambiguouswhen several fit (ask the user which one),nonewhen nothing matched.matchedBysays how the first course matched:code,nickname,titleordescription.titleis the catalog’s full title;descriptionis the catalog text (prerequisites first, when there are any).levelis PantherWatch’s reading of the course number: 5000 and up is graduate, courses Perimeter College owns are associate. Graduate courses are often open to undergraduates too.
curl "https://www.pantherwatch.app/api/courses/catalog?q=calculus%202&level=US"{
"query": "calculus 2",
"level": "US",
"confidence": "high",
"matchedBy": "nickname",
"matched": "calculus 2",
"courses": [
{
"subject": "MATH",
"courseNumber": "2212",
"title": "Calculus of One Variable II",
"description": "Prerequisite: Math 2201 or Math 211 with a C or higher.\nApplications and techniques of integration; transcendental and inverse trigonometric functions; …",
"credits": "4",
"level": "US",
"score": 1
},
{
"subject": "MATH",
"courseNumber": "2202",
"title": "Calculus for the Life Sciences II",
"credits": "4",
"level": "US",
"score": 0.6
},
…
]
}Search sections
GET/api/courses/search?term&subject&courseNumber&level
| Parameter | Required | Description |
|---|---|---|
term | Yes | 6-digit term code. |
subject | Yes | One subject code, 2–8 letters (case-insensitive), e.g. CSC. |
courseNumber | No | 1–8 letters or digits, e.g. 1301 or 1301L. Leave it out to get the whole subject. |
level | No | GoSolar level: US bachelor’s (undergraduate, Atlanta campus and Online), UA associate (Perimeter College), GS graduate. Leave it out for all levels. The site’s search form and the assistant default to US. |
- Returns every matching section in one response: no paging, no 12-per-page limit. One subject per call; for several subjects, make one call each (about a second apart).
- Each section is GoSolar’s own record, passed through. Useful fields:
seatsAvailable(can be negative when over-enrolled; whencrossListis set the section shares a seat pool, and onlymin(seatsAvailable, crossListAvailable)seats are takeable),waitCount,waitCapacity,creditHourLow/creditHourHigh(a range for variable-credit courses, elsecreditHourLoworcreditHours),faculty[].displayName(“Last, First”;primaryIndicatormarks the main instructor), andinstructionalMethodDescriptionplussectionAttributesfor the delivery mode (“Fully Distance 95% tech”, “Hybrid/Partially Online”). - Meeting days are booleans on
meetingsFaculty[].meetingTime(monday…sunday).beginTime/endTimeare 24-hourHHMMstrings,nullwhen the time is TBA or the class is asynchronous online. A section can have several meeting rows (lecture + lab, or different date ranges). - Seat counts are live, so this endpoint is never cached: every call is a fresh GoSolar search (about a second for one course, longer for a whole large subject).
curl "https://www.pantherwatch.app/api/courses/search?term=202701&subject=CSC&courseNumber=1301&level=US"{
"sections": [
{
"term": "202701",
"termDesc": "Spring Semester 2027",
"courseReferenceNumber": "19010",
"subject": "CSC",
"subjectDescription": "COMPUTER SCIENCE",
"courseNumber": "1301",
"sequenceNumber": "002",
"courseTitle": "PRINCIPLES OF COMPUTER SCI I",
"scheduleTypeDescription": "Lecture",
"campusDescription": "Atlanta",
"creditHours": null,
"creditHourLow": 3,
"creditHourHigh": null,
"maximumEnrollment": 120,
"enrollment": 22,
"seatsAvailable": 98,
"waitCapacity": 99,
"waitCount": 0,
"waitAvailable": 99,
"openSection": true,
"faculty": [
{
"displayName": "Saghaeiannejad Esfahani, Sayed Hossein (Hossein) ",
"primaryIndicator": true,
…
}
],
"meetingsFaculty": [
{
"meetingTime": {
"beginTime": "1100",
"endTime": "1215",
"monday": true, "tuesday": false, "wednesday": true, "thursday": false,
"friday": false, "saturday": false, "sunday": false,
"buildingDescription": "Langdale Hall",
"room": "700",
"startDate": "01/11/2027",
"endDate": "05/04/2027",
"meetingTypeDescription": "Class",
…
},
…
}
],
"sectionAttributes": [
{ "code": "FMTH", "description": "Area F Mathematics Electives", … },
{ "code": "LF2F", "description": "Face to Face - Instruction", … },
…
],
"instructionalMethodDescription": "*Tech enhanced classrm inst re",
…
},
…
]
}Grades, ratings and syllabus
GET/api/courses/meta?term&subject&courseNumber&crn&instructor
| Parameter | Required | Description |
|---|---|---|
term | Yes | 6-digit term code. |
subject | Yes | Subject code. |
courseNumber | Yes | Course number. |
crn | Yes | 5-digit CRN (used for the syllabus lookup). |
instructor | No | The primary instructor’s faculty[].displayName from the search response, as-is. Without it, rating is null and only course-wide grades come back. |
grades: GSU grade history for the course over recent terms.overallis course-wide,professorshas one entry per instructor, andinstructorDistributionis the queried instructor’s own history wheninstructorHasTaughtis true.gpais enrollment-weighted;dwfPercentcounts D, F, WF and W.hasData: falsemeans no history (often a new course).rating: RateMyProfessors summary.found: falsemeans no match, which is “unknown”, not “bad”.avgRatingandavgDifficultyare out of 5.syllabus: a public PDF when the department has posted one.- Each part is
nullif its lookup fails; a failed lookup never turns the whole response into an error.
curl -G https://www.pantherwatch.app/api/courses/meta \
--data-urlencode term=202701 --data-urlencode subject=CSC \
--data-urlencode courseNumber=2720 --data-urlencode crn=12992 \
--data-urlencode "instructor=Islam, S M Towhidul (Towhid)"{
"grades": {
"subject": "CSC",
"courseNumber": "2720",
"hasData": true,
"termsIncluded": ["202605", "202601", "202508", "202505", …],
"overall": { "professor": null, "gpa": 3.08, "dwfPercent": 12, "total": 2077, "sectionsCount": 99, … },
"professors": [ … ],
"instructorQueried": "Islam, S M Towhidul (Towhid)",
"instructorHasTaught": true,
"instructorDistribution": {
"professor": "Islam, S M Towhidul",
"gpa": 2.65,
"dwfPercent": 16.1,
"total": 790,
"withdrawCount": 32,
"sectionsCount": 36,
"termsTaught": ["202601", "202508", "202505", "202501", "202408"],
"gradeCounts": { "A+": 38, "A": 76, "A-": 64, "B+": 74, "B": 104, "B-": 83,
"C+": 60, "C": 163, "C-": 0, "D": 60, "F": 35, "WF": 0 }
}
},
"rating": {
"professorQueried": "Islam, S M Towhidul (Towhid)",
"found": true,
"matchedName": "S M Towhidul Islam",
"department": "Computer Science",
"profileUrl": "https://www.ratemyprofessors.com/professor/2921056",
"avgRating": 3.7,
"avgDifficulty": 2.8,
"numRatings": 13,
"wouldTakeAgainPercent": 76.9231,
"distribution": { "r1": 2, "r2": 1, "r3": 2, "r4": 2, "r5": 6 },
"topTags": [],
…
},
"syllabus": { "url": "https://cdn.gsu.edu/static/syllabi-public/files/0021772031792993727494.pdf" }
}Prerequisites and restrictions
GET/api/courses/requirements?term&crn
| Parameter | Required | Description |
|---|---|---|
term | Yes | 6-digit term code. |
crn | Yes | 5-digit CRN of the section. |
prerequisitesisnullwhen there are none.textis GoSolar’s wording;treenestsand/orgroups ofcourse,testandother(e.g. student attributes) nodes;itemslists every course once.restrictions: the student must match one value of eachmustBegroup and none of themustNotBeones.descriptionis the catalog text, ornull.
curl "https://www.pantherwatch.app/api/courses/requirements?term=202701&crn=19010"{
"term": "202701",
"crn": "19010",
"description": "Principles of Computer Science I.\nPrerequisite: Only for STEM majors and minors. …",
"prerequisites": {
"text": "(Course or Test: MATHEMATICS 1111 to 1113 Minimum Grade of C May be taken concurrently.) or …",
"rule": "Prereq",
"tree": {
"type": "or",
"children": [
{ "type": "course", "subject": "MATH", "subjectDescription": "MATHEMATICS", "courseNumber": "1111",
"courseNumberTo": "1113", "minimumGrade": "C", "concurrent": true },
…,
{ "type": "and", "children": [ … ] }
]
},
"items": [ { "subject": "MATH", "courseNumber": "1111", "courseNumberTo": "1113", "minimumGrade": "C", … }, … ]
},
"corequisites": [
{ "subject": "CSC", "subjectDescription": "COMPUTER SCIENCE", "courseNumber": "1301L",
"title": "PRINCIPLES COMPUTER SCI LAB I" }
],
"restrictions": {
"mustBe": [
{ "kind": "major", "label": "Fields of Study (Major, Minor or Concentration)",
"values": ["Computer Science (CSC)", "Data Science (DSCI)", …] }
],
"mustNotBe": [
{ "kind": "level", "label": "Levels", "values": ["Undergrad Associate Semester (UA)"] },
…
],
"specialApprovals": [],
"other": []
}
}Batch meta (same-origin only)
POST/api/courses/meta/batch
What the results page uses to load grades and ratings for a whole page of cards at once. Body: { "items": [{ term, subject, courseNumber, crn, instructor? }, …] } (1–50 items); response: { "items": [...] } in the same order, each shaped like /api/courses/meta. It only accepts requests from pantherwatch.app pages (cross-origin calls get 403 forbidden) and sends no CORS headers. From your own code, call GET /api/courses/meta once per section instead.
Errors
Errors share one shape. 400 invalid_request means a parameter is missing or malformed (the message names it). 502 upstream_unavailable means GoSolar didn’t answer; wait a minute and retry. 500 internal_error is a bug on our side.
{ "error": { "code": "invalid_request", "message": "term: term must be a 6-digit term code" } }Caching
| Endpoint | Cache-Control |
|---|---|
/api/courses/terms | public, s-maxage=300, stale-while-revalidate=600 |
/api/courses/subjects | public, s-maxage=3600, stale-while-revalidate=86400 |
/api/courses/catalog | public, s-maxage=3600 |
/api/courses/requirements | public, s-maxage=3600, stale-while-revalidate=86400 |
/api/courses/meta | public, s-maxage=600, stale-while-revalidate=3600 |
/api/courses/search, /api/courses/meta/batch | private, no-store (live seat counts) |
The s-maxage values apply at our CDN, which strips them, so your client sees Cache-Control: public. Repeating a cached request is cheap; a search always reaches GoSolar. Grade history changes once a term and ratings rarely, so cache meta on your side for at least a day.
Reading the web pages
If you must read the rendered pages, don’t parse the text or the styling. Each card’s seat panel and meeting list carry stable data attributes: data-days lists meeting days in M T W R F S U order (Monday–Sunday, empty for no set days), and data-start/data-end are 24-hour HHMM (absent when TBA). data-seats-available is the takeable count: for a cross-listed section, the smaller of its own seats and the shared pool’s. The Rate My Professors pill has data-rmp (average out of 5) and the grade line data-gpa, with data-gpa-scope saying whether it is the professor’s own history for the course (instructor) or course-wide (course). Either is absent when there is no data.
<div data-crn="19010" data-subject="CSC" data-course-number="1301"
data-term="202701" data-seats-available="98" data-wait-count="0">…</div>
<ul data-crn="19010" …>
<li data-days="MW" data-start="1100" data-end="1215">…</li>
</ul>
<span data-rmp="3.7">…</span>
<div data-gpa="2.65" data-gpa-scope="instructor">…</div>Results pages are not for crawlers (robots.txt disallows them): each one is a live GoSolar search.
AI assistants
The MCP connector is the way to connect an assistant: any MCP-capable assistant can use it with no setup beyond pasting the URL, and it can act on the student’s account. Agents that can’t use connectors can call the public API above directly. Signed-in students can also chat with PantherWatch’s own assistant on the AI Assistant page; it uses the same tools as the connector and has no API of its own.
- /llms.txt (a short site summary for language models)