TAF

TAFs (Terminal Aerodrome Forecasts) are structured forecasts for a specific airport and its immediate vicinity, typically covering roughly five nautical miles around the aerodrome. Standard TAFs are issued four times daily at 00Z, 06Z, 12Z, and 18Z and remain valid for 24 or 30 hours depending on the station. Each TAF describes expected wind, visibility, cloud layers, weather phenomena, and flight categories across timed forecast groups such as FROM, TEMPO, and BECMG. Pilots, dispatchers, and flight planners use these forecasts to assess expected conditions at departure, arrival, and alternate airports when building operational plans. Many small airports publish METAR only — a TAF 404 is common and does not mean the airport is unknown.

Request

Requirements

Auth
x-api-key on every request (direct subscription). Learn more →
ICAO
4-letter airport code (e.g. KJFK). US fields often use a leading K. Learn more →

Send a GET with the airport ICAO in the path. Optional query flags control decoding.

GEThttps://data.skylinkapi.com/v3/weather/taf/{icao}
ParameterTypeRequiredDefaultDescription
icaostringYes-4-letter ICAO code — lookup, e.g. KJFK
parsedbooleanNofalseAdd decoded forecast periods under a parsed key

Response

Raw (default)

{
  "raw": "TAF KJFK 072334Z 0800/0906 35014G20KT P6SM FEW050 SCT100 FM080600 03011KT P6SM SCT025 ...",
  "icao": "KJFK",
  "airport_name": "John F. Kennedy International Airport",
  "timestamp": "2026-06-08T23:45:12Z"
}
FieldTypeRequiredDescription
rawstringYesFull TAF string as issued
icaostringYesICAO code
airport_namestringYesAirport name
timestampstring (ISO 8601)YesWhen the API fetched this data — not the forecast issue time

Parsed (parsed=true)

{
  "raw": "TAF KJFK 061933Z 0620/0724 ...",
  "icao": "KJFK",
  "airport_name": "John F. Kennedy International Airport",
  "timestamp": "2026-06-06T23:09:48Z",
  "parsed": {
    "start_time": "2026-06-06T22:00:00+02:00",
    "end_time": "2026-06-08T02:00:00+02:00",
    "forecast": [
      {
        "type": "FROM",
        "start_time": "2026-06-06T22:00:00+02:00",
        "end_time": "2026-06-07T01:00:00+02:00",
        "wind": { "direction": 200, "speed": 14, "gust": null, "variable": [] },
        "visibility": { "value": null, "repr": "P6" },
        "clouds": [
          { "type": "SCT", "base": 70, "repr": "SCT070" },
          { "type": "BKN", "base": 250, "repr": "BKN250" }
        ],
        "wx_codes": [],
        "flight_rules": "VFR",
        "probability": null,
        "turbulence": [],
        "icing": []
      },
      {
        "type": "TEMPO",
        "start_time": "2026-06-07T01:00:00+02:00",
        "end_time": "2026-06-07T05:00:00+02:00",
        "wind": { "direction": 290, "speed": 25, "gust": 45, "variable": [] },
        "visibility": { "value": 3, "repr": "3" },
        "clouds": [
          { "type": "SCT", "base": 25, "repr": "SCT025" },
          { "type": "BKN", "base": 50, "repr": "BKN050CB" }
        ],
        "wx_codes": [
          { "repr": "TSRA", "value": "Thunderstorm Rain" },
          { "repr": "BR",   "value": "Mist" }
        ],
        "flight_rules": "MVFR",
        "probability": null,
        "turbulence": [],
        "icing": []
      }
    ]
  }
}
FieldTypeRequiredDescription
parsedobjectYes (with parsed=true)Absent when parsed=false — not null
parsed.start_timestring (ISO 8601)YesStart of the TAF valid period
parsed.end_timestring (ISO 8601)YesEnd of the TAF valid period
parsed.forecastarrayYesForecast periods in chronological order
parsed.forecast[].typestringYes (per item)FROM, TEMPO, or BECMG
parsed.forecast[].start_timestring (ISO 8601)Yes (per item)When this group becomes active
parsed.forecast[].end_timestring (ISO 8601)Yes (per item)When this group ends
parsed.forecast[].windobjectYes (per item)Same structure as METAR parsed.wind
parsed.forecast[].visibilityobjectYes (per item)Same structure as METAR parsed.visibility
parsed.forecast[].cloudsarrayYes (per item)Same structure as METAR parsed.clouds — cloud cover codes
parsed.forecast[].wx_codesarrayYes (per item)Expected weather phenomena — { repr, value }[]
parsed.forecast[].flight_rulesstringYes (per item)Expected flight category for this group
parsed.forecast[].probabilityobject | nullYes (key always present)Populated on PROB30/PROB40 groups
parsed.forecast[].turbulencearrayYes (per item)Turbulence intensity when issued — [] when none
parsed.forecast[].icingarrayYes (per item)Icing intensity when issued — [] when none

