# Strait of Hormuz Tanker Flow Recovery Index (`zinin/strait-of-hormuz-tanker-flow-recovery`) Actor

Compare aggregate commercial-tanker observations across three fixed Strait of Hormuz zones with seven- and thirty-observation baselines. Get deterministic recovery labels, freshness, confidence, gaps, and a human-review action without exposing raw identities or positions.

- **URL**: https://apify.com/zinin/strait-of-hormuz-tanker-flow-recovery.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** MCP servers, Automation, Travel
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.0425 / delivered hormuz recovery report

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Strait of Hormuz Tanker Flow Recovery Index

![Strait of Hormuz Tanker Flow Recovery: baseline, recovery index, evidence coverage, and review](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/8192b1d6e7092f54d1c5e401786e5c0be091e318/commercial115/strait-of-hormuz-tanker-flow-recovery/readme-hero.webp)

![Strait of Hormuz Tanker Flow Recovery: bounded observations to a reviewable recovery assessment](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/8192b1d6e7092f54d1c5e401786e5c0be091e318/commercial115/strait-of-hormuz-tanker-flow-recovery/readme-workflow.webp)

### What this Actor does

This Actor turns repeated commercial-tanker observations in three fixed Strait of Hormuz zones into a small, machine-readable decision report. It compares the current aggregate with seven-observation and thirty-observation baselines and emits deterministic signals:

- `FLOW_COLLAPSE`: current flow is at or below 50% of the selected baseline.
- `RECOVERY_STARTED`: a prior flow ratio was at or below 0.60 and the current ratio reached at least 0.70.
- `RECOVERY_STALLED`: a recovery signal was previously observed but the ratio fell below 0.70.
- `THROUGHPUT_SPIKE`: current flow is at least 1.50 times the selected baseline.
- `DATA_STALE`: source quality is low or the source is partial.

It is an aggregate monitor, not a vessel search product. It does not return vessel names, MMSI, IMO numbers, coordinates, routes, military information, sanctions conclusions or a geopolitical risk score.

### Key features

- Monitors three fixed commercial-tanker zones: western approach, transit corridor and eastern approach.
- Produces deterministic flow-collapse, recovery, stalled-recovery and throughput-spike signals.
- Uses seven-observation and thirty-observation baselines under a same-watch lock.
- Rejects partial, stale, malformed or empty live samples before paid delivery or state advancement.
- Stores only a version-2 minimal private snapshot: HMAC-derived identity, fixed zone, movement class and source/observation timestamps. Exact provider speed and navigation status are used only ephemerally while calculating the aggregate and are never returned, logged or persisted.
- Provides closed schemas for Apify API, Tasks, schedules, MCP clients and automation workflows.

### How to run

1. Run the default `demo` input to inspect one synthetic aggregate Dataset row. The automatic start event still applies, but demo does not charge `result-found`.
2. For live monitoring, select `datalastic` and provide your Datalastic API key in the secret input field.
3. Keep the same `watchId` across scheduled runs so confirmed live reports can build a compatible baseline.
4. Read the aggregate report from the Dataset and the authoritative delivery/state envelope from `OUTPUT`.

### Input

#### Example input

```json
{
  "schemaVersion": "1.0",
  "requestId": "auto",
  "watchId": "hormuz-tanker-flow",
  "sourceMode": "datalastic",
  "datalasticApiKey": "provided as a secret",
  "identitySalt": "optional stable 32+ character secret",
  "maxVessels": 100,
  "maxProviderRequests": 4,
  "maxTotalChargeUsd": 0.055
}
```

`watchId` must remain stable to build history. At the input boundary, externally supplied `requestId` and `watchId` values are replaced by deterministic domain-separated one-way digests before validation, lock naming, output, Dataset, KVS or logging; this keeps repeated scheduled calls stable without retaining a pasted credential. `sourceMode=demo` writes one synthetic Dataset row after the automatic `$0.005` start charge and never charges the `$0.05` `result-found` event. `sourceMode=datalastic` requires the user's own Datalastic API key. The key is sent only in the provider request header and is never written to output, logs or state. `identitySalt` is optional; keep the same 32+ character secret while rotating the provider key. A changed identity salt fails closed as incompatible before paid delivery.

