SchemaCheck/ DocsDashboard

API Reference

Error Codes

Every error the SchemaCheck API can return, its HTTP status, and how to fix it. All errors follow the same shape so you can handle them with a single catch block.

Error response shape

All error responses include an error object. The code field is machine-readable and stable — safe to switch on in your code.

json
{
  "error": {
    "code": "invalid_api_key",   // stable machine-readable string
    "message": "Human-readable explanation."
  }
}

Quick reference

CodeHTTPMeaning
missing_api_key401Missing API Key
invalid_api_key401Invalid API Key
inactive_api_key401Inactive API Key
quota_exceeded429Monthly Quota Exceeded
rate_limit_exceeded429Rate Limit Exceeded
missing_input400Missing Input
invalid_url400Invalid URL
invalid_jsonld400Invalid JSON-LD
parse_error422JSON-LD Parse Error
fetch_failed422URL Fetch Failed
fetch_timeout422Fetch Timeout
internal_error500Internal Server Error

Error details

missing_api_keyHTTP 401

When

Request has no x-api-key header and no access_key query parameter.

Fix

Add your API key as a header (x-api-key: sc_live_…) or query param (?access_key=sc_live_…).

json
{
  "error": {
    "code": "missing_api_key",
    "message": "API key is required. Pass it as x-api-key header or access_key query param."
  }
}
invalid_api_keyHTTP 401

When

The key was provided but doesn't exist in the database or is malformed.

Fix

Double-check the key value. Keys follow the pattern sc_live_[32 hex chars]. Get a new key at /docs/getting-started.

json
{
  "error": {
    "code": "invalid_api_key",
    "message": "Invalid or inactive API key."
  }
}
inactive_api_keyHTTP 401

When

The key exists but the account has been deactivated.

Fix

Contact support if you believe this is an error. You can sign up for a new free key at /docs/getting-started.

json
{
  "error": {
    "code": "inactive_api_key",
    "message": "Invalid or inactive API key."
  }
}
quota_exceededHTTP 429

When

Your account has used all included validations for the current billing period. Free plan accounts are blocked at the limit.

Fix

Upgrade to a paid plan or wait until your quota resets at the next billing cycle. Cached responses don't count against your quota.

json
{
  "error": {
    "code": "quota_exceeded",
    "message": "You've used all 100 free validations this month. Upgrade to continue."
  }
}
rate_limit_exceededHTTP 429

When

Too many requests in a short window. Limits are per-plan: free 10/min, basic 30/min, growth 60/min, scale 120/min.

Fix

Slow your request rate or implement exponential backoff. The Retry-After header tells you how many seconds to wait.

json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Your free plan allows 10 requests per minute. Retry after 2026-03-18T10:31:00.000Z."
  }
}
missing_inputHTTP 400

When

POST request body contains neither a url field nor a jsonld field, or both are empty.

Fix

Include either url (string, valid URL) or jsonld (object) in the request body — not both, not neither.

json
{
  "error": {
    "code": "missing_input",
    "message": "Provide either 'url' or 'jsonld' in the request body."
  }
}
invalid_urlHTTP 400

When

The url parameter is present but not a valid URL (e.g. missing scheme, localhost, IP address).

Fix

Pass a fully qualified URL including scheme: https://example.com. Private or localhost URLs are not supported.

json
{
  "error": {
    "code": "invalid_url",
    "message": "URL must use http:// or https://."
  }
}
invalid_jsonldHTTP 400

When

The jsonld body field was provided but is not a valid JSON-LD object or array.

Fix

Ensure the jsonld field is a plain JSON object (or array of objects) with an @type field. Strings and numbers are not accepted.

json
{
  "error": {
    "code": "invalid_jsonld",
    "message": "The 'jsonld' field must be a JSON object or array."
  }
}
parse_errorHTTP 422

When

A JSON-LD block was found but could not be parsed as valid JSON, or the @type field is missing or unrecognised.

Fix

Run the raw JSON through a JSON linter to find the syntax error. Ensure every schema block has a valid @type.

json
{
  "error": {
    "code": "parse_error",
    "message": "Failed to parse JSON-LD block: Unexpected token } at position 142."
  }
}
fetch_failedHTTP 422

When

SchemaCheck attempted to fetch the URL but received a non-2xx HTTP response (e.g. 404, 403, 500) or a network error.

Fix

Verify the URL is publicly accessible. If the page requires authentication or blocks bots, use the jsonld field with the raw markup instead.

json
{
  "error": {
    "code": "fetch_failed",
    "message": "Failed to fetch https://example.com: server responded with HTTP 403."
  }
}
fetch_timeoutHTTP 422

When

The target URL did not respond within 25 seconds.

Fix

Check that the URL is reachable and not unusually slow. If the page is slow to load, extract the JSON-LD yourself and send it via the jsonld field instead.

json
{
  "error": {
    "code": "fetch_timeout",
    "message": "Validation timed out after 25 seconds."
  }
}
internal_errorHTTP 500

When

An unexpected error occurred on SchemaCheck's servers. This is not caused by your request.

Fix

Retry after a short delay. If the error persists, contact support.

json
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred during validation."
  }
}

Retry logic

Use exponential backoff for 5xx errors and respect the Retry-After header on rate_limit_exceeded. Never retry 4xx errors (except rate limits) — they indicate a problem with the request itself.

typescript
async function validateWithRetry(url: string, apiKey: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const res = await fetch(
      `https://schemacheck.dev/api/v1/validate?url=${encodeURIComponent(url)}&access_key=${apiKey}`
    );
    const data = await res.json();

    if (res.ok) return data;

    const { code } = data.error ?? {};

    // Don't retry client errors
    if (res.status >= 400 && res.status < 500 && code !== "rate_limit_exceeded") {
      throw new Error(`[SchemaCheck] ${code}: ${data.error.message}`);
    }

    // For rate limits, respect Retry-After header
    if (code === "rate_limit_exceeded") {
      const retryAfter = Number(res.headers.get("Retry-After") ?? 60);
      await new Promise((r) => setTimeout(r, retryAfter * 1000));
      continue;
    }

    // Exponential backoff for 5xx
    if (attempt < maxRetries) {
      await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
    }
  }

  throw new Error("Max retries exceeded");
}

Tips

Switch on code, not message

The code field is stable across API versions. The message field is for humans and may change.

Credits are never charged on errors

4xx and 5xx errors never consume a validation credit. Only successful non-cached validations count.

Use jsonld to bypass fetch issues

If a URL is behind a firewall, returns 403 to bots, or is slow — extract the JSON-LD yourself and POST it in the jsonld field.

Cached responses never error

If a URL was validated in the last hour and is cached, the cached response is returned immediately without fetching the page.