# NWS Weather API: Forecast, Observations and Alerts (`conserving_celerytop/nws-weather-forecast-alerts-api`) Actor

Get the US National Weather Service forecast, latest valid station observation and active alerts for each coordinate or station ID, plus active alerts by state. One flat row per location. Public NWS data, $4.00 per 1,000 rows.

- **URL**: https://apify.com/conserving_celerytop/nws-weather-forecast-alerts-api.md
- **Developed by:** [Don Mangu](https://apify.com/conserving_celerytop) (community)
- **Categories:** Developer tools, News, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 location weathers

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

## NWS Weather API: Forecast, Observations and Alerts

NWS weather API for the United States that returns one flat row per location with the National Weather Service forecast, the latest valid station observation and the active alerts, from the public weather.gov API. Add coordinates, station IDs or state codes and run. Each row costs $4.00 per 1,000 on the Free plan, and the prefill run of 3 rows costs about one cent. I built this Actor as an independent developer. It is not affiliated with or endorsed by the National Weather Service or NOAA.

### Sample output

One row from a National Weather Service API run for downtown Los Angeles. Values were read from api.weather.gov on 2026-10-04 and change over time. Fields with no value (`null`) are left out here for space, and the alert text is shortened.

```json
{
  "recordType": "location",
  "query": "34.0522,-118.2437",
  "status": "ok",
  "latitude": 34.0522,
  "longitude": -118.2437,
  "place": "Los Angeles",
  "state": "CA",
  "timeZone": "America/Los_Angeles",
  "forecastOffice": "LOX",
  "gridId": "LOX",
  "gridX": 155,
  "gridY": 45,
  "forecastZone": "CAZ368",
  "county": "CAC037",
  "radarStation": "KSOX",
  "forecastGeneratedAt": "2026-10-04T07:45:34+00:00",
  "forecastUpdatedAt": "2026-10-04T06:26:38+00:00",
  "forecastUnits": "us",
  "forecastNowName": "Overnight",
  "forecastNowSummary": "Mostly Clear",
  "forecastNowTemperature": 77,
  "forecastNowTemperatureUnit": "F",
  "observationStationId": "KHHR",
  "observationStationName": "Hawthorne, Hawthorne Municipal Airport",
  "observationStationDistanceKm": 16.5,
  "observationStationsTried": 2,
  "observationTime": "2026-10-04T07:30:00+00:00",
  "observationAgeMinutes": 16,
  "observationDescription": "Clear",
  "temperatureC": 27,
  "temperatureF": 80.6,
  "dewpointC": 17,
  "relativeHumidityPercent": 54,
  "windSpeedKmh": 0,
  "windSpeedMph": 0,
  "windDirectionDegrees": 0,
  "stationPressureHpa": 1009.8,
  "visibilityKm": 16.09,
  "heatIndexC": 27.7,
  "alertCount": 1,
  "alertMaxSeverity": "Severe",
  "alertsTruncated": false,
  "fetchedAt": "2026-10-04T07:45:32.872Z",
  "forecastPeriods": [
    {
      "number": 1,
      "name": "Overnight",
      "startTime": "2026-10-04T00:00:00-07:00",
      "endTime": "2026-10-04T06:00:00-07:00",
      "isDaytime": false,
      "temperature": 77,
      "temperatureUnit": "F",
      "precipitationChancePercent": 1,
      "windSpeed": "5 mph",
      "windDirection": "ENE",
      "shortForecast": "Mostly Clear",
      "detailedForecast": "Mostly clear, with a low around 77. East northeast wind around 5 mph."
    }
  ],
  "alerts": [
    {
      "id": "urn:oid:2.49.0.1.840.0.469a5488af3dceb8f4d0e41c09f4dbf04bb51c75.002.1",
      "event": "Extreme Heat Warning",
      "severity": "Severe",
      "urgency": "Expected",
      "certainty": "Likely",
      "status": "Actual",
      "messageType": "Update",
      "headline": "Extreme Heat Warning issued October 3 at 12:15PM PDT until October 8 at 8:00PM PDT by NWS Los Angeles/Oxnard CA",
      "areaDescription": "Catalina and Santa Barbara Islands; Santa Clarita Valley; Ventura County Beaches; Ventura County Inl...",
      "sentAt": "2026-10-03T12:15:00-07:00",
      "onset": "2026-10-03T12:15:00-07:00",
      "ends": "2026-10-08T20:00:00-07:00",
      "expires": "2026-10-07T20:00:00-07:00",
      "senderName": "NWS Los Angeles/Oxnard CA",
      "response": "Execute",
      "description": "* WHAT...Dangerously hot conditions with temperatures up to 90 at the\ncoast and 105 in the valleys.\n\n* WHERE...Coast and valleys of Los Angeles and Ve...",
      "instruction": "Drink plenty of fluids, stay in an air-conditioned room, stay out of\nthe sun, an..."
    }
  ]
}
```

### How to use the National Weather Service API with this Actor

1. Open the Actor and add **Coordinates**, one latitude and longitude pair per line, for example `47.6062,-122.3321`. The prefill has two cities.
2. Add **Station IDs** such as `KSEA` or `KDEN` when you want the observation of one named station. Each ID returns that station's latest report with the forecast and alerts for its location.
3. Add **State codes** such as `WA`, `TX` or `PR` for one row of active alerts per state, territory or marine area.
4. Open **Advanced options** to change the number of forecast periods (1 to 14), the units (US or SI), the parts of each row and the alert filters.
5. Click Start. Rows appear in the dataset as they are saved. Use the Overview table, or export to JSON, CSV or Excel.

You can also call the Actor from code through the Apify API. The example below returns active Extreme and Severe alerts for three states.

```bash
curl -X POST "https://api.apify.com/v2/acts/conserving_celerytop~nws-weather-forecast-alerts-api/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"alertStates": ["TX", "LA", "FL"], "alertSeverities": ["Extreme", "Severe"]}'
```

### What data you get from the weather.gov API

Every row has the same columns. A column that does not apply to a row is `null`, for example the forecast columns on a state row.

| Group | Fields |
|---|---|
| Row | `recordType` (`location` or `state_alerts`), `query`, `status`, `statusMessage`, `sourceUrl`, `fetchedAt` |
| Place | `latitude`, `longitude`, `place`, `state`, `timeZone`, `forecastOffice`, `gridId`, `gridX`, `gridY`, `forecastZone`, `county`, `radarStation` |
| Forecast | `forecastGeneratedAt`, `forecastUpdatedAt`, `forecastUnits`, `forecastNowName`, `forecastNowSummary`, `forecastNowTemperature`, `forecastNowTemperatureUnit`, `forecastPeriods` (list of periods with name, start, end, temperature, precipitation chance, wind, short and detailed forecast) |
| Latest observation | `observationStationId`, `observationStationName`, `observationStationDistanceKm`, `observationStationsTried`, `observationTime`, `observationAgeMinutes`, `observationDescription`, `temperatureC`, `temperatureF`, `dewpointC`, `relativeHumidityPercent`, `windSpeedKmh`, `windSpeedMph`, `windGustKmh`, `windDirectionDegrees`, `seaLevelPressureHpa`, `stationPressureHpa`, `visibilityKm`, `heatIndexC`, `windChillC`, `precipitationLastHourMm`, `precipitationLast3HoursMm`, `precipitationLast6HoursMm` |
| Active alerts | `alertCount`, `alertMaxSeverity`, `alertCountsBySeverity`, `alertsTruncated`, `alerts` (list with event, severity, urgency, certainty, headline, area, sent, onset, ends, expires, sender, response, description and instruction) |

The latest observation comes from the nearest station that reports a valid temperature. The Actor reads the stations of the grid in distance order, skips a station whose report has no temperature or is older than 6 hours, and tries up to 5 stations. `observationStationsTried` shows how many it needed.

The `status` column says what happened for the row:

| Status | Meaning | Charged |
|---|---|---|
| `ok` | Every part you switched on is in the row | yes |
| `partial` | One part is missing, and `statusMessage` says which and why, for example a marine grid without a forecast | yes |
| `outside_coverage` | The NWS has no data for the point, for example a point in another country or in open ocean | yes |
| `station_not_found` | The NWS knows no station with that ID | yes |
| `area_not_accepted` | The NWS did not accept the area code | yes |
| `source_unavailable` | The NWS did not answer for this location after 3 attempts | yes |

### NWS weather API pricing

Pricing is pay per event. One event is **one saved row** (one location or one state), plus the standard Actor start event.

| Plan | Price per row | Price per 1,000 rows |
|---|---|---|
| Free | $0.0040 | $4.00 |
| Bronze | $0.0036 | $3.60 |
| Silver | $0.0032 | $3.20 |
| Gold | $0.0028 | $2.80 |

Worked example: a weather check of 100 coordinates plus the alerts of 5 states is 105 rows. On the Free plan that is 105 x $0.004 = $0.42, plus $0.00005 for the Actor start, so about $0.42 in total. The same run on the Gold plan costs $0.29.

How charging works:

- A row is charged only after it is saved to the dataset.
- A row that carries a status such as `outside_coverage` or `station_not_found` is charged, because the NWS lookups for it were made.
- A row with the status `source_unavailable` is saved and charged too, because the attempts were made. Three such rows in a row stop the run, so at most a few rows are affected. Run those locations again later.
- Input the Actor rejects before any request (for example a text value that is not a coordinate pair) ends the run with a plain message and charges nothing.
- Repeated entries in the input are removed before the run, so a location is charged once.
- The run stops when the maximum charge you set for the run is reached.

### Input example

```json
{
  "coordinates": ["47.6062,-122.3321", "34.0522,-118.2437"],
  "stationIds": ["KSEA"],
  "alertStates": ["WA"],
  "forecastPeriods": 4,
  "units": "us",
  "alertSeverities": ["Extreme", "Severe"]
}
```

### Output example for a state alert row

A state code returns one row with the active alerts of that state, most severe first. Values were read on 2026-10-04.

```json
{
  "recordType": "state_alerts",
  "query": "TX",
  "status": "ok",
  "state": "TX",
  "alertCount": 3,
  "alertMaxSeverity": "Moderate",
  "alertCountsBySeverity": {
    "Extreme": 0,
    "Severe": 0,
    "Moderate": 2,
    "Minor": 1,
    "Unknown": 0
  },
  "alertsTruncated": false,
  "alerts": [
    {
      "event": "Rip Current Statement",
      "severity": "Moderate",
      "urgency": "Expected",
      "certainty": "Likely",
      "headline": "Rip Current Statement issued October 3 at 9:32AM CDT until October 4 at 9:00AM CDT by NWS Houston/Galveston TX",
      "onset": "2026-10-03T09:32:00-05:00",
      "ends": "2026-10-04T09:00:00-05:00",
      "expires": "2026-10-04T09:00:00-05:00",
      "senderName": "NWS Houston/Galveston TX"
    }
  ],
  "fetchedAt": "2026-10-04T07:45:37.167Z"
}
```

### Use cases for a weather.gov forecast and alerts feed

- Check the forecast, the current conditions and any warning for a list of job sites, stores, venues or fields in one run.
- Watch active Extreme and Severe alerts for the states where you operate, on an hourly schedule.
- Add current temperature, wind and visibility from the nearest working station to a logistics or insurance sheet.
- Give an AI agent one row per place with the forecast, the latest observation and the alerts, so it needs no extra calls.
- Pull the same forecast the NWS publishes for a grid cell and compare it with the observed value later.

### Run it every day

Switch on **Only new since the last run** and the Actor saves a row only for a place that has an alert it has not delivered to you before.

1. Fill the input with your coordinates, station IDs and state codes. Keep **Active alerts** on. Pick the severities you want in **Alert severities**.
2. Switch on **Only new since the last run** and enter a **Monitor name**, for example `job-sites`. Use one name per saved task.
3. Click **Save as task** and run it once. The first run saves a row for every place with an active alert and becomes the baseline.
4. In the Apify Console, open **Schedules**, click **Create new**, pick your Task, and enter a cron expression for once a day, for example `0 6 * * *`. An alert can come and go within a day, so use a shorter schedule, for example every hour, if you need to see short alerts.

```json
{
  "coordinates": ["47.6062,-122.3321", "34.0522,-118.2437"],
  "alertStates": ["WA"],
  "alertSeverities": ["Extreme", "Severe"],
  "onlyNew": true,
  "monitorName": "job-sites"
}
```

How a row looks in this mode: the row has the same fields as before, with the forecast and the latest observation, and `alerts` lists only the new alerts. `alertCount` and `alertCountsBySeverity` count the new alerts only. A place with no new alert gets no row, the Actor does not read its forecast and observation, and it costs nothing. Alerts that were skipped as already delivered are not charged. If a row has more new alerts than **Maximum alerts per row**, the alerts beyond that number are not remembered and come with the next run if they are still active.

The Actor remembers each alert per place, so an alert that covers two of your places is delivered once in each place. An alert whose NWS alert id the monitor has not seen is delivered as new. A place outside the NWS coverage, or an unknown station ID or state code, returns its status row once and is then skipped. If the NWS cannot give the alerts of a place, that place gets no row and no charge, and the run message says how many places could not be checked. `STATS` has the counts: `locationsWithNewAlerts`, `locationsWithoutNewAlerts` and `locationsNotChecked`.

The memory lives in your own key-value store, named `nws-monitor-<monitor name>`. It holds ids only: the place and the NWS alert id. Alerts expire, and the oldest ids are dropped first when the memory grows past 150,000 ids. If you change the alert severities, the Actor starts a separate memory, and its first run saves the active alerts again. You can add and remove places without losing the memory.

Worked cost example on the Free plan, with the prices in the pricing table above ($0.004 per row, $0.00005 for the start). Say you watch 100 coordinates and on a day 4 places get a new alert. That day costs 4 x $0.004 = $0.016 plus $0.00005, so $0.01605. Thirty such days cost 30 x $0.01605 = $0.4815. A day with no new alert costs only the start fee of $0.00005. The same 100 coordinates run daily without Only new return 100 rows every day: 100 x $0.004 = $0.40 plus $0.00005, so $0.40005 a day and 30 x $0.40005 = $12.0015 in 30 days. The 4 places per day is an example, not a measured value.

### Related Actors

- [RSS Feed Reader](https://apify.com/conserving_celerytop/rss-feed-reader) reads news and agency feeds into a table.
- More Actors by the same author are on the author's Apify profile.

### FAQ

#### Is it legal to use the National Weather Service API with this Actor?

The Actor reads the public weather.gov API without a login, a key or a proxy, and it returns weather data only, with no personal data. The NWS says: "All of the information presented via the API is intended to be open data, free to use for any purpose." and "A User Agent is required to identify your application." The Actor sends a User-Agent that names itself. The NWS disclaimer says its information "\[is] in the public domain, unless specifically noted otherwise, and may be used without charge for any lawful purpose" so long as you do not claim it as your own, imply an endorsement or affiliation with NOAA or the NWS, or modify it and present it as official government material. This Actor reformats NWS data into rows, shortens long alert text, and is not official. For decisions that affect safety, read the original warnings at weather.gov. This is not legal advice.

#### Which NWS endpoints does it use?

Only `api.weather.gov`: the points lookup (coordinate to grid), the gridpoint forecast, the stations of a grid, the latest observation of a station, the station record, and active alerts by point or by area. Grid lookups are cached for the run, and a forecast, a station list and an observation are shared by locations in the same grid cell.

#### How fast does it run and how does it treat the NWS rate limit?

The NWS does not publish its rate limit and says it allows "a generous amount for typical use". The Actor keeps to 4 requests per second at most, waits for the time in a `Retry-After` header when the NWS asks for it, and stops with the rows saved so far when the NWS refuses requests (status 403). A run needs about 5 requests per location. A run of 500 locations took about 10 minutes in the test.

#### What are the limits?

- Up to 500 locations (coordinates, station IDs and state codes together) per run. Split larger lists across runs.
- Coverage is the United States, its territories and nearby waters. The NWS has no data for points in other countries.
- The NWS API takes coordinates, not addresses, ZIP codes or place names. Convert them to latitude and longitude first, then add them as coordinates.
- Forecasts for marine grids are not supported by the NWS, so a point on open water can have an observation and alerts with no forecast.
- The forecast has up to 14 periods (7 days). Alert descriptions are cut at 1,500 characters and instructions at 600.
- A row lists up to 200 alerts (50 by default, set by **Maximum alerts per row**), most severe first. `alertCount` always shows the full total.
- The observation is the latest report of the nearest working station, not a reading at the exact coordinates. `observationStationDistanceKm` shows the distance.

#### Why did my run stop early or return fewer rows?

Open the `STATS` record in the key-value store. The `status` field says why: `succeeded`, `succeeded_with_gaps`, `invalid_input`, `rate_limited`, `blocked`, `source_unavailable`, `charge_limit_reached` or `unexpected_error`. The status message on the run page repeats it in a sentence. After `source_unavailable`, `rate_limited` or `blocked`, the rows saved so far are kept, and a new run a few minutes later continues the work.

#### Why does a row have the status `partial`?

One part of the row was missing from the NWS at that moment. Read `statusMessage`. A marine grid has no forecast, and a remote area can have no station with a valid temperature in the last 6 hours.

#### A coordinate returned `outside_coverage`. What now?

Check that latitude comes first and longitude second, and that western longitudes are negative, for example `47.6062,-122.3321`. A point in Canada, Mexico or another country returns this status too.

#### Can I schedule it?

Yes. Use an Apify schedule with the same input. Each run reads the current forecast, observation and alerts.

If this saved you time, a rating helps others find it.

# Actor input Schema

## `coordinates` (type: `array`):

Add latitude and longitude pairs, one per line, for example 47.6062,-122.3321. Use points in the United States, its territories or nearby waters.

## `stationIds` (type: `array`):

Add NWS observation station IDs, one per line, for example KSEA or KDEN. Each ID returns the latest observation of that station with the forecast and alerts for its location.

## `alertStates` (type: `array`):

Add two-letter state, territory or marine area codes, one per line, for example WA, TX or PR. Each code returns one row with every active alert for that area.

## `forecastPeriods` (type: `integer`):

Set how many forecast periods to list for each location, from 1 to 14. A period is a day or a night, so 14 periods cover 7 days.

## `units` (type: `string`):

Choose the units for forecast temperatures and wind. Observation rows carry both Celsius and Fahrenheit.

## `includeForecast` (type: `boolean`):

Turn on to add the forecast periods to each location row.

## `includeObservation` (type: `boolean`):

Turn on to add the latest valid observation from the nearest reporting station.

## `includeAlerts` (type: `boolean`):

Turn on to add the active alerts for each location.

## `alertSeverities` (type: `array`):

Pick the severities to list. The default lists every severity.

## `includeAlertText` (type: `boolean`):

Turn on to add the description and the instruction of each alert.

## `maxAlertsPerRow` (type: `integer`):

Set how many alerts to list in one row, from 1 to 200. The most severe alerts come first and the alert count shows the full total.

## `onlyNew` (type: `boolean`):

Return only items that this monitor has not delivered before. The first run returns everything and becomes the baseline. Use it with a daily schedule.

## `monitorName` (type: `string`):

Names the memory of this monitor. Use one name per saved task, so two tasks never share their memory.

## Actor input object example

```json
{
  "coordinates": [
    "47.6062,-122.3321",
    "34.0522,-118.2437"
  ],
  "alertStates": [
    "WA"
  ],
  "forecastPeriods": 4,
  "units": "us",
  "includeForecast": true,
  "includeObservation": true,
  "includeAlerts": true,
  "includeAlertText": true,
  "maxAlertsPerRow": 50,
  "onlyNew": false,
  "monitorName": "default"
}
```

# Actor output Schema

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

No description

## `stats` (type: `string`):

No description

# 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 = {
    "coordinates": [
        "47.6062,-122.3321",
        "34.0522,-118.2437"
    ],
    "alertStates": [
        "WA"
    ],
    "forecastPeriods": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("conserving_celerytop/nws-weather-forecast-alerts-api").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 = {
    "coordinates": [
        "47.6062,-122.3321",
        "34.0522,-118.2437",
    ],
    "alertStates": ["WA"],
    "forecastPeriods": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("conserving_celerytop/nws-weather-forecast-alerts-api").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 '{
  "coordinates": [
    "47.6062,-122.3321",
    "34.0522,-118.2437"
  ],
  "alertStates": [
    "WA"
  ],
  "forecastPeriods": 4
}' |
apify call conserving_celerytop/nws-weather-forecast-alerts-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,conserving_celerytop/nws-weather-forecast-alerts-api"
        }
    }
}
```

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/HB742hlVWVsPgi4UP/builds/yA802XBaYjIUywVgP/openapi.json