Coordinates are not configurable. The Actor uses only the approved western approach, transit corridor and eastern approach zones. Provider usage is capped at exactly four actual attempts/pages: one Datalastic `/stat` validation attempt plus one bounded page for each fixed zone. `maxVessels` is a global cap across all three zones; a response that needs more rows or pagination is diagnostic and ineligible for paid delivery.

### Output

The Actor writes an authoritative envelope to `OUTPUT`. A useful live run, including a bootstrap report, writes exactly one report with `result-found`. Demo writes one free Dataset row after the automatic start charge and does not charge `result-found`. A budget-blocked run is uncharged; its terminal `UNCHARGED` intent replays the same result without pricing, provider, Dataset, charge or state effects. A source error is uncharged; an ambiguous delivery may already have charged `result-found`, is reported as unknown, is not retried, and does not advance state. The delivery block never exposes a provider receipt identifier: it contains only fixed delivery counts and a safe `replay-sha256:...` identifier derived from a versioned one-way receipt digest.

The public report contains only aggregates:

#### Example output

```json
{
  "schemaVersion": "1.0",
  "actor": "strait-of-hormuz-tanker-flow-recovery",
  "reportId": "sha256:bb8beb4bfeb750d16a517f0499650a931216ca0a0ba92052750c0e12f7c73019",
  "watchId": "hormuz-tanker-flow",
  "region": "strait-of-hormuz",
  "status": "bootstrap",
  "observedAt": "2026-08-08T00:00:00.000Z",
  "metrics": {
    "tankerCount": 3,
    "zoneCounts": { "western_approach": 1, "transit_corridor": 1, "eastern_approach": 1 },
    "movement": { "moving": 2, "slow": 0, "stopped": 1, "unknown": 0 },
    "transitions": { "entries": null, "exits": null, "crossZoneTransitions": null, "nullReasons": { "entries": "no_prior_observation", "exits": "no_prior_observation", "crossZoneTransitions": "no_prior_observation" } }
  },
  "baseline": {
    "observationCount": 0,
    "minimumObservations": 7,
    "sevenObservationMean": null,
    "thirtyObservationMean": null,
    "selectedWindow": null,
    "ratio": null,
    "percentDelta": null,
    "status": "bootstrap",
    "previousReportId": null,
    "coverage": { "observations": 0, "minimum": 7, "preferred": 30 },
    "nullReasons": { "sevenObservationMean": "insufficient_observations", "thirtyObservationMean": "insufficient_observations", "selectedWindow": "insufficient_observations", "ratio": "insufficient_observations", "percentDelta": "insufficient_observations" }
  },
  "signals": [],
  "confidence": "high",
  "dataQuality": { "sourceOk": true, "partial": false, "freshnessSeconds": 0, "sourceFreshnessSeconds": 0, "sampleCount": 3, "freshnessKnownCount": 3, "freshnessCoverage": 1, "expectedCount": 3, "score": 100, "confidence": "high", "coverage": { "sample": 3, "expected": 3, "freshness": 3 }, "nullReasons": { "freshnessSeconds": null, "sourceFreshnessSeconds": null, "expectedCount": null }, "limitations": ["Complete paid delivery requires all fixed zones, a non-empty aggregate and 100% timestamp coverage."] },
  "sources": [{ "mode": "demo", "provider": "synthetic_fixture", "endpoint": "synthetic_fixture", "calls": 0, "license": "synthetic", "retrievedAt": "2026-08-08T00:00:00.000Z" }],
  "limitations": ["Aggregated commercial-tanker observations only.", "No military, sanctions, ownership, route or tactical conclusions.", "A null baseline metric always carries a closed reason in baseline.nullReasons."],
  "actions": [{ "actionId": "sha256:801d216247d7d6e0e83d902e41bb1fe3755bf304ca12b2cd6bf341626dcf96ab", "review_required": true, "not_sent": true, "instruction": "Review the evidence and source freshness before making an operational decision." }]
}
```

The first seven observations are honest `bootstrap` reports. On the first observation, transition counts are `null` with `no_prior_observation`; they become numeric only after a prior delivered snapshot exists. The seven-observation baseline becomes available once seven prior observations exist, so the next run can be compared against it; the thirty-observation baseline is preferred after thirty prior observations. Hashed identities and snapshots are private state only.

### Pricing

The Actor uses pay-per-event pricing:

| Event | Price |
| --- | ---: |
| `apify-actor-start` | `$0.005` |
| `result-found` | `$0.05` per delivered report |
| Default dataset item | `$0` |

