# Aviation Weather API — METAR & TAF Reports and Watch (NOAA AWC) (`mouadapi/aviation-weather`) Actor

METAR and TAF reports for any airport by ICAO code from the Aviation Weather Center Data API, or only new ones: raw text, flight category, wind, clouds; failed or unchanged rows are never charged. Not affiliated with or endorsed by NOAA, the National Weather Service or the Aviation Weather Center.

- **URL**: https://apify.com/mouadapi/aviation-weather.md
- **Developed by:** [COMPASS DEV](https://apify.com/mouadapi) (community)
- **Categories:** Travel, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 report returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Returns the latest METAR and TAF aviation weather reports for any airport by ICAO code from the Aviation Weather Center's public Data API, or in watch mode only the new reports since your last run; failed or unchanged rows are never charged.

Give ICAO station codes (KJFK, EGLL, LFPG, RJTT …); get one flat row per station and report type: the coded report as
published, the station's name and position, and the source's own decoded fields: the flight category (VFR, MVFR, IFR,
LIFR), wind, gusts, visibility, clouds, weather, temperature, dew point and altimeter for a METAR, and the validity period
and forecast groups for a TAF. Built for flight-ops dashboards, drone and charter operators, airport and logistics tools,
travel apps and AI agents that need airport weather without polling a website. *Not affiliated with or endorsed by NOAA,
the National Weather Service or the Aviation Weather Center.*

**Not an official weather briefing.** These are the published reports as data, for information. For flight planning and
operations, use an official briefing service and the authorities' own products.

### What it does

- **Export mode:** the latest METAR (observation) and TAF (forecast) of every station you give, one row each. METARs are
  usually issued every hour (and as SPECIs when the weather changes); TAFs about every 6 hours.
- **Watch mode** (give a watch list name in `stateName`): remembers each station's latest report and returns only:
  - `baseline`: the first run of the list;
  - `new`: a station added to the list;
  - `changed`: a newer or corrected report (`changedFields` and `previousValues` say what and from what, for example the
    previous flight category);
  - unchanged reports are free and left out (`includeUnchanged: true` returns them as free rows).
  - A watch run with nothing new returns **exactly one free `no_data` row** that says so.
- **Flight category alerts:** with `onlyCategoryChanges: true`, a watch run (after its baseline) returns only the METARs
  whose flight category changed, for example from VFR to IFR. Every other new report is left out, free.
- **Worldwide stations**, as the Aviation Weather Center publishes them: up to 400 stations a run, 100 per request.
- **You are never charged for failed results:** failed, no_data and unchanged rows are free.

### Quick start

The latest METAR and TAF of New York JFK and London Heathrow:

```json
{ "stations": ["KJFK", "EGLL"] }
```

Watch three airports for new reports (the first run is the baseline; run it every 30 or 60 minutes with an Apify
schedule):

```json
{ "stations": ["KJFK", "KBOS", "KDCA"], "stateName": "east-coast" }
```

Alert only when a flight category changes (VFR, MVFR, IFR, LIFR):

```json
{ "stations": ["KSFO", "KOAK", "KSJC"], "reportTypes": ["metar"], "stateName": "bay-area", "onlyCategoryChanges": true }
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `stations` | list of ICAO codes | — (prefilled `KJFK`, `EGLL`) | Four letters or digits each; at most 400 a run. Never a URL |
| `reportTypes` | list of `metar`, `taf` | `["metar","taf"]` | The latest observation and the latest forecast of each station |
| `onlyCategoryChanges` | boolean | `false` | Watch: after the baseline, only METARs whose flight category changed |
| `mode` | `watch` or `export` | — | Empty: watch when `stateName` is given, otherwise export. An explicit mode wins |
| `stateName` | string | — (prefilled `aviation-weather`) | Watch list name. Watch mode without a name uses the list `default` |
| `includeUnchanged` | boolean | `false` | Watch: also return unchanged reports (free) |
| `maxItems` | integer 1–100,000 | `1000` | Most charged reports per run; in watch mode the rest come in the next run |

Adding or removing stations keeps a watch list as it is (a new station gets a `new` row); changing `reportTypes` starts a
new baseline.

### Output

```json
{
    "status": "ok",
    "attempts": 1,
    "error": null,
    "input": "KJFK",
    "mode": "export",
    "watchList": null,
    "dropReason": null,
    "reportType": "metar",
    "stationId": "KJFK",
    "stationName": "New York/JF Kennedy Intl, NY, US",
    "latitude": 40.6392,
    "longitude": -73.7639,
    "elevationM": 3,
    "rawText": "METAR KJFK 050651Z 32007KT 10SM BKN037 OVC050 14/12 A3002 RMK AO2 SLP165 T01390122 $",
    "observedAt": "2026-10-05T06:51:00.000Z",
    "issuedAt": null,
    "validFrom": null,
    "validTo": null,
    "flightCategory": "VFR",
    "metarType": "METAR",
    "temperatureC": 13.9,
    "dewpointC": 12.2,
    "windDirection": "320",
    "windSpeedKt": 7,
    "windGustKt": null,
    "visibility": "10+",
    "altimeterHpa": 1016.7,
    "weather": null,
    "cloudCover": "OVC",
    "clouds": "BKN 3700 | OVC 5000",
    "verticalVisibilityFt": null,
    "forecastPeriods": null,
    "changeType": null,
    "changedFields": null,
    "previousValues": null,
    "url": "https://aviationweather.gov/api/data/metar?ids=KJFK&format=json",
    "source": "Aviation Weather Center Data API (NOAA National Weather Service). U.S. Government work, not subject to copyright (17 U.S.C. § 403 notice).",
    "license": "U.S. Government work (NOAA/NWS), public domain",
    "licenseUrl": "https://www.weather.gov/disclaimer",
    "scrapedAt": "2026-10-05T07:10:00.000Z"
}
```

| Status | Meaning | Charged? |
|---|---|---|
| `ok` | A report returned | Export: yes. Watch: `baseline`, `new` and `changed` yes; `unchanged` no |
| `no_data` | No report of that type for the station right now, or nothing new since the last run | No |
| `failed` | An invalid station code, or no answer after retries (`error` says why) | No |

The key-value store holds `RUN_REPORT` (counts, charged and free rows, stop reason) and, when something fails, the raw
response (`SNAPSHOT_*`).

### Output fields

### Output fields

| Field | Type | Description |
|---|---|---|
| `status` | string (or null) | ok = report returned (charged in export mode and for baseline, new and changed rows; free when unchanged); no_data = nothing to return (no report for the station, or nothing new since the last run) (free); failed = invalid input or no answer (free) |
| `attempts` | integer (or null) | Requests made for this row (retries included) |
| `error` | string (or null) | Why a row is no_data or failed; null on ok rows |
| `input` | string (or null) | The station code this row came from, or the input that failed |
| `mode` | string (or null) | watch or export |
| `watchList` | string (or null) | Watch list name (watch mode); null in export mode |
| `dropReason` | string (or null) | Always null: stations and coded reports name no one, so nothing is left out for people |
| `reportType` | string (or null) | metar (observation) or taf (terminal aerodrome forecast) |
| `stationId` | string (or null) | ICAO station code |
| `stationName` | string (or null) | The station's name as the Aviation Weather Center gives it |
| `latitude` | number (or null) | Station latitude (degrees) |
| `longitude` | number (or null) | Station longitude (degrees) |
| `elevationM` | number (or null) | Station elevation in metres |
| `rawText` | string (or null) | The report as published, unmodified (the coded METAR or TAF) |
| `observedAt` | string (or null) | METAR: observation time (ISO 8601, UTC) |
| `issuedAt` | string (or null) | TAF: issue time (ISO 8601, UTC) |
| `validFrom` | string (or null) | TAF: start of the forecast period (ISO 8601, UTC) |
| `validTo` | string (or null) | TAF: end of the forecast period (ISO 8601, UTC) |
| `flightCategory` | string (or null) | METAR: the source's flight category (VFR, MVFR, IFR, LIFR) |
| `metarType` | string (or null) | METAR (routine) or SPECI (special) |
| `temperatureC` | number (or null) | METAR: air temperature in degrees Celsius |
| `dewpointC` | number (or null) | METAR: dew point in degrees Celsius |
| `windDirection` | string (or null) | METAR: degrees true as text, or VRB (variable) |
| `windSpeedKt` | number (or null) | METAR: wind speed in knots |
| `windGustKt` | number (or null) | METAR: gust speed in knots |
| `visibility` | string (or null) | METAR: visibility in statute miles as the source gives it ("10+" means 10 or more) |
| `altimeterHpa` | number (or null) | METAR: altimeter setting in hectopascals |
| `weather` | string (or null) | METAR: present weather codes (e.g. -RA BR) |
| `cloudCover` | string (or null) | METAR: the source's overall cover (SKC, FEW, SCT, BKN, OVC …) |
| `clouds` | string (or null) | METAR: cloud layers as "<cover> <base ft>", joined with " / " |
| `verticalVisibilityFt` | number (or null) | METAR: vertical visibility in feet (an obscured sky) |
| `forecastPeriods` | integer (or null) | TAF: the number of forecast groups (FM, TEMPO, PROB …) |
| `changeType` | string (or null) | Watch mode: baseline (first run of the list), new (a station added), changed (a newer or corrected report) or unchanged; null in export mode |
| `changedFields` | string (or null) | Watch mode: the tracked fields that changed, comma-separated |
| `previousValues` | string (or null) | Watch mode: the changed fields' previous values, as JSON (e.g. the previous flight category) |
| `url` | string (or null) | The documented Data API request for this station and report type |
| `source` | string (or null) | The data source and its copyright notice |
| `license` | string (or null) | The terms of the data |
| `licenseUrl` | string (or null) | Where the terms are published |
| `scrapedAt` | string (or null) | When the row was made (ISO 8601) |

### Pricing

Pay per event: one `report` event per returned report (export: every report; watch: `baseline`, `new` and `changed` rows).

| Plan | Price per report | Per 1,000 reports |
|---|---|---|
| Free (no discount) | $0.0015 | $1.50 |
| Bronze | $0.0013 | $1.30 |
| Silver | $0.00115 | $1.15 |
| Gold (and Platinum, Diamond) | $0.0010 | $1.00 |

- Never charged: failed rows (an invalid code, no answer), no_data rows (no report, nothing new) and unchanged rows. You are
  never charged for failed results.
- Apify also charges its small per-run start event. There are no usage fees on top, so the actor is eligible for x402
  agent payments.

### Use it from AI agents

One clear main input, `stations` (ICAO codes); every row has `status`, `error`, `reportType`, `stationId`, `rawText` and
`scrapedAt`. Call it through the Apify API, the Apify MCP server (`mouadapi/aviation-weather`) or x402 agentic payments.
Copy-paste call (your Apify token in place of `YOUR_APIFY_TOKEN`):

```bash
curl -X POST "https://api.apify.com/v2/acts/mouadapi~aviation-weather/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" -H "Content-Type: application/json" -d '{"stations": ["KJFK", "EGLL"]}'
```

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('mouadapi/aviation-weather').call({ stations: ['KSFO', 'KLAX'], reportTypes: ['metar'], stateName: 'west', onlyCategoryChanges: true });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Limits

- The Aviation Weather Center limits its Data API to 100 requests a minute; this actor sends one request every 2 seconds
  at most, and a run needs only one per 100 stations and report type. If the API answers HTTP 429, the run pauses 60, 120
  and 240 seconds on the same connection, then stops with free `failed` rows; an HTTP 403 is a block and stops the run. No
  other IP or proxy is ever tried.
- The latest report of each station only (no history), as the API gives it; times are UTC.
- Some stations publish METARs but no TAF (a free `no_data` row says so), and a station that does not report has none.

### Known issues

- The decoded fields (flight category, wind, visibility, clouds) are the Aviation Weather Center's own; the coded
  `rawText` is the report as published and is the reference.

### FAQ

**Which stations can I use?** Any ICAO code the Aviation Weather Center publishes reports for, worldwide (US airports
start with K, Canadian with C, UK with EG, French with LF …).

**Why did a watch run return one `no_data` row?** Nothing was new since the last run of the watch list; the row says how
many reports were unchanged. It is free.

### Data and licence

- Source: the Aviation Weather Center's Data API (aviationweather.gov/api/data), documented at
  https://aviationweather.gov/data/api/.
- Terms: the National Weather Service's disclaimer: NWS information "is in the public domain, unless specifically noted
  otherwise, and may be used without charge for any lawful purpose" so long as it is not claimed as one's own, not used to
  imply an endorsement or affiliation with NOAA/NWS, and not modified and presented as official government material.
  Notice under 17 U.S.C. § 403: the reports in this actor's output are U.S. Government works, not subject to copyright.
- This actor is not affiliated with, endorsed by or provided by NOAA, the National Weather Service or the Aviation Weather
  Center. It returns the reports as published, unmodified; it is not an official weather briefing.

# Actor input Schema

## `stations` (type: `array`):

ICAO station codes, one per line: four letters or digits, e.g. KJFK (New York JFK), EGLL (London Heathrow), LFPG (Paris CDG), RJTT (Tokyo Haneda). At most 400 a run. Never a URL.

## `reportTypes` (type: `array`):

metar: the latest observation of each station; taf: the latest terminal aerodrome forecast. Default: both.

## `onlyCategoryChanges` (type: `boolean`):

Watch mode: after the first run, return only the METARs whose flight category changed (VFR, MVFR, IFR, LIFR). Every other new report is left out and free.

## `mode` (type: `string`):

Empty: watch when a watch list name is given, otherwise export. "watch" returns only reports that are new or changed since the last run of the watch list; "export" returns every station's latest report. An explicit mode wins.

## `stateName` (type: `string`):

Name of your watch list (kept in your own storage between runs). Giving a name turns on watch mode. Watch mode without a name uses the list "default".

## `includeUnchanged` (type: `boolean`):

Watch mode: also return reports that did not change, as free rows.

## `maxItems` (type: `integer`):

Most reports returned and charged per run. In watch mode the rest come in the next run.

## Actor input object example

```json
{
  "stations": [
    "KJFK",
    "EGLL",
    "LFPG"
  ],
  "reportTypes": [
    "metar",
    "taf"
  ],
  "onlyCategoryChanges": false,
  "stateName": "aviation-weather",
  "includeUnchanged": false,
  "maxItems": 1000
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with one row per station and report type, or per entry when nothing is returned

## `runReport` (type: `string`):

Summary of the run (counts, charged and free rows, stop reason)

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "stations": [
        "KJFK",
        "EGLL"
    ],
    "stateName": "aviation-weather"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mouadapi/aviation-weather").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "stations": [
        "KJFK",
        "EGLL",
    ],
    "stateName": "aviation-weather",
}

# Run the Actor and wait for it to finish
run = client.actor("mouadapi/aviation-weather").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "stations": [
    "KJFK",
    "EGLL"
  ],
  "stateName": "aviation-weather"
}' |
apify call mouadapi/aviation-weather --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mouadapi/aviation-weather"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DyJsn15hkOfQ15CU3/builds/zzs9xVwbWA0I3huAv/openapi.json
