README
¶
purdue-plan
Your Purdue degree audit as data, in the terminal.
MyPurduePlan (Ellucian DegreeWorks) will show you a progress bar and hand you a
PDF. purdue-plan gives you the same audit as structured output you can read at
a glance, grep, diff, or pipe — plus what-if audits against programs you are
not enrolled in, so you can price out a minor before committing to it.
$ purdue-plan todo
[MAJOR] Major in Mathematics -- 42.6% credits applied: 16
TODO Linear Algebra II need 1 class
WIP Ordinary Differential Equations need 1 class <- MA 36600
TODO Elements Of Complex Analysis need 1 class
TODO Foundations Of Analysis need 1 class
TODO Elements Of Algebra need 1 class
[OTHER] College Of Science Requirements -- 79.6%
TODO TECHNICAL WRITING AND PRESENTATION
TODO Technical Writing need 3 cr
Unofficial, and not affiliated with Purdue University. It reads your own records through the same API the web dashboard uses, after you sign in the same way you always do.
Install
go install github.com/upuddu/purdue-plan@latest
Or from source:
git clone https://github.com/upuddu/purdue-plan
cd purdue-plan && go build -o purdue-plan .
Requires Go 1.21+ and, for the browser login, any Chromium-family browser.
Signing in
DegreeWorks sits behind Purdue SSO (Microsoft Entra + Duo) and its session
cookie is HttpOnly, so no script can read it off the page. Rather than making
you copy it by hand, the CLI drives a real browser and captures the session
itself:
purdue-plan login
A browser window opens at MyPurduePlan. Sign in normally — Duo included — and the CLI takes it from there. Your password is typed into the browser, never into this tool; it only ever reads the resulting cookies.
It uses its own browser profile under ~/.config/purdue-plan/browser, so your
everyday browser data is untouched. Because Entra and Duo can remember that
profile, later sign-ins are often instant:
purdue-plan login --headless # reuse a remembered session, no window
purdue-plan login --browser brave # pick a specific browser
purdue-plan login --paste # paste a Cookie header by hand
purdue-plan login --cookie '…' # non-interactive, for scripts
Browsers are auto-detected — Chrome, Brave, Edge, Vivaldi, Opera, Arc,
Chromium — and the first installed one wins. Override with --browser (a name
or an executable path) or PURDUE_PLAN_BROWSER.
Firefox and Safari cannot be driven this way. They do not speak the DevTools protocol. Use
--paste: sign in in your own browser, then copy theCookie:header from DevTools → Network → anyapi/…request.
Every command checks the session first; with none stored you get these instructions instead of a stack trace. Sessions last about an hour.
Commands
| Command | What it does |
|---|---|
login / logout / whoami |
session management |
degrees |
your declared programs, each with per-block progress |
todo |
only what is still outstanding |
audit |
the full requirement tree, satisfied items included |
courses [pattern] |
the courses that can satisfy matching requirements |
classes |
what you are enrolled in (--history for every term) |
whatif |
audit a program you are not enrolled in |
list <entity> |
catalog codes — minors, majors, degrees, colleges, … |
search --discipline MA |
course catalog lookup |
Flags
| Flag | Effect |
|---|---|
--degree CODE |
which program to audit (default: your first) |
--all |
include already-satisfied requirements |
--allowed |
list qualifying courses inline |
--json |
machine-readable output |
--file PATH |
parse a saved audit offline, no session needed |
--no-inprogress / --no-prereg |
exclude in-progress or preregistered classes |
--major/--minor/--concentration/--program/--college CODE |
repeatable what-if goals |
--catalog-year YYYYTT |
e.g. 202510 |
Status markers: DONE satisfied · WIP a class is in progress · TODO nothing
applied yet. A rule sitting at 98% is almost always a course in progress, not a
shortfall.
What am I enrolled in?
$ purdue-plan classes
Fall 2026 (202710)
ECE 30100 Signals And Systems 3 cr WIP [preregistered]
ECE 30200 Probabilistic Methods 3 cr WIP [preregistered]
MA 36600 Ord Differential Eqs 4 cr WIP [preregistered]
6 classes, 19 credits
--history gives every term you have attended, with titles, grades and credits
— effectively an unofficial transcript. --term "Fall 2026" or --term 202710
filters. Transfer and exam credit is flagged ([transfer credit]); it posts
with a literal TR grade rather than a letter.
Evaluating a minor
The part the web UI makes tedious. Find the code, then score it against your real transcript:
$ purdue-plan list minors stat
STAT Statistics
$ purdue-plan whatif --degree MATH-BS --major MATH --minor STAT --allowed
[MINOR] Minor in Statistics -- 25%
TODO MINOR:Area I need 1 class
allowed: STAT 35000, STAT 35500, STAT 50300, QM 30500, IE 33000
DONE MINOR:Area II <- MA 41600
TODO MINOR:Area III need 1 class
allowed: STAT 51200
TODO MINOR:Area IV need 2 classes
allowed: IE 33600, IE 53000, STAT 41700, STAT 51300, STAT 51400
Courses you have already taken are credited automatically, so you see exactly how much of a program you have accidentally already finished.
Scripting
--json emits the flattened rule list:
purdue-plan todo --json | jq -r '.[] | select(.percent=="0") | .label'
# diff your progress between two points in time
purdue-plan audit --json > before.json
# …a semester later…
purdue-plan audit --json > after.json && diff before.json after.json
--file re-renders a saved audit with no session at all, which is handy for
archiving a snapshot each term.
API notes
Reverse-engineered from the dashboard bundle. Undocumented and unstable — Ellucian can change any of it without notice.
-
GET /api/users/myself— cheap session probe. -
GET /api/students/myself— your goals (degree, school, catalog year). RequiresAccept: application/vnd.hedtech.degreeworks.dashboard.students.v1+json. -
GET /api/audit?studentId=&school=°ree=&audit-type=AA&…— a declared program. RequiresAccept: application/vnd.net.hedtech.degreeworks.dashboard.audit.v1+json. -
POST /api/audit— what-if, plainapplication/json:{"studentId":"...","isKeepCurriculum":false,"school":"UG","degree":"MATH-BS", "catalogYear":"202510","goals":[{"code":"MINOR","value":"STAT"}],"classes":[]}catalogYearmust be non-empty or the service answers500with an empty body — no error message, no hint. That one cost an afternoon. Goal codes:MAJOR,MINOR,CONC,PROGRAM,COLLEGE,SPEC,LIBL. -
GET /api/validations/special-entities/{degrees,majors,minors,concentrations,programs,colleges,disciplines,…} -
GET /api/courses/search/byDiscipline?page=&discipline=&courseNumber=— page is 0-based.
/api/audit/planner/whatif exists in the bundle but rejected every payload
shape tried; the UI itself uses POST /api/audit, and so does this tool.
Response quirks handled here
percentCompleteand the credit fields arrive as a JSON string or number, inconsistently.classesAppliedToRuleis sometimes a bare array, sometimes an object wrappingclassArray.requirement.courseArrayholds the courses that satisfy a rule — this is whatcoursesand--allowedread.
Why not SSH through a jump host?
A reasonable idea, and it was worth measuring rather than assuming. Running the
API call from scholar.rcac.purdue.edu, i.e. from inside Purdue's network:
# on a Purdue login node, inside the campus network
$ curl -s -o /dev/null -w "%{http_code}\n" \
https://mypurdueplan.purdue.edu/api/users/myself
401
Same 401 as from anywhere else. Authentication is per-identity (an Entra/CAS
session), not per-network, so a Purdue source IP buys nothing. Separately,
jump.it.purdue.edu requires a password plus Microsoft MFA and refuses
key-only auth — so tunnelling could not have reduced the login burden anyway:
you would MFA into SSH and still need a MyPurduePlan session on the far side.
Security
- The session cookie is a bearer credential for your student record. It is
stored at
~/.config/purdue-plan/session.jsonwith0600permissions. Treat it like a password. - This tool never sees or stores your password. Authentication happens in the browser, against Purdue's own login pages.
- Read-only by construction. Every endpoint it calls is a
GET, except the what-if audit, which is aPOSTthat computes a hypothesis and stores nothing. DegreeWorks' actual write endpoint,/api/registration, is never called — this tool cannot add, drop, or change a class. - It reads only your own records — the same ones the web dashboard shows you. It grants no access you do not already have.
purdue-plan logoutclears the stored session; the browser profile lives in~/.config/purdue-plan/browserand can be deleted at any time.- Do not commit captured audits or HAR files; they contain student records. The
bundled
.gitignorealready excludes them.
License
MIT
Documentation
¶
There is no documentation for this package.