Discount tiers are `0%`, `5%`, `10%`, `15%`, `18%` and `20%`. At the base tier, one useful live report costs `$0.055` including the `$0.005` start event; provider BYOK costs are separate. The Actor checks `maxTotalChargeUsd` before the provider call and before delivery. An ambiguous delivery may already have charged `result-found`; its charge and delivery status are reported as unknown and it is never retried automatically.

### Data quality

Every report includes provider mode, retrieval time, sample count, timestamp coverage, maximum source freshness and a confidence level. Every source `retrievedAt` is required to be no later than the report's `observedAt`, including persisted intent and replay validation. A live sample is eligible only when all three fixed zone responses are complete, every provider row is structurally valid, every accepted tanker has a valid source timestamp, the maximum freshness is at most 10,800 seconds, timestamp coverage is 100%, and the aggregate is nonempty. Valid circle-edge rows outside the Actor's stricter internal aggregate geofences are intentionally excluded and are not treated as malformed. Unknown timestamps, future timestamps beyond 300 seconds of clock skew, stale rows, mixed-zone failures, malformed rows and zero accepted samples produce a diagnostic `partial` report in `OUTPUT`; they do not emit `result-found` and do not advance state.

All public errors use a closed error code and an allowlisted `stage:*` label. Backend exception prose is never copied to `OUTPUT`, KVS, or logs, including when a provider error contains an MMSI or an old API key. State and delivery-intent records are recursively validated before every write and replay; state version 1 is incompatible and requires a new monitoring series or explicit migration.

Baselines advance only after a confirmed paid delivery and compatible state save. State includes an exact contract for Actor ID, schema version, fixed-zone version and a non-reversible source-mode plus identity-epoch digest. A same-watch Request Queue lock prevents overlapping updates for the same `watchId`; backend errors are terminal and are never reported as normal contention. Every nullable baseline decision metric carries a closed `baseline.nullReasons` entry and its observation coverage. The private intent ledger uses a versioned closed receipt and a receipt-bound intent digest; it rejects poisoned replay records before any source call, charge, Dataset write, state write or public output.

Datalastic is an optional BYOK source. Users are responsible for their provider plan and permitted use of provider data. This Actor publishes derived aggregates, not raw AIS records.

### Limitations

- This is a commercial-tanker aggregate, not a complete maritime picture.
- A seven-observation baseline is directional; thirty observations are preferred.
- It does not predict future events. This report is not for navigation and does not provide tactical guidance.
- It does not identify vessels or infer military activity, sanctions status, ownership or intent.
- `sourceMode=demo` is synthetic and must not be used for operational decisions.
- The Actor does not scrape Panama Canal Authority, PortWatch or other restricted dashboards.

### What the Actor gives you

The product answers one deliberately narrow question: **how does the current aggregate commercial-tanker observation compare with the same watch's accepted history?** The answer is an evidence record with a baseline ratio, deterministic recovery labels, source coverage, confidence, limitations, and a human-review action.

Use it to monitor an aggregate series, create a repeatable review trigger, or attach flow context to a separate commercial analysis. It is especially useful when a team needs to distinguish “we observed more tankers than in the recent accepted baseline” from stronger claims that the Actor cannot support.

It is not a real-time navigation product, an intelligence feed, a sanctions screen, a military tracker, an ownership database, or a prediction of supply, price, safety, or geopolitical events. It intentionally withholds raw identities and positions and never recommends a tactical route.

### Analytical model

The live model observes three fixed areas: western approach, transit corridor, and eastern approach. Every accepted identity belongs to one aggregate snapshot. The public report exposes zone totals, movement-class totals, and—after a compatible prior observation—aggregate entries, exits, and cross-zone transitions.

The baseline is observation-based, not calendar-based. Seven accepted prior observations unlock the first comparison window; thirty accepted prior observations unlock the preferred window. If a schedule runs irregularly, the baseline reflects those accepted observation times rather than evenly spaced days. Review `observedAt`, history coverage, and your Task schedule before interpreting a ratio as a time trend.

