The SCORM API explained, call by call

How the SCORM API works: finding the API object, the five runtime methods, the string-only data model, the error codes, and a full call sequence.

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.

JobSCORM 1.2 (API)SCORM 2004 (API_1484_11)
Open the sessionLMSInitialize("")Initialize("")
Read a valueLMSGetValue(key)GetValue(key)
Write a valueLMSSetValue(key, val)SetValue(key, val)
PersistLMSCommit("")Commit("")
Close the sessionLMSFinish("")Terminate("")
Last errorLMSGetLastError()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
}
A null API is the single most common launch failure. It usually means the course was opened directly from disk or from a plain web server rather than through the LMS, or that the LMS launched it cross-origin so the frame chain is unreadable. Content that silently no-ops when the API is null looks perfectly fine to the learner and reports nothing at all.

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.

CodeMeaningWhat it usually is in practice
0No errorThe call worked
101General exceptionCatch-all; check GetDiagnostic — the LMS often puts the real reason there
103Already initializedInitialize called twice, often by two scripts both bootstrapping
122 / 132 / 142Get / set / commit before initializationCode ran before Initialize returned, usually a race in page load
123 / 133 / 143Get / set / commit after terminationAn unload handler writing after the session closed
401Undefined data model elementA typo, or a 2004 key used against a 1.2 API
403Element is read onlyWriting to cmi.learner_name or another LMS-supplied field
405Element is write onlyReading cmi.exit back
406Type mismatchA number or boolean passed where a string was required
407Value out of rangeA 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.

Keep reading

How-to4 min Convert SCORM to video or PowerPoint? Can you convert SCORM to MP4, HTML5 or PowerPoint? What is inside a package, the routes that work, and what you lose with each one. Explainer4 min Does your LMS support xAPI? How to check What xAPI LMS support really means: launching xAPI content, a built-in LRS, or forwarding statements. The questions to ask and a quick test to run. Explainer4 min LMS vs LRS: what is the difference? LMS vs LRS: an LMS runs courses and learners; an LRS stores xAPI statements. Why SCORM does not use an LRS, and when you need both.

Start with one file.

Upload something you already have and see it as a package, with the conformance report and the runtime log alongside it.

→ upload  handbook.pdf
  parsing … 14 sections
  building manifest …
  validating … 0 errors, 2 notes
✓ handbook-scorm2004.zip
✓ preview link ready