Object schemas and nullability

Each object in parsed.forecast[] includes the same keys on every period — same rules as METAR nullability for wind, visibility, clouds, and wx_codes.

FieldTypeRequiredNull / emptyNotes
parsed.forecast[].windobjectYesAlways presentSame shape as METAR parsed.wind
parsed.forecast[].visibilityobjectYesAlways presentvalue: null common for P6SM
parsed.forecast[].cloudsarrayYes[] possibleSame element shape as METAR
parsed.forecast[].wx_codesarrayYes[] when noneSame { repr, value } item shape
parsed.forecast[].probabilityobject | nullYesnull on most groupsObject on PROB30/PROB40
parsed.forecast[].turbulencearrayYes[] when not issuedNever null
parsed.forecast[].icingarrayYes[] when not issuedNever null

Probability on PROB groups

"probability": {
  "repr": "30",
  "value": 30,
  "spoken": "three zero"
}
Turbulence, icing, and forecast period typesExpand section

Forecast period types

TAF tokentype in JSONWhat it means
Initial / FMFROMBaseline conditions — each FM group replaces everything before it
TEMPOTEMPOTemporary fluctuations within the baseline
BECMGBECMGGradual change toward values in this group by end_time
PROB30 / PROB40FROM or TEMPO + probabilityProbabilistic sub-period

Client types

"""TAF response and departure-summary types - matches weather-taf field tables."""from __future__ import annotationsfrom dataclasses import dataclassfrom typing import LiteralFlightRules = Literal["VFR", "MVFR", "IFR", "LIFR"]TafPeriodType = Literal["FROM", "TEMPO", "BECMG"]@dataclassclass TafRequest:    """Path/query input for GET /weather/taf/{icao}."""    icao: str    parsed: bool = False@dataclassclass TafWind:    direction: int    speed: int    gust: int | None    variable: list[int]@dataclassclass TafVisibility:    value: float | None    repr: str@dataclassclass TafCloudLayer:    type: str    base: int    repr: str@dataclassclass TafWxCode:    repr: str    value: str@dataclassclass TafProbability:    repr: str    value: Literal[30, 40]    spoken: str@dataclassclass TafPeriod:    type: TafPeriodType    start_time: str    end_time: str    wind: TafWind    visibility: TafVisibility    clouds: list[TafCloudLayer]    wx_codes: list[TafWxCode]    flight_rules: FlightRules    probability: TafProbability | None    turbulence: list[object]    icing: list[object]@dataclassclass TafParsed:    start_time: str    end_time: str    forecast: list[TafPeriod]@dataclassclass TafResponse:    raw: str    icao: str    airport_name: str    timestamp: str    parsed: TafParsed | None = None@dataclassclass TafModifierSummary:    type: TafPeriodType    category: FlightRules    probability: int | None    wx: list[str]@dataclassclass TafDepartureSummary:    """Shape returned by taf_at_departure() in the fetch example."""    icao: str    valid_to: str    baseline_category: FlightRules | None    modifiers: list[TafModifierSummary]    worst_from_category: FlightRules | None

Integration

Fetch, cache, and interpret forecast periods at a departure time.