| Field | Interpretation | Boundary |
| --- | --- | --- |
| `metrics.tankerCount` | Safe unique commercial-tanker identities in the fixed aggregate zones. | Not complete regional traffic or an official transit count. |
| `metrics.zoneCounts` | Aggregate distribution across the three fixed zones. | Not individual routes or intended direction. |
| `metrics.movement` | Deterministic speed-based classes used in the accepted snapshot. | Not navigation status, safety, or intent. |
| `metrics.transitions` | Aggregate changes against one compatible prior delivered snapshot. | Null on the first observation; not a voyage history. |
| `baseline.ratio` | Current aggregate divided by the selected accepted-history mean. | Not a forecast or causal estimate. |
| `confidence` | Contract-owned evidence-quality class. | Not probability that a market outcome will happen. |

#### Recovery labels

- `FLOW_COLLAPSE` means the current aggregate is no more than half of the selected baseline.
- `RECOVERY_STARTED` means a prior ratio was no more than 0.60 and the current ratio reached at least 0.70.
- `RECOVERY_STALLED` means a prior recovery signal existed but the current ratio fell below 0.70.
- `THROUGHPUT_SPIKE` means the current aggregate is at least 1.50 times the selected baseline.
- `DATA_STALE` means the evidence contract failed and paid delivery is withheld.

These labels describe transitions within this Actor's accepted series. “Recovery” does not mean that every commercial, operational, environmental, or security condition recovered.

### Review workflow

1. Confirm the platform run and exact build identity.
2. Read `status`, `dataQuality.sourceOk`, `dataQuality.partial`, freshness, and coverage before any signal.
3. Check whether the report is still `bootstrap` and whether the seven- or thirty-observation window is selected.
4. Preserve every null value with its `nullReasons` entry.
5. Compare zone distribution and movement classes with the total ratio.
6. Inspect `sources`, `limitations`, and the explicit review action.
7. Add independently authorized commercial context; do not infer it from missing fields.
8. Record the reviewer, the decision, and the evidence identifiers outside the Actor.

The generated `actions` item always requires review and is marked `not_sent: true`. The Actor does not alert an external recipient or execute the action. This makes the output suitable for a controlled queue in which another system can request review without pretending the review already happened.

### Source completeness and freshness

A live report can be useful only when all three fixed zone calls are complete, all raw rows needed for aggregation are structurally valid, at least one safe tanker is accepted, source timestamp coverage is 100%, no accepted timestamp is more than five minutes in the future, and maximum freshness is no more than 10,800 seconds.

Structurally valid circle-edge rows outside the Actor's stricter internal geofence may be excluded. Provider count and accepted count can therefore differ without implying malformed data. The report exposes coverage so that this distinction is reviewable.

Missing timestamps, partial pages, pagination requirements beyond the one bounded page per zone, malformed rows, mixed success/error zones, empty accepted aggregates, stale evidence, and future timestamps are fail-closed. They produce diagnostic `OUTPUT`, no useful paid Dataset row, and no baseline advancement.

### Billing and settlement

The Dataset is the aggregate report channel. `OUTPUT` is the authoritative run, delivery, and state channel. A useful live report—including a bootstrap—uses one linked `result-found` push. Demo uses an ordinary unpriced Dataset push and still purchases the automatic start event.

| Terminal fact | Dataset fact | Billing fact | Baseline fact |
| --- | --- | --- | --- |
| Demo success | One synthetic row | No result event | No live baseline advance |
| Confirmed live result | One aggregate row | Exactly one result event | Save attempted only after confirmation |
| Source/quality refusal | No useful paid row | Result not attempted | Not advanced |
| Definitive budget refusal | No useful paid row | Confirmed zero result | Not advanced; refusal is replayable |
| Ambiguous push or count | Row may exist | Settlement unknown | Not advanced |
| State write failure after delivery | Paid row remains known | One result remains known | Persistence failed/unknown |
| Lock release uncertainty | Preserves known row | Preserves known settlement | Preserves known save; lock is uncertain |

Do not blindly rerun an ambiguous, state-write, or lock-release outcome. Inspect the existing platform run, Dataset, KVS `OUTPUT`, and charged-event counts. The no-retry rule prevents compounding an unknown result, but a manually created new run is still a new start purchase and can become a new result purchase.

### API, Tasks, schedules, and MCP

Demo is the safest integration test:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/zinin~strait-of-hormuz-tanker-flow-recovery/runs?token=$APIFY_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "schemaVersion":"1.0",
    "requestId":"auto",
    "watchId":"hormuz-tanker-flow",
    "sourceMode":"demo",
    "maxVessels":100,
    "maxProviderRequests":4,
    "maxTotalChargeUsd":0.055
  }'
