If you have kids in school using Canvas LMS (*.instructure.com), checking grades as a Parent / Observer through the web UI is surprisingly painful. To figure out why a grade moved—or whether an assignment is missing across two kids and twelve courses—you have to switch observed students in a dropdown, open each course one by one, click into the Grades tab, and mentally cross-reference category weights (Tests 55%, Quizzes 30%, Homework 15%).

I wanted a single command I could run from my terminal (or on a weekday cron job) that would pull every observed student’s courses, overall scores, assignment category weights, and submission statuses in seconds—and render them as both a colorized terminal table and a self-contained HTML dashboard.

That turned into canvas-scraper: a cross-platform Go binary with zero runtime dependencies.

canvas-scraper Terminal CLI Output


Architecture: Two-Tier Authentication & Local Cache

Many school Canvas instances sit behind SSO login flows, SAML redirects, or MFA prompts where generating a personal Canvas API token in the UI is disabled for student/parent accounts. On the other hand, driving a full headless browser to click through twelve course pages on every run is slow and fragile.

canvas-scraper uses a two-tier hybrid approach:

┌────────────────────────────────────────────────────────────────────────┐
│                          Canvas Scraper (Go)                           │
└────────────────────────────────────────────────────────────────────────┘
          │                                              │
          ▼ (If session expired / fresh)                 ▼ (If session valid)
┌───────────────────────────┐                  ┌───────────────────────────┐
│ Headless Chrome (Chromedp)│                  │ Go HTTP Client (REST API) │
│  - Authenticates securely │                  │  - Fetches users/observees│
│  - Saves session cookies  │───────►──────────│  - Fetches courses/scores │
│    to storage_state.json  │                  │  - Fetches groups/submits │
└───────────────────────────┘                  └───────────────────────────┘
                                                             │
                                                             ▼
                                               ┌───────────────────────────┐
                                               │ Local Cache Envelope      │
                                               │  (.cache_raw.json)        │
                                               └───────────────────────────┘
  1. Session Persistence (storage_state.json): On the very first run (or when cookies expire), canvas-scraper uses chromedp to complete the login flow and writes the session cookies to storage_state.json in Playwright-compatible format. (If your school requires MFA/2FA, running ./canvas-scraper --headed once lets you approve the prompt in a visible window.)
  2. Direct Go HTTP Client against /api/v1/: Once storage_state.json exists, Chrome isn’t launched at all—the scraper attaches the session cookies to a native Go http.Client and queries Canvas’s REST endpoints directly:
    • Discovers observed students via /api/v1/users/self/observees.
    • Pulls active courses and current grading-period scores via /api/v1/courses?enrollment_state=active&include[]=total_scores&include[]=current_grading_period_scores.
    • Joins assignment groups, category weights, and assignments in a single request per course (/api/v1/courses/:id/assignment_groups?include[]=assignments) with polite jittered pacing (150–300ms) between courses.
  3. Instant Local Data Cache (CACHE_TIMER): Raw API results are cached in output/.cache_raw.json (default 10m, configurable to 2h, 7d, etc.). Any subsequent CLI filter or sort runs instantaneously against the local cache in under 10 milliseconds without touching Canvas servers.

Interactive Single-File HTML Dashboard (--web)

Passing --web generates output/canvas_grades.html and automatically opens it in your default browser:

canvas-scraper –web Interactive Dark Mode HTML Report

The generated HTML file has zero external CSS or JavaScript dependencies—everything is inlined so you can email it, drop it on a phone, or archive it:

  • Automatic Light & Dark Mode via prefers-color-scheme.
  • Courses Summary Table with color-coded grade badges (A in green, B in cyan, C in yellow, <70% in red) and inline category weight formulas.
  • Per-Course Assignment Cards with direct clickable links back to each assignment on Canvas, points earned/possible, percentage, category weight, and status badges (Graded, Missing, Late, Excused, Unsubmitted).

Instant CLI Filtering & Sorting

Because filtering runs against the local cache envelope, you can slice the gradebook from the command line immediately:

# Sort assignments by urgency (Missing/Overdue -> Late -> Submitted -> Graded)
./canvas-scraper --sort status

# Filter to a specific student on a Parent/Observer account
./canvas-scraper --student Alex

# Show only assignments due in the next 7 days
./canvas-scraper --due-within 7d

# Investigate trouble spots: Missing work or graded items below 75% in major categories (>= 30% weight)
./canvas-scraper --missing-days 1
./canvas-scraper --below 75 --min-weight 30

AI-Ready Markdown (--markdown), CSV & JSON Deliverables

In addition to the terminal UI and HTML dashboard, canvas-scraper writes structured data files to output/:

  • canvas_grades.md (--markdown): A clean Markdown briefing containing an executive summary table, a pre-extracted Action Items section (all missing, overdue, or late assignments across all courses), and full course breakdowns—formatted so you can pipe it straight into a local LLM (glow output/canvas_grades.md or an AI study-plan prompt).
  • courses_summary.csv, assignment_weights.csv, assignments_detail.csv, and canvas_grades.json: Ready for spreadsheet tracking or custom scripts.

Download or Build from Source

Pre-compiled binaries for macOS (Apple Silicon & Intel), Linux (amd64 & arm64), and Windows (.exe) are published automatically via GitHub Actions: