New rate limits now apply to all accounts.

PIREPs (Pilot Reports)

PIREPs (Pilot Reports) are voluntary in-flight weather observations filed by pilots that capture conditions automated sensors miss — turbulence, icing, cloud tops, visibility, and temperature aloft — often minutes before they appear in forecasts. This endpoint returns every PIREP whose position falls inside your bounding box within a configurable time window. Use PIREPs alongside METAR, TAF, and AIRMET/SIGMET when you need recent pilot-observed hazards along a route. Reports are geographic — pass a bounding box around your flight path, not a single airport ICAO. Coverage is US-focused; queries over areas with low pilot traffic often return empty reports arrays even when the bbox is valid.

Report types: UA (routine PIREP) and UUA (urgent PIREP — significant hazard; prioritize in your UI).

Request

Requirements

Auth
x-api-key on every request (direct subscription). Learn more →

Send a GET with a geographic bounding box and optional look-back window.

GEThttps://data.skylinkapi.com/v3/weather/pireps
ParameterTypeRequiredDefaultDescription
bboxstringYes-Bounding box as lat1,lon1,lat2,lon2 — southwest corner first. Example: 39,-78,42,-71 (Northeast US corridor)
hoursintegerNo2How far back to search, in hours (1–24)

Bounding box tips. Size the box to your route segment plus a buffer — PIREPs are sparse outside busy airspace. An empty reports array is a normal response when no pilots filed reports in the area recently.

Response

{
  "bbox": "39.0,-78.0,42.0,-71.0",
  "hours": 3,
  "reports": [
    {
      "raw": "UA /OV JFK/TM 1845/FL085/TP B738/TB MOD/RM CONT MOD CHOP",
      "report_type": "UA",
      "location": "JFK",
      "time": "2025-01-15T18:45:00Z",
      "altitude": "FL085",
      "aircraft_type": "B738",
      "turbulence": "Moderate",
      "icing": null,
      "sky_conditions": null,
      "temperature": null,
      "wind": null,
      "remarks": "CONT MOD CHOP",
      "latitude": 40.64,
      "longitude": -73.78
    }
  ],
  "total": 1
}

When no reports match, the API returns "reports": [] and "total": 0 — not an error.

FieldTypeRequiredDescription
bboxstringYesEcho of the queried bounding box
hoursintegerYesLook-back window applied
reportsarrayYesMatching PIREPs (may be empty)
totalintegerYesCount of items in reports

Report

JSON path: reports[]. Only raw is guaranteed non-null. All decoded fields are null when not present in the filed report — expect sparse objects, especially on older or brief PIREPs.

FieldTypeRequiredNull?Description
rawstringYesNeverFull PIREP text as filed
report_typestring | nullYes (key always present)YesUA (routine) or UUA (urgent)
locationstring | nullYes (key always present)YesLocation identifier or bearing/distance from a navaid
timestring | nullYes (key always present)YesObservation time (UTC, ISO 8601)
altitudestring | nullYes (key always present)YesFlight level or altitude, e.g. FL085
aircraft_typestring | nullYes (key always present)YesAircraft type code, e.g. B738
sky_conditionsstring | nullYes (key always present)YesSky or cloud conditions
turbulencestring | nullYes (key always present)YesTurbulence intensity — see Intensity scales
icingstring | nullYes (key always present)YesIcing intensity — see Intensity scales
temperaturestring | nullYes (key always present)YesOutside air temperature
windstring | nullYes (key always present)YesWind direction and speed
remarksstring | nullYes (key always present)YesAdditional remarks
latitudenumber | nullYes (key always present)YesReport latitude when geocoded
longitudenumber | nullYes (key always present)YesReport longitude when geocoded

Object schemas and nullability

Every key in a Report object is always present — nothing is omitted when a field is not reported. Use null when the pilot did not include a section.

SituationWhat you getNot this
Pilot omitted turbulenceturbulence: nullkey missing
Pilot omitted icingicing: nullkey missing
Navaid-relative position onlylatitude: null, longitude: nullkey missing
No reports in windowreports: [], total: 0null, HTTP error
Routine reportreport_type: "UA"null
Urgent hazard reportreport_type: "UUA""UA"

Intensity scales

Decoded turbulence and icing strings follow standard PIREP intensity vocabulary. Values are decoded text; the exact string may vary slightly between reports.

Turbulence (examples)Meaning
NEG / NegativeNone reported
LGT / LightLight
MOD / ModerateModerate
SVR / SevereSevere
EXTM / ExtremeExtreme
Icing (examples)Meaning
NEG / NegativeNone
TRC / TraceTrace
LGT / LightLight
MOD / ModerateModerate
SVR / SevereSevere

Client types

