How to Track Learner Progress, Completion Status, and Course Activity with Unified's LMS API
February 19, 2026
Tracking learner progress across LMS platforms is harder than it should be.
Every LMS models progress differently. Some expose percent completion. Others only track a completion flag. Some provide timestamps and time spent. Others provide minimal signals. Very few emit webhook notifications when progress changes.
If you build progress tracking assuming every LMS behaves the same way, your dashboards will drift, your compliance reporting will break, and your 'real-time' claims will collapse under provider variability.
Unified's LMS API does not invent a universal progress model. It exposes what the underlying LMS provides through a normalized Activity object, with consistent endpoints and filtering semantics. Progress tracking becomes a matter of structured polling, reconciliation, and provider-aware fallbacks—not brittle event streams.
This guide shows how to track learner progress, completion status, and course activity using Unified's LMS API, while staying aligned with actual provider capabilities.
What 'Track Progress' Means Here
This use case focuses on:
- Fetching learner activity records
- Monitoring completion status
- Tracking percent progress
- Measuring engagement duration
- Updating dashboards and compliance reports
It does not assume:
- Real-time webhooks across all LMS providers
- Uniform availability of progress fields
- Guaranteed incremental filtering support
Progress is tracked through observable activity records—not inferred enrollment or vendor-specific signals.
The Canonical Progress Object: Activity
Unified represents learner progress through the LmsActivity object.
Key fields
idcourse_idstudent_idcontent_idis_completedprogress_percentageduration_minutesstarted_atcompleted_atcreated_atupdated_at
Activity records capture interactions with content or courses. They are the authoritative source for tracking progress and completion.
Important:
- Enrollment is not inferred from Activity.
- Absence of an Activity does not imply absence of enrollment.
- Activity reflects engagement, not membership.
Step 1: Fetch Activity Records
Use the list endpoint to retrieve progress data.
import { UnifiedTo } from '@unified-api/typescript-sdk';
const sdk = new UnifiedTo({
security: { jwt: process.env.UNIFIED_API_KEY! },
});
async function fetchActivities(connectionId: string) {
const activities = [];
let offset = 0;
const limit = 100;
while (true) {
const page = await sdk.lms.listLmsActivities({
connectionId,
limit,
offset,
sort: 'updated_at',
order: 'asc',
fields: [
'id',
'course_id',
'student_id',
'content_id',
'is_completed',
'progress_percentage',
'duration_minutes',
'started_at',
'completed_at',
].join(','),
});
if (!page || page.length === 0) break;
activities.push(...page);
offset += limit;
}
return activities;
}
Supported filters:
student_idcourse_idcontent_idquerylimitoffsetfieldsraw
Step 2: Segment by Student or Course
You can filter progress by student or course directly in the API.
Per-student progress
await sdk.lms.listLmsActivities({
connectionId,
student_id: studentId,
});
Per-course progress
await sdk.lms.listLmsActivities({
connectionId,
course_id: courseId,
});
This enables:
- Learner dashboards
- Instructor dashboards
- Compliance tracking
- Cohort analytics
Step 3: Understand Provider Variability
Not all LMS providers expose all progress fields.
From field-level support:
is_completedis widely supported.progress_percentageis supported by some providers (e.g., Coursera, Go1, LearnUpon), but not universally.duration_minutesis supported by some providers (e.g., Coursera, D2L, SAP), but not all.content_idmay be absent for some providers.updated_aton Activity is not universally populated.
Your implementation must treat missing fields as legitimate absence—not errors.
Example fallback logic:
function normalizeProgress(activity) {
return {
completed: activity.is_completed ?? false,
progress: activity.progress_percentage ?? null,
duration: activity.duration_minutes ?? null,
};
}
Step 4: Incremental Sync Strategy
Unlike many HR categories, LMS integrations do not universally support incremental filtering for Activity.
updated_gte support:
- Supported: SAP SuccessFactors (LMS)
- Not supported: most other LMS providers
Webhook support:
- Only SAP exposes LMS webhook events.
- No other provider supports LMS webhooks.
Implication
For most providers:
- You must poll the Activity endpoint.
- Deduplicate by
id. - Compare stored values for changes.
- Recompute progress aggregates.
For SAP:
- You may use
updated_gtefor incremental polling. - You may use webhooks if available.
Step 5: Recommended Polling Loop
A safe polling loop:
- Fetch activities (filtered by student_id or course_id if needed).
- Store activity IDs and progress values.
- On next poll:
- Re-fetch.
- Compare
is_completed,progress_percentage,completed_at. - Update downstream dashboards accordingly.
Example high-level pattern:
function reconcileActivities(previousMap, currentList) {
const updates = [];
for (const activity of currentList) {
const prev = previousMap.get(activity.id);
if (!prev || prev.progress_percentage !== activity.progress_percentage ||
prev.is_completed !== activity.is_completed) {
updates.push(activity);
}
}
return updates;
}
Step 6: Enrich with Course and Content Context
Progress alone is not meaningful without context.
Join Activity with:
Coursefor catalog metadataContentfor lesson/module infoCollectionfor module hierarchy
Example:
await sdk.lms.getLmsCourse({ connectionId, id: activity.course_id });
await sdk.lms.getLmsContent({ connectionId, id: activity.content_id });
This enables:
- Module-level completion views
- Time-to-completion metrics
- Content-level engagement insights
What 'Real-Time' Means Here
In LMS integrations, 'real-time' means:
- On-demand, pass-through reads
- No caching
- Polling-based updates where required
- Webhooks only where supported (SAP)
It does not mean:
- Universal event-driven notifications
- Instant push updates across all LMS providers
Unified preserves provider realities instead of simulating them.
What Unified Does Not Assume
Unified does not:
- Infer progress from enrollment
- Guarantee incremental filtering across all LMS providers
- Emit synthetic webhook streams for LMS
- Normalize missing progress fields
It exposes the data the LMS provides, consistently and transparently.
Closing Thoughts
Tracking learner progress across LMS platforms requires discipline, not assumptions.
Unified's LMS API gives you:
- A normalized Activity model
- Consistent list endpoints
- Flexible filtering
- Clear provider variability boundaries
With the right polling strategy and provider-aware fallbacks, you can build accurate, portable progress dashboards across heterogeneous LMS ecosystems—without overclaiming real-time behavior or inventing missing data.