Data Quality and Suppression
How test-score values that are not a single exact number are represented in API 3.0
In short
- States withhold or round some test results to protect student privacy (small student counts). SchoolDigger now stores exactly what the state published.
- Available for 2026 and later test-score data (the 2025-26 school year onward), state by state as each state's results are loaded. Earlier years keep their existing exact values, plus the
legacyImputedstand-ins described below. - Starting with API 3.0, add
includeRanges=trueto/v3.0/schools/{id}or/v3.0/districts/{id}and each percent metric in a test score (percentMetStandard,percentTier1…percentTier5) is either a plain number or an object with astatus, and is absent when nothing was reported. - Without
includeRanges(the default) a 3.0 record is identical to 2.4: exact values only, and rows the state reported as a range or suppressed are left out. - API versions 2.4 and below are unchanged: they return only exact values and omit rows the state reported as a range or suppressed.
- The API carries no display strings. The rendering rule below is the consumer's job.
The 3.0 shape
A school, district or state test score object in /v3.0/schools/{id}?includeRanges=true and /v3.0/districts/{id}?includeRanges=true:
"schoolTestScore": {
"studentsTested": 48,
"meanScaledScore": 645,
"percentMetStandard": 82.3,
"percentTier1": { "status": "range", "low": 0, "high": 10 },
"percentTier2": 27,
"percentTier3": { "status": "derived", "value": 8 },
"percentTier4": { "status": "suppressed" }
}
A score object that contains only status objects is a real record: students were tested and the results were withheld. studentsTested, studentsEligible, meanScaledScore and the numTier counts are unchanged. Tier metrics remain Enterprise-only; percentMetStandard objects are returned on every plan that includes test scores.
Status vocabulary
| Representation | Meaning | Fields |
|---|---|---|
| a plain number | The state reported an exact value. Identical to API 2.4. | — |
{ "status": "range" } | The state published only a range, for example "less than 10%" or "at least 95%". | low, high: the state's bounds in percent. low of 0 means "less than high"; high of 100 means "at least low". |
{ "status": "suppressed" } | The state withheld the value to protect student privacy. | none |
{ "status": "legacyImputed" } | A pre-2018 SchoolDigger stand-in for a value the state published as a range at the time. Older API versions return the stand-in (4 for "less than 5%", 96 for "at least 95%") as a plain number. | value: the stand-in; low, high: the state's bounds. |
{ "status": "derived" } | Computed by SchoolDigger from the other levels the state did report (for example the remaining tier when the others are exact). | value when the result is exact, otherwise low, high. |
| absent | Nothing was reported for this metric. | — |
Rendering rule
SchoolDigger.com renders these values as follows; we recommend consumers do the same.
| Value | Rendered |
|---|---|
82.3 | 82.3% |
{ "status": "range", "low": 0, "high": 10 } | < 10% |
{ "status": "range", "low": 95, "high": 100 } | ≥ 95% |
{ "status": "range", "low": 10, "high": 15 } | 10–15% |
{ "status": "suppressed" } | — (with a footnote: some values were suppressed by the state to protect student privacy) |
{ "status": "legacyImputed", "value": 4, "low": 0, "high": 5 } | < 5% (render from the bounds, not the stand-in) |
{ "status": "derived", "value": 8 } | 8% (a derived exact value is shown as a normal value) |
Rankings
SchoolDigger rankings use exact values where the state reported them and the midpoint of a range where it did not; a school whose results were entirely suppressed for a test is not ranked on that test. Ranking output in the API (rankHistory, /rankings) is unchanged.
Migrating from 2.4
- Opt in per call with
includeRanges=true; a 3.0 call without it returns the 2.4 record, so existing code keeps working when you switch the version. - With
includeRanges=true, check the JSON type of each percent metric before using it as a number:typeof v === 'number'(JavaScript) orisinstance(v, (int, float))(Python). - With
includeRanges=true, expect test-score entries and score objects that 2.4 omitted: rows the state reported only as ranges or suppressed values. - Tier labels (
tier1…tier5) are now present whenever the tier percent is present as a number or an object. - All other 3.0 endpoints (
/schools,/districts,/rankings,/autocomplete) return the same data as 2.4. - The official Python and JavaScript/TypeScript SDKs model the union as
APIReportedPercent.
See the API 3.0 reference and the Change Log. Questions: api@schooldigger.com.