# METAR Weather Observations API - Airport Temperature, Hourly (`neverempty/metar-weather-observations`) Actor

For aviation dashboards, weather-market traders and climate analysts: airport weather by ICAO or IATA code with temperature, dewpoint, wind, pressure, visibility, clouds, flight category and raw METAR, latest or up to 30 days back (KJFK: 772 reports in 30 days). Monitoring returns only new reports.

- **URL**: https://apify.com/neverempty/metar-weather-observations.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Developer tools, Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.19 / 1,000 observation 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

## METAR Weather Observations API - Airport Temperature, Hourly

Weather observations from airports worldwide, read from the public API of **aviationweather.gov** (the NOAA / US National Weather Service Aviation Weather Center). Give airport codes, get one row per observation: temperature, dewpoint, humidity, wind, pressure, visibility, clouds, flight category and the original METAR text.

- **Any airport that sends METAR reports**, by 4-letter ICAO code (`KJFK`, `EGLL`, `RJTT`) or 3-letter IATA code (`JFK`, `LHR`, `HND`)
- **The latest report, or every report up to 30 days back** (`hoursBack` up to 720)
- **Monitoring mode**: run it on a schedule and receive only observations you have not received yet
- No API key, no browser, no scraping of web pages

### What a row looks like

```json
{
  "icaoCode": "KJFK", "iataCode": "JFK", "stationName": "New York/JF Kennedy Intl", "country": "US",
  "observedAt": "2026-09-18T14:51:00.000Z", "reportType": "METAR",
  "temperatureC": 26.1, "temperatureF": 79, "dewpointC": 17.8, "relativeHumidityPct": 60.3,
  "windDirectionDeg": 10, "windSpeedKt": 12, "windSpeedKmh": 22.2, "windGustKt": null,
  "visibilityMiles": 10, "visibilityMilesOrMore": true,
  "altimeterHpa": 1018.7, "altimeterInHg": 30.08, "seaLevelPressureHpa": 1018.5,
  "cloudCover": "BKN", "ceilingFtAgl": 8000, "flightCategory": "VFR",
  "rawMetar": "METAR KJFK 181451Z 01012KT 10SM SCT020 BKN080 BKN250 26/18 A3008 RMK AO2 SLP185 T02610178 51007"
}
```

### Output fields

| Field | |
|---|---|
| `icaoCode` / `iataCode` / `inputCode` | the station, and the code you typed |
| `stationName` / `country` / `region` | from aviationweather.gov's official station list (`region` is the state or province code as listed there, e.g. `NY`, or `13` for Tokyo) |
| `latitude` / `longitude` / `elevationM` | station position as given with each report |
| `observedAt` | observation time in UTC (matches the day and time group of the METAR, e.g. `181451Z`) |
| `reportType` / `isSpecialReport` / `isCorrection` / `isAutomated` | METAR or SPECI (special report when the weather changes), COR, AUTO |
| `temperatureC` / `temperatureF` / `dewpointC` / `dewpointF` | °C and °F. US stations report tenths of a degree, most others whole degrees |
| `relativeHumidityPct` | **computed** from temperature and dewpoint (Magnus formula); METAR does not report humidity |
| `windDirectionDeg` / `isWindVariable` / `isCalm` | degrees true; `null` and `isWindVariable: true` for variable wind (VRB); `null` and `isCalm: true` for calm wind (`00000KT`), so calm is never read as a north wind |
| `windSpeedKt` / `windSpeedKmh` / `windGustKt` / `windGustKmh` | knots as given by aviationweather.gov, and km/h (for stations reporting in m/s, km/h is computed from the original m/s value) |
| `windUnitInReport` / `windSpeedInReport` / `windGustInReport` | the unit and numbers in the METAR itself. Stations that report in m/s (Russia, China and others) are converted to knots by aviationweather.gov in whole knots (4 m/s becomes 8 kt), so the original is kept here |
| `visibilityMiles` / `visibilityMilesOrMore` / `visibilityText` | statute miles; `10+` and `6+` mean "at least" |
| `visibilityMeters` / `visibilityMetersOrMore` | for stations that report meters: `9999` and `CAVOK` are 10,000 m or more. `null` for stations that report miles |
| `altimeterHpa` / `altimeterInHg` / `seaLevelPressureHpa` / `pressureTendency3hHpa` | pressure |
| `weather` | present weather codes such as `-SHRA` (light rain showers), `BR` (mist) |
| `cloudCover` / `cloudLayers` / `ceilingFtAgl` / `verticalVisibilityFt` | cloud layers with base in feet above ground; ceiling = lowest broken or overcast layer (or vertical visibility) |
| `flightCategory` | VFR, MVFR, IFR or LIFR |
| `maxTemp6hC` / `minTemp6hC` / `maxTemp24hC` / `minTemp24hC` | when the station reports them (mostly US stations, at synoptic hours) |
| `precipLastHourIn` / `precip3hIn` / `precip6hIn` / `precip24hIn` / `snowDepthIn` | inches, when reported. `0.005` means a trace (less than 0.01 inch, reported as `P0000`), not a measured amount: do not add it up as rain |
| `rawMetar` | the original report |
| `observationId` | stable id of the report (station, time, type and text) |
| `monitorStatus` | monitoring mode only: `first-check` or `new` |
| `requestedHours` / `scrapedAt` | what was asked and when |

