Conventions
The response envelope, pagination, filtering, sorting and embedded relationships — the rules every endpoint follows.
Every endpoint follows the same conventions, so once you can read one response you can read them all.
Base URL
https://api.masteryconnect.com
Path prefix
/api/v2
Methods
GET only
Content type
application/json
Envelope
JSON:API-shaped
Timestamps
ISO 8601, UTC (2026-09-01T14:32:07.000Z)
Ids
Strings in data.id, integers in filter parameters
The JSON:API convention is that resource ids are strings, so data.id is "1234", not 1234. Numeric ids inside attributes (such as school_id) stay numbers. Don't compare the two without casting.
Response Envelope
A Single Resource
GET /api/v2/sections/4321
{
"data": {
"id": "4321",
"type": "section",
"attributes": {
"name": "Period 3 Biology",
"archived": false,
"course_number": "BIO-101",
"created_at": "2026-08-14T09:12:44.000Z",
"updated_at": "2026-08-28T16:03:11.000Z"
},
"relationships": {
"school": { "data": { "id": "5678", "type": "school" } }
}
}
}A Collection
Collections add meta and links. Note that data is an array, and each entry has the same shape as a single resource.
Members
data
Always
The resource, or an array of them.
data.id
Always
The id, as a string.
data.type
Always
The resource type, singular (section, material).
data.attributes
Always
The resource's own fields.
data.relationships
When the resource has related records
Linkage only — ids and types, not the related objects.
meta
Collections
Pagination counts.
links
Collections
Pagination URLs.
Pagination
Every collection is paginated.
page[number]
1
—
1-based page number.
page[size]
25
100
Records per page.
Out-of-range values are rejected rather than clamped: page[size]=500 returns 422, not a page of 100. A page[number] beyond the last page returns 200 with an empty data array.
Paging Correctly
Follow links.next until it is absent, rather than incrementing a counter and guessing when to stop:
links.next is omitted on the last page; first and last are always present.
Pagination Is Not a Snapshot
Paging is offset-based against live data. If records are created or deleted while you page, a record can appear twice across pages or be skipped. For a consistent extract, sort by a stable column (sort=created_at) and de-duplicate by id as you go.
Filtering
Filters use filter[key]=value. Each endpoint allows a specific set of keys.
Multiple filters combine with AND. Each key takes a single value — there is no IN, no range and no negation.
An unrecognised filter key is a 422, never silently ignored. This is deliberate: a typo like filter[shcool_id] fails loudly instead of handing you a full unfiltered result set that looks correct.
Allowed Filters
/districts
—
/districts/{id}/schools
nces_school_id
/sections
school_id, archived
/classrooms
school_id, teacher_id, archived
/teachers
school_id
/materials
subject_id, objective_id, draft, available, updated_since
/banks
bank_type, name
/curriculum_maps
privacy_level
/class_objectives
available, class_objective_type
/pathways
available, subject_id
/reports/{report_name}
Per report — see Reports
Nested collections (/banks/{id}/questions, /materials/{id}/items, /curriculum_maps/{id}/objectives, /classrooms/{id}/objectives, /trackers/{id}/assessments) are already scoped by their parent and accept no filters of their own.
/teachers intentionally has no name filter — a teacher's name is assembled from several columns, so there is no single column to match on.
Filtering by Modification Time
filter[updated_since] takes a full ISO 8601 datetime and returns records modified at or after it — the basis of an incremental sync:
Sorting
sort takes a comma-separated list of columns. Prefix a column with - for descending.
Like filters, sort columns are allow-listed; an unknown column is a 422.
/districts/{id}/schools
name, created_at
/sections
name, created_at
/classrooms
title, created_at
/classrooms/{id}/objectives
name, created_at
/teachers
last_name, created_at
/materials
title, created_at, updated_at
/materials/{id}/items
id
/banks
name, created_at
/banks/{id}/questions
created_at
/banks/{id}/passages
created_at
/curriculum_maps
title, created_at, updated_at
/curriculum_maps/{id}/materials
title, created_at, updated_at
/curriculum_maps/{id}/objectives
name, created_at, updated_at
/class_objectives
name
/pathways
name
Relationships and Embeds
Related data appears in two different places, and the difference matters.
relationships — Linkage Only
relationships gives you ids and types, never the related object:
To resolve one, call its own endpoint. A to-one relationship with no record is { "data": null }.
Embeds — The Related Object Inline
Some resources also render related objects inside attributes, so you get them without a second request. A material, for example, embeds its objective, subject and teacher:
Note that objective appears in both places — embedded under attributes and as linkage under relationships. That is expected; they are independent.
Two rules govern which embeds you see:
To-one embeds (a material's teacher) appear on both single-resource and collection responses.
To-many embeds (a material's items) appear only on single-resource responses. Embedding them in a collection would multiply the payload by the page size, so collections omit them.
So if you need every material's items, list the materials and then fetch each one — or use /materials/{id}/items, which pages properly. To-many embeds are also count-capped, so a resource with hundreds of children will not return all of them inline; page the nested route when you need the full set.
Next
Errors — status codes and error codes.
Rate Limits & Policies — budgets and headers.
API Reference — per-endpoint parameters and schemas.
Last updated
Was this helpful?