Skip to main content

βŒ₯ API Reference

Public HTTP endpoints

The endpoints below power our own surfaces (search, leaderboards, profile pages). They're publicly callable β€” no API key for read-only routes. Auth-required routes use the same Supabase session cookie our web app does; if you want third-party access we can issue a bearer token; contact us.

Search

GET/api/search?q=<query>&kind=<surface?>PUBLIC

Full-text search across lessons, glossary, cheatsheets, guides, projects, and learning paths. Scores title > module > body, with small per-surface boost. Top 30 with <mark>-wrapped snippet. Optional `&kind=lesson|glossary|cheatsheet|guide|project|path` filter.

REQUEST

# query length must be between 2 and 80 chars
curl "https://learnpython.academy/api/search?q=list+comprehension"

RESPONSE

{
  "results": [
    {
      "id": "lesson-66",
      "title": "List comprehensions",
      "url": "/lesson/lesson-66",
      "kind": "lesson",
      "trackId": "foundations",
      "trackName": "Python Foundations",
      "moduleTitle": "Module 4 Β· Strings & comprehensions",
      "type": "write",
      "is_free": false,
      "score": 15,
      "snippet": "…use <mark>list comprehension</mark> instead of map+lambda…"
    }
  ],
  "total": 13
}
  • Edge-cached 5 minutes.
  • No API key needed.
  • Index covers ~1,350 lessons + 60 glossary terms + 8 cheatsheets + 7 guides + 12 projects + 9 paths.

Leaderboards

GET/api/leaderboardPUBLIC

Top 50 learners by total XP. Only display_name + counters β€” no PII.

REQUEST

curl "https://learnpython.academy/api/leaderboard"

RESPONSE

{
  "entries": [
    { "id": "uuid", "display_name": "Ada K.", "xp_total": 12450, "streak_days": 18 },
    …
  ]
}
  • Edge-cached 60s.
  • Requires migration 20260517_public_leaderboard.sql to be applied.
GET/api/challenges/leaderboard?challenge_id=<slug>PUBLIC

Per-challenge ranking. Score column varies by kind (perf=ms ASC, golf=chars ASC, speed=correct DESC).

REQUEST

curl "https://learnpython.academy/api/challenges/leaderboard?challenge_id=perf-fizzbuzz"

RESPONSE

{
  "entries": [
    {
      "user_id": "uuid",
      "display_name": "Bob M.",
      "execution_ms": 0.74,
      "correct_count": null,
      "char_count": null,
      "rank": 1,
      "created_at": "2026-05-18T14:23:00Z"
    }
  ]
}
  • Edge-cached 30s.

Profiles

GET/u/<handle>PUBLIC

HTML profile page for an opted-in learner. Returns 404 when handle missing or profile not public.

REQUEST

# HTML page β€” no JSON variant. Use it in an iframe or scrape.
curl "https://learnpython.academy/u/ada-k"

RESPONSE

<!doctype html>…

Feeds

GET/blog/rss.xmlPUBLIC

RSS 2.0 feed of every blog post. Auto-discovered by Feedly / NetNewsWire / Reeder.

REQUEST

curl "https://learnpython.academy/blog/rss.xml"

RESPONSE

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">…
GET/sitemap.xmlPUBLIC

Standard XML sitemap covering 1000+ URLs β€” lessons, tracks, paths, projects, blog, public profile pages.

REQUEST

curl "https://learnpython.academy/sitemap.xml"

RESPONSE

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">…

Glossary

GET/api/glossaryPUBLIC

Flat list of every Python term in the CodeMentor glossary β€” definition, category, linked lesson, public URL. CORS open, edge-cached 1 hour.

REQUEST

curl "https://learnpython.academy/api/glossary"

RESPONSE

{
  "items": [
    {
      "term": "Decorator",
      "slug": "decorator",
      "definition": "A higher-order function that wraps another to add behavior β€” logging, caching, auth. `@cache` is one.",
      "category": "Functions",
      "lesson": "lesson-115",
      "url": "https://learnpython.academy/glossary/decorator"
    }
  ],
  "total": 60
}
  • CORS open.
  • Edge-cached 1 hour.
GET/api/glossary/<slug>PUBLIC

Single term by slug. Same shape as one item from /api/glossary, plus an `example` code snippet when available.

REQUEST

curl "https://learnpython.academy/api/glossary/decorator"

RESPONSE