**Missing readings stay `null`.** A value that is not in the report is never filled with `0`. aviationweather.gov's own API specification gives temperature a default of `0`, so the temperature is returned only when the METAR text itself has a temperature group and the two agree within 1 °C; otherwise temperature, dewpoint and humidity are `null`.

### Input

```json
{ "airports": ["KJFK", "LHR", "RJTT"], "hoursBack": 24 }
```

| Input | |
|---|---|
| `airports` | ICAO codes (`KJFK`) or IATA codes (`JFK`), one per line or separated by commas. IATA codes are converted with aviationweather.gov's station list; a code that belongs to two stations is not guessed. Up to 500 per run. |
| `hoursBack` | Every report from the last N hours, newest first. `0` = only the latest report. Up to `720` (30 days). |
| `maxResults` | Stop after this many observation rows (monitoring off). Default 1,000. |
| `monitoringMode` | Return only observations not returned by an earlier monitoring run. See below. |
| `resetMonitoringState` | Forget what monitoring remembered. |
| `useProxy` | Retry through a proxy only if aviationweather.gov refuses the connection (HTTP 403). |

### Monitoring mode

Schedule the Actor (for example every hour) with `monitoringMode: true`. The first run returns the `hoursBack` window as a baseline. Every later run returns only observations that were not returned before: new hourly reports, special reports, and reports that arrived late or were corrected. Rows come oldest first in this mode.

A run that was delayed does not skip anything: each airport is read back to the last observation already returned (up to 30 days). If nothing is new, you get one free `no-new-observations` row.

Observations are remembered per airport. Do not put the same airport in two schedules that can run at the same time.

### Rows that explain instead of guessing (free)

| `status` | when |
|---|---|
| `no-reports` | aviationweather.gov answered that there is no report in the window (HTTP 204). The row says whether the code is in the official station list at all |
| `invalid-input` / `unknown-iata-code` / `ambiguous-iata-code` | the code could not be used; nothing was requested for it |
| `blocked` / `unreadable` / `refused` | the request failed after retries; the airport is **not** reported as having no weather |
| `incomplete` | part of a long window could not be read |
| `duplicate` / `not-checked` / `budget-reached` / `no-new-observations` | what was not returned and why |

### Reliability and conduct

aviationweather.gov asks clients to send their own User-Agent, to stay under 100 requests per minute, and returns at most 400 reports per request. This Actor sends its own User-Agent, waits between requests, waits 30 seconds after a "too many requests" answer, retries outages, and reads long windows page by page so a 30-day request is not silently cut at 400 reports.

Measured on 2026-09-18: 30 days of KJFK were 772 reports in two requests; five airports on four continents were read twice 20 seconds apart and returned identical reports.

### Pricing

- **$3.00 per 1,000 observation rows** returned
- Monitoring mode: **$0.30 per 1,000 airport checks** (one check per airport whose reports were read in a run), plus the rows returned

Explanation rows are free. If you set a maximum total charge on the run, the Actor stops before exceeding it and says so in a free row and in the run status.

### Data source

