Public API
The learning graph over HTTP. The catalog routes are open to anyone; the routes that carry a learner's progress are scoped to the account that owns them.
Quickstart
No key, no sign-up: the first three calls work from a terminal right now.
# what is in the library
curl https://lightmysky.com/api/v1/subjects
# find a topic
curl "https://lightmysky.com/api/v1/topics?q=long+division&limit=3"
# what has to come first
curl https://lightmysky.com/api/v1/topics/$ID/prerequisitesWith a key, the same loop runs per learner: ask what to teach, record the answer, read the memory back.
curl -H "Authorization: Bearer $KEY" https://lightmysky.com/api/v1/learners/$ID/session/next
curl -X POST -H "Authorization: Bearer $KEY" \
-d '{"topicId":"$TOPIC","grade":"correct","mode":"review"}' \
https://lightmysky.com/api/v1/learners/$ID/session/answer
curl -H "Authorization: Bearer $KEY" https://lightmysky.com/api/v1/learners/$ID/memoryAuthentication
Catalog routes need nothing. Learner and pack routes need Authorization: Bearer <api key>, and a key belongs to one account: it never sees another account's learners. Keys are issued from the account that owns them, so create a free account first. Keep the key on a server. The same thing in machine-readable form: /auth.md.
Endpoints
| Method | Path | Auth | What it returns |
|---|---|---|---|
GET | /api/v1/subjects | none | Subjects with topic counts and domains |
GET | /api/v1/topics?q=&subject=&ageMin=&ageMax= | none | Search micro-topics |
GET | /api/v1/topics/:id | none | One topic, with evidence and assessment prompt |
GET | /api/v1/topics/:id/prerequisites?transitive=&strength= | none | What must come first, and why |
GET | /api/v1/topics/:id/unlocks | none | What this topic opens up |
GET | /api/v1/standards/:key/topics | none | Topics distilled from a curriculum standard |
GET | /api/v1/topics/:id/content | none | Lesson and video only; the graded questions come through a learner session, not by topic id |
GET | /api/v1/learners | key | Learners in your org |
POST | /api/v1/learners | key | Create a learner (displayName, birthYear) |
GET | /api/v1/learners/:id | key | Learner with memory summary and per-subject progress |
GET | /api/v1/learners/:id/session/next?subject= | key | What to ask now: placement probe, refresh, or new topic |
POST | /api/v1/learners/:id/session/answer | key | Record a grade; credit propagates, misses flag a prerequisite |
GET | /api/v1/learners/:id/memory | key | Per-topic memory: strength, recall probability, due date |
GET | /api/v1/learners/:id/mastery | key | Mastered and in-progress topic ids |
POST | /api/v1/learners/:id/mastery | key | Import external state (vouched, audited later) |
GET | /api/v1/learners/:id/levels | key | Per-subject level bands (two-year, grade-mapped) |
GET | /api/v1/packs | key | Custom packs you can use: own, shared in, public |
POST | /api/v1/packs | key | Create a pack (name, description) |
GET | /api/v1/packs/:id | key | A pack with topics and question pools |
GET | /api/v1/packs/format | none | The portable pack file format, as markdown for agents |
GET | /api/v1/packs/:id/export | key | Download a pack as a portable file |
POST | /api/v1/packs/import | key | Create a pack from a file; errors carry JSON paths |
PATCH | /api/v1/packs/:id | key | Set visibility, or restore: true to undelete |
DELETE | /api/v1/packs/:id | key | Soft-delete a pack; everything is kept and restorable |
POST | /api/v1/packs/:id/topics | key | Add a custom topic (subject, name, ages, prereqIds, lesson, videoUrl) |
PATCH | /api/v1/packs/:id/topics/:topicId | key | Update the lesson, video link, or prerequisites; restore: true undeletes |
DELETE | /api/v1/packs/:id/topics/:topicId | key | Soft-delete a topic; prereqs on it are waived until restore |
POST | /api/v1/packs/:id/topics/:topicId/items | key | Add a question (mcq, true_false, numeric) |
POST | /api/v1/packs/:id/share | key | Share the pack with an email |
PUT | /api/v1/learners/:id/packs | key | Switch packs on for a learner (packIds) |
GET | /api/v1/learners/:id/frontier?subject=&limit= | key | Ranked list of what is within reach |
GET | /api/v1/learners/:id/path/:topicId | key | Learning path to a goal topic |
GET | /api/v1/learners/:id/report?curriculum= | key | Standards coverage report |
Errors
Every answer is { "data": ... }. Every failure is { "error": ..., "code": ..., "status": ... } with the same status on the response. Branch on code; show error to a person.
| Status | code | When |
|---|---|---|
400 | invalid_request | A parameter is missing or the body did not parse |
401 | unauthorized | No key, or a key that is not recognised |
403 | forbidden | The key is valid but not allowed to do this |
404 | not_found | No such topic, learner, pack, or endpoint |
409 | conflict | The change collides with something already there |
429 | rate_limited | Too much at once; back off and retry |
500 | internal | Our fault. Retry, and tell us if it keeps happening |
Versioning and deprecation
The major version sits in the path: /api/v1. Anything additive, such as a new field or a new endpoint, arrives inside v1, so parse leniently and ignore what you do not recognise. A breaking change arrives as a new major version instead.
When a version is going to be retired, its responses carry Deprecation and Sunset headers (RFC 8594) naming the date, and it keeps answering for at least six months after that first warning. Nothing is deprecated today, and no route has a sunset date.
Limits
600 requests a minute, counted against your key when you send one and against your IP when you do not. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, so you can pace yourself without guessing; over the ceiling you get 429 with Retry-After. There is no quota to buy, because the whole thing is free, as /pricing.md says in machine-readable words. If you need more headroom, write to us before you point a fleet at it.
A write can carry an Idempotency-Key. Send the same key on the same path again within 24 hours and you get the first answer back, marked Idempotency-Replayed: true, so a retry after a dropped connection cannot record an answer twice. Only answers under 400 are kept, so a request that failed can be corrected and sent again under the same key.
Machine-readable descriptions
- /openapi.json: OpenAPI 3.1 for the open routes, with operation ids and a typed error schema
- /.well-known/api-catalog: RFC 9727 linkset
- /.well-known/ai-catalog.json: every machine-readable file on this domain
- /agent-instructions.md: when to use this site, and when not to
- /llms.txt and /llms-full.txt: the catalog as plain text
- Any public page answers in Markdown: send
Accept: text/markdown, add.mdto the address, or add?mode=agent
The graph endpoints (topics, prerequisites, unlocks, standards) also run as a stateless MCP server from the repo: pnpm --filter @atlas/mcp-server start. It is not hosted yet, so an agent has to run it locally. The hosted API adds the per-learner memory model on top. Tell us at info@lightmysky.com if a hosted one would help you.