```

For a live Task, change `sourceMode` to `datalastic` and store `datalasticApiKey` in the secret input field. Keep `watchId` stable for one series. If you use `identitySalt`, keep it stable across provider-key rotations. A changed identity epoch intentionally fails closed because repeated-observation continuity can no longer be proven.

Schedules should not overlap for the same watch. The lock is a safety net, not a scheduling strategy. Choose an interval that fits your provider plan, source freshness needs, and desired observation-based baseline.

An MCP or automation client should wait for the Apify run to become terminal and then validate `OUTPUT`, the Dataset report, `reportId`, source/quality fields, and platform charged-event counts. It should route partial or uncertain outcomes to manual review and must not translate `actions` into autonomous navigation or operational commands.

### Privacy, source rights, and retention

Datalastic mode uses a buyer-owned provider key. You are responsible for a plan and permissions that cover API access, transformation, retention, and downstream use. The Actor reduces exposure by publishing aggregates only and by keeping raw source identity, position, route, provider prose, speed, and navigation status out of public storage.

Private state uses HMAC-derived identity values only for bounded continuity. Cryptographic pseudonyms are a security and minimization control; they are not a universal legal determination that data is anonymous. Do not attempt to reverse, enrich, or join the aggregates to identify vessels, owners, crews, cargo, sanctions exposure, or military activity.

Define a legitimate purpose, authorize users, and set retention for Tasks, named state stores, request ledgers, Datasets, KVS records, webhooks, and exports. Delete obsolete watches and downstream copies. Never place the provider key or identity salt in `requestId`, `watchId`, a URL, a log, or a report field.

### Production checklist

Before a live schedule:

- verify demo integration and zero result-event behavior;
- confirm current tier pricing and a cap that covers start plus one result;
- verify the provider account and downstream-use rights;
- choose a stable watch and, when needed, a stable identity salt;
- document the observation cadence and how it affects the baseline;
- preserve null/reason pairs and source coverage in downstream storage;
- prohibit automated retries for ambiguous or post-delivery failures;
- assign a reviewer for recovery, collapse, spike, stale, and partial states;
- retain run ID, build ID, report ID, Dataset ID, KVS ID, and event counts;
- define deletion and state-migration procedures.

### Troubleshooting

| Symptom | Likely cause | Next step |
| --- | --- | --- |
| `bootstrap` | Fewer than seven accepted prior observations. | Continue the same compatible watch; do not invent a baseline. |
| Null transitions | No compatible prior delivered snapshot. | Preserve `no_prior_observation`; collect the next accepted run. |
| `DATA_STALE` | Missing/old/future timestamps, partial zone, malformed row, or empty aggregate. | Inspect quality coverage and closed errors; do not read zero as recovery. |
| `watch_busy` | Another same-watch run owns the lock. | Wait for settlement and remove schedule overlap. |
| `request_replay_blocked` | Intent or state cannot be safely authenticated/reconciled. | Investigate the original run and storage before a new request. |
| Incompatible state | Source mode, fixed zones, schema, or identity epoch differs. | Start a reviewed new series or perform an explicit migration. |
| Budget refusal | Cap cannot fund the current start plus result price. | Review tier and cap; the refused result remains uncharged. |

### Acceptance criteria

Treat a live output as an accepted report only when the exact candidate/build is known, the platform run is terminal and successful, `OUTPUT` is present and schema-valid, the Dataset contains the same one report, `reportId` recomputes, all three zones and timestamp coverage are complete, evidence is fresh, `dataQuality.partial` is false, the source contains no forbidden raw identity fields, and the platform counters show one start plus exactly one new `result-found` event. Demo acceptance instead requires one synthetic row, zero result events, and no baseline advance.

### Related Actors

| Actor | Why use it in the same workflow |
| --- | --- |
| [Singapore Bunker & Tanker Divergence](https://apify.com/zinin/singapore-bunker-tanker-divergence) | Compare Hormuz flow recovery with Singapore bunker demand and tanker pressure. |
| [Panama Canal Queue & Transit Imbalance](https://apify.com/zinin/panama-canal-queue-transit-imbalance) | Add a second global shipping bottleneck to the same monitoring dashboard. |
| [Live Price Oracle](https://apify.com/zinin/live-price-oracle) | Attach current commodity or freight-price context to a flow signal. |
| [Counterparty Risk Rollup](https://apify.com/zinin/counterparty-risk-rollup) | Add structured counterparty context before a human reviews a commercial decision. |

### FAQ

#### Why did a live run return no paid Dataset row?

The Actor refuses paid delivery when any zone is partial, a provider error is present, source timestamp coverage is incomplete, the newest accepted observation is older than three hours, rows are malformed, or the aggregate is empty. Check `OUTPUT.errors` and the diagnostic report's `dataQuality` fields.

#### Why is an older baseline rejected as incompatible?

The state contract prevents observations collected under a different Actor schema, zone definition or source mode from being mixed silently. Use a new `watchId` when intentionally starting a new monitoring series.

#### Is the Datalastic key stored with vessel identities?

No. The provider key is used only for the Datalastic request. The optional identity salt is used only in memory for HMAC identity derivation and its non-reversible epoch fingerprint; neither secret is returned or persisted. A provider-key rotation with the same identity salt preserves identities. A salt change requires a new `watchId` or explicit state migration.

#### What happens if lock release fails after a successful paid report?

The known delivery, billing count and saved baseline remain truthful. `OUTPUT.delivery.lockRelease` records the separate release uncertainty so the next run can be investigated without rewriting confirmed payment as unknown.

### MCP and automation

The closed input/output schemas are designed for MCP clients and scheduled workflows. A machine can call the Actor with a stable `watchId`, inspect `status`, `signals`, `dataQuality` and `baseline`, and decide whether human review is required. This Actor never sends external alerts or executes an operational action by itself.

# Actor input Schema

## `schemaVersion` (type: `string`):

Contract version for this Actor input.

## `requestId` (type: `string`):

Use auto for a trusted run-derived identifier or provide an explicit idempotency value.

## `watchId` (type: `string`):

Stable lowercase identifier for one monitoring series.

## `sourceMode` (type: `string`):

Demo is synthetic, includes no $0.05 result-found fee, and still incurs the automatic start charge. Datalastic uses your own API key.

## `datalasticApiKey` (type: `string`):

Required only for live Datalastic mode. Runtime validation requires a non-empty secret, and the value is never returned in output.

## `identitySalt` (type: `string`):

Optional stable secret for identity continuity across Datalastic API-key rotation. Runtime validation requires 32-256 characters; changing it starts an incompatible state epoch.

## `maxVessels` (type: `integer`):

Maximum bounded provider rows accepted for each fixed zone.

## `maxProviderRequests` (type: `integer`):

Exactly four actual provider attempts/pages: one /stat and one bounded page per fixed zone.

## `maxTotalChargeUsd` (type: `number`):

Buyer-side cap checked before the paid report event.

## Actor input object example

```json
{
  "schemaVersion": "1.0",
  "requestId": "auto",
  "watchId": "hormuz-tanker-flow",
  "sourceMode": "demo",
  "maxVessels": 100,
  "maxProviderRequests": 4,
  "maxTotalChargeUsd": 0.055
}
```

# Actor output Schema

## `OUTPUT` (type: `string`):

API link to the terminal status and delivery receipt.

## `REPORTS` (type: `string`):

API link to confirmed aggregate reports or the uncharged synthetic demo row.

# 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 = {
    "schemaVersion": "1.0",
    "requestId": "auto",
    "watchId": "hormuz-tanker-flow",
    "sourceMode": "demo",
    "maxVessels": 100,
    "maxProviderRequests": 4,
    "maxTotalChargeUsd": 0.055
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/strait-of-hormuz-tanker-flow-recovery").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 = {
    "schemaVersion": "1.0",
    "requestId": "auto",
    "watchId": "hormuz-tanker-flow",
    "sourceMode": "demo",
    "maxVessels": 100,
    "maxProviderRequests": 4,
    "maxTotalChargeUsd": 0.055,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/strait-of-hormuz-tanker-flow-recovery").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 '{
  "schemaVersion": "1.0",
  "requestId": "auto",
  "watchId": "hormuz-tanker-flow",
  "sourceMode": "demo",
  "maxVessels": 100,
  "maxProviderRequests": 4,
  "maxTotalChargeUsd": 0.055
}' |
apify call zinin/strait-of-hormuz-tanker-flow-recovery --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/strait-of-hormuz-tanker-flow-recovery"
        }
    }
}

```

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/WoU00jEoXyrI6RPoi/builds/uIzLOkvjJcEP6dExO/openapi.json