Aviation Weather Center, NOAA / National Weather Service: `https://aviationweather.gov/api/data/metar` and the station list `https://aviationweather.gov/data/cache/stations.cache.json.gz`. This Actor is not affiliated with NOAA or the National Weather Service. METAR reports are observations for aviation; this data is not an official source for flight planning.

# Actor input Schema

## `airports` (type: `array`):

Airports to read, one per line: 4-letter ICAO codes such as KJFK, EGLL, RJTT, YSSY (commas and spaces also separate codes). 3-letter IATA codes such as JFK or LHR are converted to ICAO with aviationweather.gov's official station list; an IATA code shared by two stations is not guessed and comes back as a free row. A repeated airport is read and charged once. If you leave this field out with monitoring off, three example airports (KJFK, EGLL, RJTT) are read; with monitoring on it is required. Up to 500 airports per run.

## `hoursBack` (type: `integer`):

Return every observation (routine METAR and special SPECI reports) from the last N hours, newest first (oldest first in monitoring mode). 0 = only the latest report of each airport. Maximum 720 (30 days, all that aviationweather.gov keeps). Most airports report once an hour, some every 30 minutes, plus special reports when the weather changes, so 24 hours is usually 24 to 60 rows per airport. In monitoring mode this is the window of the first run; later runs read back far enough to cover everything since the last observation returned.

## `maxResults` (type: `integer`):

With monitoring off, the run stops once this many observation rows have been returned, and a free row says what was not returned. Free rows do not count. In monitoring mode it does not limit the new observations returned: every airport in the list is checked, as far as the run's maximum total charge allows.

## `monitoringMode` (type: `boolean`):

Off = every observation in the window comes back, charged per row. On = the Actor remembers, per airport, which observations it already returned, and later runs return only newer ones (and late or corrected reports it had not returned). The first run returns the window once as a baseline. In monitoring mode every airport whose observations are read costs $0.30 per 1,000 checks, new data or not (airports with no report, unreadable codes and failed requests are free), plus the row price for the observations returned. Example: 20 airports every hour = 14,400 checks a month = $4.32, plus about 14,400 new hourly observations. The Actor reads only as many airports as the run's maximum total charge can pay for with a check and one row each. Observations are remembered per airport; do not put the same airport in two schedules that can run at the same time.

## `resetMonitoringState` (type: `boolean`):

Clears every remembered observation for this Actor, so the next monitoring run returns each airport's window again as a first check. This affects all your monitoring runs, because observations are remembered per airport rather than per list. Turn it off again after one run: left on in a schedule, every run returns the whole window again and charges for it.

## `useProxy` (type: `boolean`):

Every request goes directly to aviationweather.gov first. Only if it answers HTTP 403 (refused) is the request retried through a proxy session. A 429 (too many requests) is never retried through a proxy: the Actor waits 30 seconds instead, as the service asks. With it off, a refused request is retried directly and then returned as a free 'blocked' row.

## Actor input object example

```json
{
  "airports": [
    "KJFK",
    "EGLL",
    "RJTT"
  ],
  "hoursBack": 3,
  "maxResults": 1000,
  "monitoringMode": false,
  "resetMonitoringState": false,
  "useProxy": true
}
```

# Actor output Schema

## `results` (type: `string`):

One row per airport weather observation (METAR or SPECI) from aviationweather.gov: ICAO and IATA code, station name, country and position, observation time in UTC, temperature and dewpoint in C and F, relative humidity, wind direction, speed and gust in knots and km/h, visibility, altimeter and sea-level pressure, present weather, cloud layers and ceiling, flight category, precipitation and the raw METAR text; in monitoring mode only observations not returned by an earlier run. Airports with no report, unreadable codes, refusals and limits come back as free rows that say why.

# 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 = {
    "airports": [
        "KJFK",
        "EGLL",
        "RJTT"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/metar-weather-observations").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 = { "airports": [
        "KJFK",
        "EGLL",
        "RJTT",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/metar-weather-observations").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 '{
  "airports": [
    "KJFK",
    "EGLL",
    "RJTT"
  ]
}' |
apify call neverempty/metar-weather-observations --silent --output-dataset

```

## MCP server setup

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

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/U1eZzYOlCYb3RIiKo/builds/3x6H6zvquI0Tu3DAa/openapi.json