"""PIREP response types - matches pireps field tables."""from dataclasses import dataclass@dataclassclass PirepReport:    raw: str    report_type: str | None = None    location: str | None = None    time: str | None = None    altitude: str | None = None    aircraft_type: str | None = None    sky_conditions: str | None = None    turbulence: str | None = None    icing: str | None = None    temperature: str | None = None    wind: str | None = None    remarks: str | None = None    latitude: float | None = None    longitude: float | None = None@dataclassclass PirepResponse:    bbox: str    hours: int    reports: list[PirepReport]    total: int

Integration

Poll PIREPs along a route bounding box and surface urgent reports first.

The example queries a corridor bbox, caches results for 10 minutes (PIREPs are ephemeral), and returns only UUA urgent reports plus the highest turbulence severity seen. An empty reports array is normal on quiet routes — render "no recent pilot reports," never an error state. Prioritize UUA reports above routine UA in dispatch or EFB UIs.

import osimport timefrom typing import TypedDictimport requestsHEADERS = {    "x-api-key": os.getenv("SKYLINK_API_KEY", "YOUR_API_KEY")}BASE = "https://data.skylinkapi.com/v3"CACHE: dict[str, tuple[dict, float]] = {}PIREP_TTL = 600  # 10 min - PIREPs are short-livedclass PirepReport(TypedDict, total=False):    raw: str    report_type: str | None    turbulence: str | None    icing: str | None    time: str | Nonedef fetch_pireps(bbox: str, hours: int = 3) -> dict:    cache_key = f"pirep:{bbox}:{hours}"    cached = CACHE.get(cache_key)    if cached and time.time() < cached[1]:        return cached[0]    r = requests.get(        f"{BASE}/weather/pireps",        headers=HEADERS,        params={"bbox": bbox, "hours": hours},        timeout=(10, 20),    )    r.raise_for_status()    data = r.json()    CACHE[cache_key] = (data, time.time() + PIREP_TTL)    return datadef urgent_reports(bbox: str, hours: int = 3) -> list[PirepReport]:    payload = fetch_pireps(bbox, hours=hours)    reports: list[PirepReport] = payload.get("reports", [])    return [r for r in reports if r.get("report_type") == "UUA"]if __name__ == "__main__":    import json    bbox = "-74.5,40.3,-73.5,41.0"    payload = fetch_pireps(bbox, hours=3)    print(json.dumps(payload, indent=2, default=str)[:3000])

Implementation notes

Sparse data is normal. Unlike METAR, PIREPs depend on pilots filing reports. Widen the bbox or increase hours before assuming the API is broken.

Prioritize UUA. Urgent PIREPs indicate significant hazards. Treat any UUA with turbulence: "SVR" or "EXTM" as an immediate alert.

Geocoding gaps. latitude and longitude are null when the report uses navaid-relative position only (e.g. 25 SW of ORL). Fall back to parsing location and raw for display; do not drop these reports.

Do not over-poll. A 5–15 minute refresh interval is sufficient. Combine with AIRMET/SIGMET for area-wide advisories.

time vs request time. time is the observation time from the PIREP — use it for "X minutes ago" display. There is no separate API fetch timestamp on PIREPs.

Error responses

Endpoint-specific failures only. For auth, validation, rate limits, and server errors, see Error Handling.

400 — invalid bbox format

{
  "detail": "Invalid bounding box format. Expected 'lat1,lon1,lat2,lon2'"
}

503 — upstream unavailable

{
  "detail": "PIREP service temporarily unavailable"
}

Retry with exponential backoff. A single retry after 5–10 seconds is usually sufficient.

GET
/weather/pireps
x-api-key<token>

Your SkyLink licence key, for keys bought direct from skylinkapi.com.

In: header

Query Parameters

bbox*string

Bounding box as 'lat1,lon1,lat2,lon2' (SW corner → NE corner)

hours?integer

Time window in hours (how far back to look)

Default2
Range1 <= value <= 24

Response Body

application/json

application/json

curl -X GET "https://data.skylinkapi.com/v3/weather/pireps?bbox=string"
{
  "bbox": "39,-78,42,-71",
  "hours": 2,
  "reports": [
    {
      "raw": "UA /OV JFK/TM 1845/FL085/TP B738/TB MOD/RM CONT MOD CHOP",
      "report_type": "UA",
      "location": "JFK",
      "time": "2025-01-15T18:45:00Z",
      "altitude": "FL085",
      "aircraft_type": "B738",
      "turbulence": "Moderate",
      "remarks": "CONT MOD CHOP"
    }
  ],
  "total": 1
}
Empty
{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string",
      "input": null,
      "ctx": {}
    }
  ]
}

Related: Weather hub · AIRMET/SIGMET · METAR · Urgent PIREP use case