If you have ever tried to build anything in education tech, you have probably hit the same wall: curriculum data is either a flat list of standards in a PDF, or it is locked inside someone's product.
A flat list tells you what a child should learn in Year 2. It does not tell you what they need to know first. And that second question is the one that matters if you are building adaptive practice, a tutoring app, a learning-path generator, or an AI tutor that needs to know where a kid is stuck.
os-taxonomy from Marble is an attempt to fix that. It is an open dataset that breaks primary and elementary learning into small, teachable ideas and connects them with prerequisite edges. It has picked up around 4.8k stars, which is a lot for a pure data repo.
Here is what is in it, how to use it, and whether it is worth your time.
What you actually get
It is a graph, shipped as plain JSON.
- 1,591 micro-topics (nodes). Each is one teachable idea, like "Building sentences" or "Apparent brightness of stars".
-
3,225 prerequisite edges. "Topic X depends on topic Y", each tagged
hardorsoft, with a one-line reason. - Curriculum alignment. Topics link to the standards they came from: Common Core, NGSS, the UK National Curriculum, the C3 social studies framework, and IB PYP.
- 176 domain clusters. Short, parent-friendly summaries per subject, domain, and age band.
- Teaching research. 1,051 records on how to teach Science and Mathematics topics, with activities, feedback ideas, and assessment guidance. More on this below.
Coverage is weighted toward the core subjects:
| Subject | Topics |
|---|---|
| Science | 547 |
| Mathematics | 504 |
| English | 286 |
| History | 90 |
| Personal & Social Development | 88 |
| Life Skills | 37 |
| Computing | 21 |
| Learning to Learn | 18 |
There is no runtime, no package, and no dependencies. You clone the repo and read JSON files.
The data model
Everything lives in data/:
| File | Contents |
|---|---|
topics.json |
The micro-topics (nodes) |
dependencies.json |
Prerequisite edges |
curriculum-standards.json |
Source standards, grouped by curriculum |
clusters.json |
Domain summaries |
teaching-research.json |
Teaching guidance per topic (about 23 MB) |
manifest.json |
Counts and SHA-256 checksums |
JSON Schemas for each file are in schema/.
A topic looks like this (trimmed):
{
"id": "mt_N8CpN1EJrP",
"type": "CONCEPTUAL",
"subject": "English",
"domain": "Grammar & Punctuation",
"name": "Building sentences",
"ageRangeStart": 4,
"ageRangeEnd": 6,
"centrality": 0.257,
"evidence": [
"Distinguish between complete sentences and fragments",
"Compose a complete sentence with a subject and verb"
],
"assessmentPrompt": "If {{name}} says something like \"The dog\", can they tell you...",
"standards": ["ccss-ela:L.K.1f", "uk-nc-2013:Eng.App2.Y1.Sent.1"]
}
A few fields are worth calling out:
-
typeclassifies the kind of knowledge: conceptual, procedural, representational, language, or meta. -
evidenceis a list of observable mastery criteria. This is the useful bit for anyone building assessments. -
centralitygives you a rough sense of how connected a topic is in the graph. -
assessmentPromptis a natural-language check with a{{name}}placeholder for the child's name. Replace or strip it before showing it to anyone.
And an edge:
{
"topicId": "mt__00ZSLnB7p",
"prerequisiteId": "mt_VBl1T1sFCM",
"strength": "hard",
"reason": "Must understand vibrations make sound before finding volume patterns"
}
Read it as "topicId depends on prerequisiteId". Flip it and you get "unlocks".
Getting started in five minutes
git clone https://github.com/withmarbleapp/os-taxonomy.git
cd os-taxonomy
node scripts/validate.mjs # checks structure, references, and checksums
Find the direct prerequisites of a topic
This uses JSON import attributes, so use a recent Node version (22 or newer is safest):
import topics from './data/topics.json' with { type: 'json' };
import deps from './data/dependencies.json' with { type: 'json' };
const byId = new Map(topics.topics.map(t => [t.id, t]));
const prereqsOf = id =>
deps.dependencies
.filter(d => d.topicId === id)
.map(d => byId.get(d.prerequisiteId));
// "Addition and subtraction word problems"
console.log(prereqsOf('mt_yJmvUCCym7').map(t => t.name));
// [ 'Representing Addition and Subtraction' ]
Build a full learning path
The interesting thing about a prerequisite graph is that you can walk it. Since the graph is a DAG (no cycles), a depth-first walk gives you every topic a learner needs before the target, in an order they could actually study them:
const edgesByTopic = new Map();
for (const d of deps.dependencies) {
if (!edgesByTopic.has(d.topicId)) edgesByTopic.set(d.topicId, []);
edgesByTopic.get(d.topicId).push(d);
}
function learningPath(targetId, { hardOnly = true } = {}) {
const seen = new Set();
const order = [];
function visit(id) {
if (seen.has(id)) return;
seen.add(id);
for (const d of edgesByTopic.get(id) ?? []) {
if (hardOnly && d.strength !== 'hard') continue;
visit(d.prerequisiteId);
}
order.push(byId.get(id));
}
visit(targetId);
return order; // prerequisites first, target last
}
for (const t of learningPath('mt_yJmvUCCym7')) {
console.log(`${t.ageRangeStart}-${t.ageRangeEnd} ${t.subject} ${t.name}`);
}
Running it on "Addition and subtraction word problems" prints:
4-6 Mathematics One-to-one counting
4-6 Mathematics How Many in Total?
4-6 Mathematics Addition as combining or putting together two
4-6 Mathematics Subtraction as taking away or separating
4-6 Mathematics Representing Addition and Subtraction
4-6 Mathematics Addition and subtraction word problems
That is a sensible sequence: count, combine, take away, represent it, then solve word problems.
The hardOnly flag is handy. Hard edges are true blockers. Soft edges are "helpful to know", so include them when you want a gentler path and drop them when you want the shortest one.
Prefer Python?
Same idea with networkx:
import json
import networkx as nx
topics = {t["id"]: t for t in json.load(open("data/topics.json"))["topics"]}
deps = json.load(open("data/dependencies.json"))["dependencies"]
G = nx.DiGraph()
for d in deps:
G.add_edge(d["prerequisiteId"], d["topicId"], strength=d["strength"])
target = "mt_yJmvUCCym7"
needed = nx.ancestors(G, target) | {target}
path = list(nx.topological_sort(G.subgraph(needed)))
for tid in path:
print(topics[tid]["name"])
Once it is in networkx, you also get the usual graph tools for free: finding the most "foundational" topics, spotting bottlenecks, or checking which topics unlock the most downstream learning. Note that this version follows soft edges too, so it can return a longer path than the JavaScript one.
Add the teaching guidance
The newest file, teaching-research.json, covers what to do with a topic. Each record joins to a topic by topicId and includes a teaching summary, concrete activities with feedback ideas, common misconceptions, assessment guidance, and sources.
import research from './data/teaching-research.json' with { type: 'json' };
const guidanceByTopic = new Map(research.records.map(r => [r.topicId, r]));
const guidance = guidanceByTopic.get('mt_yJmvUCCym7');
console.log(guidance?.learn.summary);
for (const a of guidance?.learn.activities ?? []) console.log('-', a.title);
// - Story-modelling with counters and a mat
// ...
Only Science and Mathematics are covered so far. For English and the other subjects, guidance will be undefined, so keep that ?. in place.
What you could build with it
- Adaptive practice. When a kid fails a topic, walk back one hard edge and test the prerequisite instead of repeating the same question.
-
AI tutors. Give an LLM the topic, its
evidencelist, its prerequisites, and (for maths and science) its teaching activities as structured context. That grounds the model far better than "teach my kid fractions". -
Placement and diagnostics. Use
evidenceandassessmentPromptto build quick checks, then use the graph to infer what a child probably already knows. - Curriculum mapping. Because topics link to both Common Core and the UK National Curriculum, you can roughly map content between the two systems.
- Visualizations. The repo includes a 3D graph render, and Marble has an interactive version where you can click a topic and see everything that leads to it.
The caveats, honestly
The licensing needs a careful read. This is the most important part. The dataset has three layers with different terms:
- The database (structure, IDs, relationships) is under ODbL 1.0.
- Marble's written text (names, descriptions, evidence, prompts, reasons) is under CC BY-SA 4.0.
-
curriculum-standards.jsonis not Marble's to license. Each source keeps its upstream terms.
So, is it worth a try?
If you work anywhere near education tech, yes. It takes minutes to load, and a well-structured prerequisite graph with mastery criteria is the kind of thing teams usually spend months building internally. Even if you do not use it as-is, it is a good reference model for how to structure learning data.
It is especially useful as grounding data for AI tutoring tools, where the hard problem is often not generating explanations but knowing what to teach next.
Just read the license section before shipping anything commercial, and treat v1 as a strong foundation that you validate, not a finished curriculum.
A good first experiment: pick a topic your own kid (or a niece, nephew, or student) is struggling with, run learningPath() on it, and see whether the prerequisites match what you see in real life.
Links
- Repository: github.com/withmarbleapp/os-taxonomy
Top comments (0)