Teacher Sanctions API Documentation

Search official state-published teacher sanctions and educator disciplinary records via REST API.

Quick start

  1. 1. Create a free account and copy your API key.
  2. 2. Add the key to an Authorization or X-API-Key header.
  3. 3. POST a full name and optional state to /api/search.
Important: This service provides access to publicly available records published by state education agencies. It is not a consumer report under the Fair Credit Reporting Act (FCRA). It is intended for research, awareness, public safety, and legitimate business purposes only. Users are solely responsible for their own compliance with applicable laws (including FCRA and state screening laws) when using this data. Always verify directly with the issuing state. See our full Terms of Service and Privacy Policy.

Authentication

All requests must be authenticated using your API key (issued from the Dashboard after signup/login).

Authorization: Bearer ts_live_...
or
X-API-Key: ts_live_...

Keys are tied to your account. Keep them secret.

POST /api/search

The primary endpoint. Performs fuzzy full-name search (with nickname expansion) + optional state filter against official state lists. The state filter accepts a two-letter code or full state name. Omit it for a nationwide search. Returns up to 20 results ordered by relevance.

Example request
curl -X POST https://www.teachersanctions.io/api/search \
  -H "Authorization: Bearer ts_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "Jordan Lee Smith",
    "state": "CA"
  }'
Example response
{
  "results": [{
    "recordId": "93ac24db-e7fe-4bb0-b5fc-794e228e65cf",
    "name": "Jordan Lee Smith",
    "firstName": "Jordan",
    "middleName": "Lee",
    "lastName": "Smith",
    "suffix": null,
    "state": "CA",
    "sanctionType": "SUSPENSION",
    "effectiveDate": "2025-03-12",
    "districtOrEmployer": "Los Angeles Unified School District",
    "licenseNumber": "1234567",
    "reason": "Violated professional standards regarding student boundaries.",
    "sourceUrl": "https://www.cde.ca.gov/...",
    "firstIngestedAt": "2026-06-13T14:24:18.000Z",
    "freshness": "2026-06-13"
  }],
  "meta": {
    "limit": 20,
    "returned": 1,
    "truncated": false
  }
}

Rate limit information is provided via response headers (X-RateLimit-*).

  • recordId is an opaque sanction-record identifier, not an educator or person identifier.
  • Parsed name fields are best-effort. Use name as the preserved display-name fallback.
  • effectiveDate is the sanction/action date when the source publishes one.
  • sourceUrl may be an empty string when no browsable official link is available, and it is not a unique record identifier.
  • firstIngestedAt is when the stored record was first ingested; it is not a sanction, publication, or last-verification date.
  • freshness is a deprecated date-only alias retained for compatibility.
  • meta.truncated is true when more matches exist than the 20 returned records. Pagination is not currently supported.

GET /api/status

Public (no authentication required) endpoint for checking service health, data coverage, and freshness. Ideal for monitoring, health checks, or dashboards.

Example response
{
  "statesCovered": 50,
  "lastFullRefresh": "Jun 12, 2026",
  "overallStatus": "All Systems Operational",
  "lastUpdated": "just now"
}

- statesCovered: Number of states with at least one record.
- lastFullRefresh: Date of the most recent successful data refresh.
- overallStatus: "All Systems Operational" or "Degraded".
- lastUpdated: Human-readable time since the latest data activity.

Error Responses

The API returns standard HTTP status codes. Search endpoint errors include an error field, and some also include a message, usage, or limit details. A failed status check returns the normal status shape with overallStatus set to Degraded.

StatusError CodeDescriptionExtra Fields
400fullName is requiredMissing or invalid input, such as a missing fullName or non-string state.
401Authentication requiredMissing or invalid API key / session. Use Authorization: Bearer ts_live_... or X-API-Key.
403Invalid request originSession-authenticated browser request did not come from an allowed origin. API-key requests are not subject to this browser-origin check.
402free_limit_reachedYou have used all 5 free searches this month.searches, limit
402enterprise_limit_reachedEnterprise credits exhausted for this month (your purchased monthly allocation is exhausted).searches, limit
429rate_limit_exceededShort-term rate limit exceeded for your plan (see rate limits table above). Check headers for exact limits.limit, reset (and usage in some cases)
503Service temporarily unavailableUsage or billing infrastructure is temporarily unavailable. Retry with bounded backoff.
500Search failedUnexpected internal error. Please try again or contact support.

Example 429 response body:

{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Please retry later.",
  "limit": 120,
  "reset": 1718270400000
}

When the rate-limit check is reached, details are available in the X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. A 429 response also includes Retry-After. The response-body reset is epoch milliseconds; the header uses epoch seconds.

Example 402 (enterprise limit) response body:

{
  "error": "enterprise_limit_reached",
  "message": "Enterprise credits exhausted for this month (10 limit). Contact us to increase your allocation.",
  "searches": 10,
  "limit": 10
}

Rate Limits & Monthly Quotas

We have two independent controls:

  • Short-term rate limits — to protect the service from abuse (per API key).
  • Monthly usage quotas — the credit/billing system (see Pricing page).
PlanRequests per 60 secondsNotes
Free15Very limited for testing.
On Demand120Usage-based plan. Space requests evenly and honor response headers.
Enterprise800High-volume screening platforms. Contact us for custom higher limits.

Successful search responses and 429 responses include rate-limit headers (X-RateLimit-*). A 429 response also includes Retry-After.

Monthly quotas (free tier or Enterprise purchased credits) are enforced separately and return HTTP 402 with free_limit_reached or enterprise_limit_reached.

Other Notes

  • Results are limited to 20 per request and ranked by relevance (trigram similarity + last/first name).
  • Nickname expansion is supported (e.g. "Bill Smith" matches William, Billy, etc.).
  • Full machine-readable spec: /api/openapi
  • Public service status (for monitoring/health checks): /api/status (also available as human-readable page at /status)
For higher volume needs or custom terms, see the Enterprise section or contact us.