How to Access Courses, Modules, and Content from LMS Platforms with Unified's LMS API
February 19, 2026
Accessing course structure across LMS platforms is harder than it looks.
Some LMS providers model courses as flat catalogs. Others introduce sections or sessions. Some expose module hierarchies. Others only return content items attached to courses. Structure depth, field availability, and filtering support vary widely across providers.
When teams build LMS integrations assuming consistent structure, course browsing breaks. Modules disappear. Sections are ignored. Content trees flatten unexpectedly.
Unified's LMS API provides a normalized interface to fetch courses, classes, modules, and content — while preserving provider-specific realities. You get a consistent object model and parameter surface, without inventing structure that doesn't exist upstream.
This guide shows how to fetch course catalogs, reconstruct module hierarchies, retrieve learning items, and keep structure in sync across LMS platforms — safely and provider-aware.
The Unified LMS Structure Model
Unified exposes the following core objects:
- Course → catalog-level course entry
- Class → section/session of a course (optional per provider)
- Collection → module or content grouping (optional per provider)
- Content → learning item (lesson, video, document, etc.)
- Activity → learner progress (optional enrichment)
Structure is reconstructed by joining these objects. Unified does not assume all providers support every layer.
Step 1: Fetch the Course Catalog
Start by listing courses.
import { UnifiedTo } from '@unified-api/typescript-sdk';
const sdk = new UnifiedTo({
security: { jwt: process.env.UNIFIED_API_KEY! },
});
async function fetchCourses(connectionId: string) {
return await sdk.lms.listLmsCourses({
connectionId,
limit: 100,
sort: 'updated_at',
order: 'asc',
fields: 'id,name,is_active,is_private,updated_at',
});
}
Supported parameters include:
limit/offset(pagination)updated_gte(where supported)queryclass_idcompany_idfieldsraw(provider passthrough)
Important
Not all LMS providers support incremental filtering (updated_gte). Where supported, you can use it for incremental sync. Where it is not supported, perform periodic full scans and reconcile changes by id and updated_at.
Step 2: Retrieve Course Details
For a detailed course view:
async function fetchCourse(connectionId: string, id: string) {
return await sdk.lms.getLmsCourse({
connectionId,
id,
fields: 'id,name,description,languages,categories,media,instructor_ids',
});
}
Using fields prevents over-fetching large media payloads in list views.
Step 3: Fetch Sections (Classes) Where Supported
Some LMS providers model courses through sections or sessions.
async function fetchCourseClasses(connectionId: string, courseId: string) {
return await sdk.lms.listLmsClasses({
connectionId,
course_id: courseId,
fields: 'id,name,course_id,languages',
});
}
Notes:
class.course_idis required and is the canonical join to Course.- Not all providers expose Classes.
- If Classes are absent, treat Course as the primary container.
Step 4: Fetch Module Hierarchy (Collections)
Collections represent module groupings when supported.
async function fetchCourseCollections(connectionId: string, courseId: string) {
return await sdk.lms.listLmsCollections({
connectionId,
course_id: courseId,
fields: 'id,name,parent_id,is_active',
});
}
Reconstruct the hierarchy using parent_id:
function buildCollectionTree(collections) {
const map = new Map();
const roots = [];
collections.forEach(c => map.set(c.id, { ...c, children: [] }));
collections.forEach(c => {
if (c.parent_id && map.has(c.parent_id)) {
map.get(c.parent_id).children.push(map.get(c.id));
} else {
roots.push(map.get(c.id));
}
});
return roots;
}
If Collections Are Not Supported
Some LMS integrations do not expose module hierarchies.
In that case:
- Skip the Collection layer
- Group Content directly under the Course
Unified preserves provider capabilities rather than simulating missing structure.
Step 5: Fetch Course Content
Content represents the actual learning items.
async function fetchCourseContent(connectionId: string, courseId: string) {
return await sdk.lms.listLmsContents({
connectionId,
course_id: courseId,
sort: 'sort_order',
order: 'asc',
fields: 'id,name,duration_minutes,collection_ids,sort_order,is_active',
});
}
Supported filters:
course_idcollection_idupdated_gte(where supported)queryfieldsraw
Where collection_ids are populated, attach content to modules.
If collection_ids are absent:
- Treat content as flat under the course.
Not all providers support sort_order, collection_ids, or deep module metadata. Always build structure dynamically.
Step 6: Retrieve Individual Content Items
For a detailed lesson view:
async function fetchContent(connectionId: string, id: string) {
return await sdk.lms.getLmsContent({
connectionId,
id,
fields: 'id,name,description,media,languages,duration_minutes,localizations',
});
}
This enables:
- Full lesson rendering
- Multilingual display
- Media asset loading
Step 7: Keep Structure in Sync
LMS integrations do not provide webhook notifications for structural changes (courses, classes, collections, content).
Structure sync must be polling-based.
Where updated_gte is supported:
await sdk.lms.listLmsCourses({
connectionId,
updated_gte: lastSync,
});
Where it is not supported:
- Perform periodic full scans
- Reconcile by
id - Compare
updated_atvalues - Deduplicate changes
Repeat the same approach for:
- Classes
- Collections
- Content
Provider Variability
Not all LMS providers expose:
- Classes
- Collections
- Deep module hierarchies
sort_orderlocalizationsupdated_gtesupport for incremental sync
Best practice:
- Check object availability dynamically
- Do not assume hierarchy depth
- Fallback to flat course + content rendering
- Treat parameter support as provider-dependent
Unified provides a consistent parameter model, while transparently reflecting provider-level support.
Optional: Enrich with Progress Data
To attach learner progress to course views:
await sdk.lms.listLmsActivities({
connectionId,
course_id: courseId,
student_id: studentId,
});
Activity enriches structure but is not part of the structure itself.
Closing Thoughts
Accessing LMS course structure is not about assuming parity across platforms. It's about building provider-aware integrations on top of a normalized API surface.
Unified's LMS API gives you:
- A consistent Course, Class, Collection, and Content model
- A stable parameter surface (
limit,offset,updated_gte,fields,raw) - Polling-based structure synchronization
- Transparent provider-level support
With careful joins and explicit fallbacks, you can build a portable course browser across heterogeneous LMS ecosystems — without hardcoding vendor-specific logic or inventing missing structure.