Finding the API object
The SCORM API is a plain JavaScript object that the LMS puts somewhere in the window hierarchy before it launches your content. There is no network call, no SDK, and no import. Your content's only job is to walk up the frame chain until it finds that object, and then call methods on it. Everything a SCORM course reports to an LMS goes through this one object.
Which name you look for depends on the version. SCORM 1.2 exposes an object called API whose methods are prefixed LMS. SCORM 2004 exposes API_1484_11 with unprefixed names. The methods do the same jobs; only the spelling changed.
| Job | SCORM 1.2 (API) | SCORM 2004 (API_1484_11) |
|---|---|---|
| Open the session | LMSInitialize("") | Initialize("") |
| Read a value | LMSGetValue(key) | GetValue(key) |
| Write a value | LMSSetValue(key, val) | SetValue(key, val) |
| Persist | LMSCommit("") | Commit("") |
| Close the session | LMSFinish("") | Terminate("") |
| Last error | LMSGetLastError() | GetLastError() |
The discovery algorithm is defined in the specification, and every player implements a version of it. Walk up through parent windows, give up after a sane number of hops, and if that fails try the opener — because some LMSs launch content in a new window rather than a frame.
function findAPI(win) {
var tries = 0;
while (win.API == null && win.parent != null && win.parent != win) {
if (++tries > 500) return null;
win = win.parent;
}
return win.API;
}
function getAPI() {
var api = findAPI(window);
if (api == null && window.opener != null) api = findAPI(window.opener);
return api; // null means: not running in an LMS
}
Initialize and Terminate
These two bracket the session. Initialize("") tells the LMS a learner has started; it returns the string "true" on success. Until it succeeds, every other call fails. Terminate("") — LMSFinish("") in 1.2 — tells the LMS the session is over, and implicitly commits anything not yet persisted.
Three rules trip people up. Both take an empty string as their argument, not no argument. Both return strings, never booleans — if (api.Initialize("")) is true for the string "false" too, which is a genuinely common bug. And both may be called exactly once per session: calling Initialize twice returns "false" with error 103, and any call after Terminate fails with a "after termination" error.
After Terminate, the session is closed. Content that tries to write a final score in an unload handler that fires after termination is writing into a closed session, and the value is discarded. That specific ordering mistake is one of the causes covered in why courses lose completions.
GetValue and SetValue
These read and write the data model — a flat namespace of dotted keys defined by the specification. GetValue takes a key and returns its value. SetValue takes a key and a value and returns "true" or "false".
Everything is a string. Scores, booleans, counts, timestamps: all strings. SetValue("cmi.score.scaled", 0.84) passes a number where a string is required and a strict LMS will reject it with a type mismatch. Write "0.84".
GetValue("cmi.learner_name") → "Dana Whitfield"
GetValue("cmi.entry") → "resume" or "ab-initio" or ""
GetValue("cmi.suspend_data") → "sec-05|q3:1,q4:0"
SetValue("cmi.location", "sec-06") → "true"
SetValue("cmi.score.scaled", "0.84") → "true"
SetValue("cmi.completion_status", "completed") → "true"
Keys divide into three kinds, and the division is worth memorising because it explains most 403 and 404 errors. Read-only elements — cmi.learner_id, cmi.learner_name, cmi.entry, cmi.credit, cmi.mode — come from the LMS and cannot be written. Write-only elements, notably cmi.exit, can be set but not read back. Everything else is read-write.
Values are also bounded. cmi.suspend_data is capped at 4,096 characters in SCORM 1.2 and 64,000 in 2004, and writing past the cap is a silent data-loss bug on some platforms and a hard error on others — see suspend data limits. Vocabulary fields only accept their defined tokens: cmi.completion_status takes completed, incomplete, not attempted, or unknown, and nothing else. The full element list with types and access rules is in the SCORM 1.2 data model reference.
Commit and persistence
Commit("") asks the LMS to persist everything set so far. This is the call that decides whether your data survives, and the specification is deliberately vague about it: the LMS may persist on every SetValue, or it may hold values in memory until Commit or Terminate. You cannot know which one you are talking to.
So the safe assumption is the pessimistic one: nothing is stored until you commit. A course that sets values throughout and commits only in its exit handler will lose the whole session on any platform that buffers, every time a learner closes the tab, loses their connection, or has their session time out.
The opposite mistake is committing after every SetValue. Each commit is typically an HTTP round trip, and a course that fires hundreds of them will be throttled by some platforms and will feel slow on all of them. The workable cadence is to commit at natural boundaries — end of a page, end of a question, after status changes — and always immediately after writing completion or score.
Error codes worth knowing
Every method returns only a success string. To find out why something failed you call GetLastError(), which returns a numeric code as a string, and optionally GetErrorString(code) and GetDiagnostic(code) for text. Check it after any call that returns "false" — content that ignores the error code cannot tell a rejected value from a broken session.
| Code | Meaning | What it usually is in practice |
|---|---|---|
| 0 | No error | The call worked |
| 101 | General exception | Catch-all; check GetDiagnostic — the LMS often puts the real reason there |
| 103 | Already initialized | Initialize called twice, often by two scripts both bootstrapping |
| 122 / 132 / 142 | Get / set / commit before initialization | Code ran before Initialize returned, usually a race in page load |
| 123 / 133 / 143 | Get / set / commit after termination | An unload handler writing after the session closed |
| 401 | Undefined data model element | A typo, or a 2004 key used against a 1.2 API |
| 403 | Element is read only | Writing to cmi.learner_name or another LMS-supplied field |
| 405 | Element is write only | Reading cmi.exit back |
| 406 | Type mismatch | A number or boolean passed where a string was required |
| 407 | Value out of range | A scaled score outside −1 to 1, or a term not in the field's vocabulary |
SCORM 1.2 uses a shorter list — 201 for a bad argument, 301 for "not initialized", 401 for not implemented, 403 read-only, 404 write-only, 405 type mismatch — so the same number can mean different things across versions. Always read the code against the version the package declares.
A full sample sequence
This is what a healthy SCORM 2004 session looks like end to end, as it would appear in a runtime log. A learner resumes at section five, works to the end, passes a quiz, and exits.
Initialize("") → "true"
GetValue("cmi.learner_name") → "Dana Whitfield"
GetValue("cmi.entry") → "resume"
GetValue("cmi.location") → "sec-05"
GetValue("cmi.suspend_data") → "q1:1,q2:1"
SetValue("cmi.location", "sec-06") → "true"
SetValue("cmi.suspend_data", "q1:1,q2:1,q3:1") → "true"
Commit("") → "true"
SetValue("cmi.score.raw", "21") → "true"
SetValue("cmi.score.max", "25") → "true"
SetValue("cmi.score.min", "0") → "true"
SetValue("cmi.score.scaled", "0.84") → "true"
SetValue("cmi.success_status", "passed") → "true"
SetValue("cmi.completion_status", "completed") → "true"
Commit("") → "true"
SetValue("cmi.exit", "normal") → "true"
Terminate("") → "true"
Three details make this log a healthy one rather than a hopeful one. Score is written before status, so an LMS that evaluates a mastery score at the moment status arrives has the number it needs. There is a Commit immediately after the status writes, so the outcome is durable before the fragile part of the session — the exit — begins. And cmi.exit is set before Terminate, not after, because after termination it would be rejected with 133.
Comparing a real log against this shape is the fastest diagnosis available for a course that "works but does not report". A missing Commit, a status written before a score, or a trailing write after Terminate each produce a distinct and recognisable failure — the method behind testing a SCORM package before upload. If you are new to how the package around this API is assembled, what SCORM is covers the manifest and the zip.
Where SCORM Central fits
SCORM Central converts files you already have — PDF, PowerPoint, Word, or video — into SCORM 1.2, SCORM 2004, cmi5, or xAPI, and shows you the manifest, the full runtime log, and a conformance report on every build, so you can see exactly what a package will report before an LMS does. See how it works, or start with one file.