{
  "term": "Decorator",
  "slug": "decorator",
  "definition": "A higher-order function that wraps another…",
  "category": "Functions",
  "lesson": "lesson-115",
  "example": "@functools.cache\ndef fib(n): ...",
  "url": "https://learnpython.academy/glossary/decorator"
}
  • 404 if slug not in the glossary.

Cheatsheets

GET/api/cheatsheetPUBLIC

Index of every cheatsheet β€” title, group count, entry count, public URL. CORS open, edge-cached 1 hour.

REQUEST

curl "https://learnpython.academy/api/cheatsheet"

RESPONSE

{
  "items": [
    {
      "slug": "fastapi",
      "title": "FastAPI cheatsheet β€” routes, Pydantic, auth, async, deploy",
      "track_id": "fastapi",
      "emoji": "⚑",
      "updated": "2026-05",
      "group_count": 6,
      "entry_count": 17,
      "url": "https://learnpython.academy/cheatsheet/fastapi"
    }
  ],
  "total": 8
}
GET/api/cheatsheet/<slug>PUBLIC

Full structured cheatsheet body β€” every group + every snippet (label, why, code).

REQUEST

curl "https://learnpython.academy/api/cheatsheet/fastapi"

RESPONSE

{
  "slug": "fastapi",
  "title": "FastAPI cheatsheet β€” …",
  "groups": [
    {
      "title": "Hello + routing",
      "entries": [
        { "label": "Minimal app", "why": "Spins up a server immediately.", "code": "from fastapi import FastAPI\napp = FastAPI()\n…" }
      ]
    }
  ]
}
  • 404 if slug not in the cheatsheet collection.

Embeds

GET/api/embed/lesson/<id>PUBLIC

JSON metadata for a single lesson β€” title, task, type, quiz options + correctIndex (for quiz/predict lessons), and links back to the full lesson + iframe URL. CORS open, edge-cached 5 minutes.

REQUEST

curl "https://learnpython.academy/api/embed/lesson/lesson-01"

RESPONSE

{
  "id": "lesson-01",
  "title": "Hello, World!",
  "type": "write",
  "is_free": true,
  "xp_reward": 75,
  "task": "Print Hello, World!",
  "track": { "id": "foundations", "name": "Python Foundations", "emoji": "🐍" },
  "quiz": null,
  "embed_url": "https://learnpython.academy/embed/lesson/lesson-01",
  "lesson_url": "https://learnpython.academy/lesson/lesson-01"
}
  • CORS open (Access-Control-Allow-Origin: *).
  • Edge-cached 5 minutes.
GET/embed/lesson/<id>PUBLIC

Iframe-ready HTML page for a single lesson. No nav, no footer, minimal UI. For quiz/predict types it includes a working check-answer flow. Frame-ancestors *, so it embeds anywhere.

REQUEST

<iframe
  src="https://learnpython.academy/embed/lesson/lesson-66"
  width="100%" height="520"
  style="border:1px solid var(--border);border-radius:12px"
></iframe>

RESPONSE

<!doctype html>…
  • Includes a 'Try full lesson β†’' link back to /lesson/<id>?ref=embed.
  • Resize via the iframe height attribute; the embed is responsive.

User data (auth required)

GET/api/progressAUTH

List of lesson IDs the authenticated user has completed.

REQUEST

# Auth via Supabase session cookie. No external API key.

RESPONSE

{ "completed": ["lesson-1", "lesson-2", …] }
GET/api/bookmarksAUTH

User's saved-for-later lessons.

REQUEST

# Auth required.

RESPONSE

{ "bookmarks": [{ "lesson_id": "lesson-66", "created_at": "…" }] }
GET/api/achievementsAUTH

Unlocked badges + the snapshot of metrics used to compute them.

REQUEST

# Auth required.

RESPONSE

{
  "badges": [{ "id": "ten-lessons", "tier": "bronze", "icon": "πŸ“š" }],
  "snapshot": {
    "completed_count": 12,
    "xp_total": 850,
    "streak_days": 4,
    "missions_completed": 1,
    …
  }
}
POST/api/bookmarksAUTH

Add a bookmark.

REQUEST

curl -X POST "https://learnpython.academy/api/bookmarks" \
  -H "Content-Type: application/json" \
  -d '{"lesson_id": "lesson-66"}'

RESPONSE

{ "ok": true }

Need a webhook? Bulk export? Bearer-token API access? Email support@learnpython.academy and tell us what you're building β€” we'll wire something up.

Rate limits: ~60 reqs/min/IP on /api/search; everything else is edge-cached. Abuse β†’ throttled or blocked.