import osimport timefrom dataclasses import dataclassfrom datetime import datetime, timezonefrom typing import Literalimport requests# TafDepartureSummary - helper return type. Full wire types (TafResponse, TafPeriod, …)# are in the Client types section on the TAF docs page.FlightRules = Literal["VFR", "MVFR", "IFR", "LIFR"]TafPeriodType = Literal["FROM", "TEMPO", "BECMG"]@dataclassclass TafModifierSummary:    type: TafPeriodType    category: FlightRules    probability: int | None    wx: list[str]@dataclassclass TafDepartureSummary:    icao: str    valid_to: str    baseline_category: FlightRules | None    modifiers: list[TafModifierSummary]    worst_from_category: FlightRules | NoneHEADERS = {    "x-api-key": os.getenv("SKYLINK_API_KEY", "YOUR_API_KEY")}BASE = "https://data.skylinkapi.com/v3"CACHE: dict[str, tuple[dict, float]] = {}TAF_TTL = 900CATEGORY_RANK = {"VFR": 0, "MVFR": 1, "IFR": 2, "LIFR": 3}def parse_time(iso: str) -> datetime:    return datetime.fromisoformat(iso.replace("Z", "+00:00"))def fetch_taf(icao: str) -> dict | None:    cache_key = f"taf:{icao}:parsed"    cached = CACHE.get(cache_key)    if cached and time.time() < cached[1]:        return cached[0]    try:        r = requests.get(            f"{BASE}/weather/taf/{icao}",            headers=HEADERS,            params={"parsed": "true"},            timeout=(10, 15),        )    except requests.Timeout as exc:        raise RuntimeError(f"SkyLink timeout fetching TAF for {icao}") from exc    if r.status_code == 404:        return None    r.raise_for_status()    data = r.json()    CACHE[cache_key] = (data, time.time() + TAF_TTL)    return datadef taf_at_departure(icao: str, departure: datetime | None = None) -> TafDepartureSummary | None:    taf = fetch_taf(icao)    if not taf:        return None    at = departure or datetime.now(timezone.utc)    forecast = taf["parsed"]["forecast"]    baseline = next(        (            p            for p in forecast            if p["type"] == "FROM"            and parse_time(p["start_time"]) <= at <= parse_time(p["end_time"])        ),        None,    )    modifiers = [        TafModifierSummary(            type=p["type"],            category=p["flight_rules"],            probability=(p["probability"] or {}).get("value"),            wx=[w["repr"] for w in p["wx_codes"]],        )        for p in forecast        if p is not baseline and parse_time(p["start_time"]) <= at <= parse_time(p["end_time"])    ]    from_groups = [p for p in forecast if p["type"] == "FROM"]    worst = (        max(from_groups, key=lambda p: CATEGORY_RANK.get(p["flight_rules"], -1))["flight_rules"]        if from_groups        else None    )    return TafDepartureSummary(        icao=icao,        valid_to=taf["parsed"]["end_time"],        baseline_category=baseline["flight_rules"] if baseline else None,        modifiers=modifiers,        worst_from_category=worst,    )if __name__ == "__main__":    import json    from dataclasses import asdict    summary = taf_at_departure("KJFK")    print(json.dumps(asdict(summary), indent=2) if summary else "TAF not available")

Implementation notes

Reading TEMPO and probabilistic groups. TEMPO layers on the active FROM baseline. PROB30/PROB40 appear as rows with non-null probability.

TAFs update four times a day at 00Z, 06Z, 12Z, and 18Z. Caching for 15–30 minutes is appropriate.

404 without a TAF. Many GA fields publish METAR only. Treat TAF 404 as missing forecast data, not a failed request.

Error responses

404 — no TAF for this ICAO

{
  "detail": "No current TAF forecast available for KOXB. Weather station may not issue TAF reports or may be temporarily offline."
}
GET
/weather/taf/{icao}
x-api-key<token>

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

In: header

Path Parameters

icao*string

4-letter ICAO airport code

Length4 <= length <= 4

Query Parameters

parsed?boolean

Include parsed/decoded TAF fields alongside raw text

Defaultfalse

Response Body

application/json

application/json

curl -X GET "https://data.skylinkapi.com/v3/weather/taf/KJFK"
{
  "raw": "TAF KJFK 271720Z 2718/2824 16012KT P6SM FEW025 SCT250 FM272100 18010KT P6SM SCT025 BKN250",
  "icao": "KJFK",
  "airport_name": "John F Kennedy International Airport",
  "timestamp": "2025-09-27T12:00:00Z"
}
{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string",
      "input": null,
      "ctx": {}
    }
  ]
}

Related: METAR · Use cases · Errors · Production