New rate limits now apply to all accounts.

Error Handling

When a request fails, the response body is JSON — but the shape depends on which layer rejected the call. Your client should branch on HTTP status first, then parse the body using the rules below. This page is the single source of truth for platform-wide error handling across all API versions (/v2, /v3, /v3.1) and all endpoints.

Auth failures differ between the two channels —

LayerTop-level fieldTypical statuses
Auth / quota layermessage or code (string)401, 403, 429
SkyLink APIdetail (string or array)404, 422, 500, 502, 504

Always read Content-Type: application/json. Do not assume one error format for every status code.

Endpoint reference pages document only failures whose meaning or detail text is specific to that resource (for example METAR station missing vs TAF not issued). They link here for auth, validation, rate limits, and server errors.


Auth and quota errors

These occur before or instead of reaching the data layer. The body uses message or code, not detail.

401 — missing or invalid API key

{
  "code": "missing_api_key",
  "message": "Provide your key in the x-api-key header."
}

The key is read from the x-api-key header only — passing it as a query parameter is not accepted and returns the same 401.

403 — endpoint not included in your plan

{
  "code": "plan_forbidden",
  "message": "This endpoint is not included in your plan."
}

429 — abuse ceiling reached

{
  "message": "Too many requests"
}

See Rate Limits — on the direct API this is the per-day anti-abuse ceiling, not your monthly allowance.

JSON Schema — gateway errors (401, 403, 429)Expand section
{
  "type": "object",
  "required": ["message"],
  "properties": {
    "message": { "type": "string" }
  },
  "additionalProperties": true
}

Fix the key or subscription — do not retry 401 or 403. Back off and retry 429; see Retry rules and Rate Limits for quota headers.

See Getting Started for header setup.


Validation errors (422)

When the request reaches SkyLink but fails input validation, detail is an array of objects:

{
  "detail": [
    {
      "type": "string_too_short",
      "loc": ["path", "icao"],
      "msg": "String should have at least 4 characters",
      "input": "KF",
      "ctx": { "min_length": 4 }
    }
  ]
}
FieldDescription
detail[].typeValidator id, e.g. string_too_short
detail[].locPath to the bad field, e.g. ["path", "icao"] or ["query", "parsed"]
detail[].msgHuman-readable explanation
detail[].inputValue that was rejected
detail[].ctxOptional constraints
JSON Schema — 422 (detail array)Expand section
{
  "type": "object",
  "required": ["detail"],
  "properties": {
    "detail": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["type", "loc", "msg", "input"],
        "properties": {
          "type": { "type": "string" },
          "loc": { "type": "array", "items": { "type": "string" } },
          "msg": { "type": "string" },
          "input": { "type": "string" },
          "ctx": { "type": "object" }
        },
        "additionalProperties": true
      }
    }
  },
  "additionalProperties": false
}

Do not retry — fix the request parameters and send again.


Server and upstream errors (5xx)

When SkyLink or an upstream data service fails:

{
  "detail": "Internal Server Error"
}
JSON Schema — 5xx (detail string)Expand section

Same shape as business detail strings (404 uses this form too):

{
  "type": "object",
  "required": ["detail"],
  "properties": {
    "detail": { "type": "string" }
  },
  "additionalProperties": false
}
StatusMeaning
500Internal server error
502Upstream data service unavailable
504Upstream timed out

Retry with exponential backoff (max 3 attempts). See Retry rules.


Not found (404)

404 responses use SkyLink's string detail form, but the message and meaning are endpoint-specific — a METAR with no station, a TAF with no forecast, an unknown airport ICAO, and a missing historical flight each carry different semantics. Document 404 on each reference page (or section integration guide when the pattern spans several endpoints). Do not duplicate every variant here; link from your handler to the page that matches the endpoint you called.

SectionWhere 404 is documented
Weather — METAR / TAFMETAR · TAF · When weather data is missing
Weather — en-routeWinds Aloft · PIREPs · AIRMET/SIGMET
Airport dataAirports · Airport search
Reference dataCountries · Regions
Live ADS-BADS-B tracking
Historical ADS-BHistorical ADS-B hub · Flight detail · Position history
ChartsAerodrome charts
Flight operationsFlight status
WebhooksSubscriptions

Important: Some endpoints return an empty result in HTTP 200 instead of 404 (for example aircraft lookup when no tail number matches). Check the reference page before mapping all "not found" cases to 404.

Plan-gate 401/403 on Historical ADS-B (subscription too low for the path prefix) are documented on the Historical ADS-B hub, not here.


Retry rules

StatusRetry?Action
401, 403NoFix API key / subscription
404NoEndpoint-specific fallback — see reference page
422NoFix input
429YesExponential backoff; check quota headers
500, 502, 504YesExponential backoff (max 3 attempts)
import time
import requests

RETRYABLE = {429, 500, 502, 504}


def fetch_with_retry(url: str, headers: dict, params: dict | None = None, max_retries: int = 3) -> requests.Response:
    delay = 1.0
    for attempt in range(max_retries + 1):
        r = requests.get(url, headers=headers, params=params, timeout=(10, 15))
        if r.status_code not in RETRYABLE or attempt == max_retries:
            return r
        time.sleep(delay)
        delay *= 2
    return r

For plan quotas, overage pricing, and Historical ADS-B path tiers, see Rate Limits.