# NOAA Tide Predictions: High Low UTC Day Bundles (`thatmike1/us-noaa-tide-high-low`) Actor

Get official high and low tide predictions for known NOAA coastal stations, in metres relative to MLLW with UTC times and date-bundle pricing.

- **URL**: https://apify.com/thatmike1/us-noaa-tide-high-low.md
- **Developed by:** [Michal Pšenčík](https://apify.com/thatmike1) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## NOAA Tide Predictions: High Low UTC Day Bundles

Get NOAA's official high and low tide predictions for coastal stations you already know, as one bundle per
station and UTC day. Every bundle holds the high (`H`) and low (`L`) tide times in UTC and the predicted heights
in metres relative to the station's Mean Lower Low Water (MLLW), exactly as NOAA wrote them, plus the source URL
and a SHA-256 of the raw response. Useful for refreshing a station's coming week into a tide calendar, a
photography or fieldwork planner or a scheduling tool, without maintaining API query parameters and units.

Data comes from the NOAA CO-OPS [Data API](https://api.tidesandcurrents.noaa.gov/api/prod/) (keyless, public).
Tide predictions are computed astronomical tides, **not measured water levels**: they do not include storm
surge, wind or atmospheric effects. **Not for navigation or safety decisions.** See NOAA's
[Tides and Currents](https://tidesandcurrents.noaa.gov/) site for official products, disclaimers and
observed water levels.

### Input

```json
{
    "stationIds": ["8518750"],
    "startDate": "2026-10-22",
    "endDate": "2026-10-23",
    "maxItems": 70
}
```

- `stationIds`: 1 to 10 seven-digit NOAA CO-OPS station IDs, for example `8518750` (The Battery, New York) or
  `9414290` (San Francisco). IDs only, not URLs. Leading zeros are kept; duplicates are removed and never cost
  extra. Default: `8518750`. Find station IDs on [tidesandcurrents.noaa.gov](https://tidesandcurrents.noaa.gov/);
  the actor never searches for stations.
- `startDate`: first UTC day, `YYYY-MM-DD`, from 2020-01-01 to 366 days after the run date. Leave it out for the
  run's UTC date, resolved once when the run starts.
- `endDate`: last UTC day, inclusive. The range is 1 to 7 days. Leave it out for the day after `startDate`.
- `maxItems`: the most bundles to store (1 to 70, default 70), in station order then date order.

Datum (MLLW), units (metres), time zone (GMT/UTC) and interval (high/low) are fixed; input fields for them are
rejected rather than silently ignored. Not every station or date has predictions: those come back as free errors
or missing days.

### Output

One dataset item per station and UTC day with at least one returned high or low tide:

```json
{
    "schemaVersion": 1,
    "recordId": "noaa-tide-day:8518750:MLLW:gmt:metric:2026-10-22",
    "deliveryId": "ef69cf30be10316b56f3aaaee505859351f959547e9fff0f3d240b6e7909e36f",
    "observedAt": "2026-10-22T21:36:22.509Z",
    "contentHash": "05649d8310f3abb8782481b49e07b0efbc711fa24cc92b6425a9ff70654e503c",
    "provenance": [
        {
            "url": "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter?product=predictions&application=PublicDataSpecResearch&begin_date=20261022&end_date=20261023&datum=MLLW&station=8518750&time_zone=gmt&units=metric&interval=hilo&format=json",
            "sha256": "f7b98e976eb8067087ae90d1e79a395cba099bcbaf40ef6d29fedc026288971c"
        }
    ],
    "stationId": "8518750",
    "date": "2026-10-22",
    "datum": "MLLW",
    "heightUnit": "m",
    "timeZone": "UTC",
    "kind": "prediction_not_observation",
    "coverage": "source_returned_high_low_predictions",
    "predictions": [
        { "dateTime": "2026-10-22T03:49:00Z", "heightMeters": "0.21", "tideType": "L" },
        { "dateTime": "2026-10-22T09:55:00Z", "heightMeters": "1.382", "tideType": "H" },
        { "dateTime": "2026-10-22T16:10:00Z", "heightMeters": "0.288", "tideType": "L" },
        { "dateTime": "2026-10-22T22:07:00Z", "heightMeters": "1.451", "tideType": "H" }
    ]
}
```

What the fields mean, and what they do not:

- `predictions` lists the events NOAA returned for that UTC day, sorted by time. Days are UTC calendar days,
  not the station's local days, so an evening local tide can fall on the next UTC date.
- `heightMeters` is a decimal string exactly as NOAA wrote it (precision, zero and sign kept, never rounded). It
  is metres above or below this station's MLLW, not sea level, and not comparable across stations.
- `coverage` means the source returned these events. The actor does not check the expected number of tides.
  A requested day with no returned event is not stored: it is listed as a missing day in the run summary,
  never as a zero or "no tide" row.
- `recordId` is the same for a station and day in every run, so you can upsert on it. `contentHash` hashes only
  the station-day content, so a later NOAA model correction changes it. `deliveryId` identifies this run's
  delivery. `observedAt` is when NOAA's response finished; `provenance` is the exact request with a hash of the
  raw response.

Export as JSON, CSV or Excel from the dataset tab.

### Pricing

Pay per event: **$0.0005 per station-day bundle stored** plus **$0.00005 per run start**. The default run
(one station, two days) costs $0.00105 in events; one bundle $0.00055; ten $0.00505. Re-running the same
station and day bills it again: there is no change detection. Platform usage is billed on top, as shown on the
pricing tab.

Free (no item event): missing days, stations with no predictions, invalid stations, source errors, capped work
and the run summary. Set a spending limit and the actor stops before a bundle the limit cannot cover.

### Run summary and limits

Every run writes a free `SUMMARY` record to the default key-value store: the resolved input (including default
dates), the run status (`complete`, `partial`, `failed` or `capped`), one outcome per station (`delivered`,
`empty`, `error` or `not_attempted`) with its error code, HTTP status and warnings such as `missing_day: <date>`,
request and byte counts, and `firstUndeliveredKey`, the first station (`8518750`) or station day
(`8518750:2026-10-23`) left unstored when a cap or failure stopped the run, so a manual rerun can start there.

- One request per station, sequential, with at least 1.2 seconds between requests.
- More than 64 events in a response, or more than 8 on one UTC day, fails that station as `unit_limit` instead
  of being cut short. Conflicting events at the same time fail it as `identity_conflict`.
- A 403, 429 or block page stops all source requests for the rest of the run; 5xx and timeouts are retried
  twice. NOAA error messages (including HTTP 200 error envelopes) are free errors, never empty forecasts. An
  "invalid station" message from NOAA can also mean a NOAA system error, so it is not proof a station is gone.
- Work stops after 300 seconds, 100 requests or 16 MiB of responses; anything not reached is `not_attempted`.

### Support

Open an issue on the actor's Issues tab with the run ID. Source documentation:
[NOAA CO-OPS Data API reference](https://api.tidesandcurrents.noaa.gov/api/prod/).

# Actor input Schema

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

Seven-digit NOAA CO-OPS tide station IDs, for example 8518750 (The Battery, New York). IDs only, never URLs. 1 to 10; duplicates are removed and cost nothing extra.

## `startDate` (type: `string`):

First UTC calendar day, YYYY-MM-DD, from 2020-01-01 to 366 days after the run date. Leave empty for the run's UTC date, resolved once when the run starts.

## `endDate` (type: `string`):

Last UTC calendar day, inclusive, at most 6 days after the start date (7 days in total). Leave empty for the day after the start date.

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

Most station-day bundles to store, in station order then date order. Bundles past this limit are not stored or charged; the SUMMARY names the first one left.

## Actor input object example

```json
{
  "stationIds": [
    "8518750"
  ],
  "maxItems": 70
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `summary` (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 = {
    "stationIds": [
        "8518750"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thatmike1/us-noaa-tide-high-low").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 = { "stationIds": ["8518750"] }

# Run the Actor and wait for it to finish
run = client.actor("thatmike1/us-noaa-tide-high-low").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 '{
  "stationIds": [
    "8518750"
  ]
}' |
apify call thatmike1/us-noaa-tide-high-low --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thatmike1/us-noaa-tide-high-low"
        }
    }
}
```

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/g0t1602Bj7c0GYYjy/builds/xWUGQu9OuB3pfPbUo/openapi.json
