Global Chokepoints Alerts API
Real-time JSON data on the operational status of the Strait of Hormuz. All /v1 endpoints are public, CORS-open, and licensed under CC-BY-4.0. No API key required for most uses.
Interactive Playground
Try every endpoint live — send real requests, inspect responses, and explore schemas in the browser.
Overview
The API is deployed on Cloudflare Pages Edge Workers. All routes run globally with no cold starts and multi-layer KV caching. The base URL is:
https://strait-of-hormuz-monitor.pages.devRate limits
There are no hard programmatic rate limits today. Cloudflare edge caching means most requests are served from cache within a 30–300 s TTL window; upstream sources are not hit per-request. Fair use is expected — treat each endpoint as a polling interval, not a real-time stream.
Attribution
When displaying data from /v1/* endpoints, include the attribution string from the license field: “Global Chokepoints Alerts” with a link to https://strait-of-hormuz-monitor.pages.dev.
Route families
| Prefix | Purpose | Auth | CORS |
|---|---|---|---|
| /v1/* | Public intelligence API | Optional key | Allow-all |
| /api/* | Internal dashboard data feeds | None | Same-origin |
| /feed.xml | RSS 2.0 event feed | None | Allow-all |
Authentication
GET requests to /v1/* are always public — no key, no header, no signup. CORS preflight (OPTIONS) and HEAD are also unauthenticated.
Authentication only applies to non-GET methods on /v1/* and is enforced only when the deployment's V1_API_KEY environment variable is set. If a key is required, send it in either form:
# Option A — x-api-key header
curl -X POST -H "x-api-key: YOUR_KEY" https://strait-of-hormuz-monitor.pages.dev/v1/...
# Option B — Bearer token
curl -X POST -H "Authorization: Bearer YOUR_KEY" https://strait-of-hormuz-monitor.pages.dev/v1/...Requests without a valid key on a gated method receive 401 Unauthorized. Internal /api/* routes are always same-origin and have no key requirement.
Public v1 API
Three endpoints provide everything needed to build embeds, dashboards, or alerting integrations. All responses include a license field and a docs pointer back to /methodology.
/v1/status30 s cache—Current strait statusThe primary endpoint. Returns the computed operational state (OPEN / PARTIALLY_CLOSED / CLOSED), a 0–100 threat score, confidence value, and a human-readable reason string.
Response fields
| Field | Type | Description |
|---|---|---|
| state | enum | OPEN | PARTIALLY_CLOSED | CLOSED |
| tensionLevel | enum | NORMAL | ELEVATED | CRITICAL |
| tensionIndex | integer | 0–100 composite threat score (see /methodology) |
| confidence | number | 0..1 based on source diversity and event density |
| reason | string | Human-readable explanation of current state |
| reasonUrl | string? | URL of the driving source article, if any |
| brent | object? | { price, change, changePercent } — null if feed is down |
| events24h | integer | Distinct classified events in the past 24 hours |
| asOf | ISO 8601 | Timestamp of this computation |
| sources | string[] | Source names contributing to current state |
Examples
curl https://strait-of-hormuz-monitor.pages.dev/v1/statusconst res = await fetch('https://strait-of-hormuz-monitor.pages.dev/v1/status');
const data = await res.json();
console.log(data.state); // "OPEN"
console.log(data.tensionIndex); // 42
console.log(data.reason); // "Maritime traffic operational. ..."/v1/events60 s cache—Geopolitical event timelineClassified events aggregated from CNN, BBC, Al Jazeera, Reuters (via Google News), and Google News RSS. Events are filtered by Hormuz/Iran/maritime keywords and scored by severity.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max events to return (1–100, default 30) |
| since | query | ISO 8601 | optional | Return only events at or after this timestamp — useful for incremental polling |
Response fields
| Field | Type | Description |
|---|---|---|
| events[].id | string | Unique event identifier (URL-derived hash) |
| events[].date | ISO 8601 | Event publication timestamp |
| events[].title | string | Headline |
| events[].description | string | Lead paragraph or snippet |
| events[].category | enum | incident | military | diplomatic | economic |
| events[].severity | enum | low | medium | high | critical |
| events[].source | string | Publisher name (e.g. "BBC") |
| events[].url | string | Original article URL |
| count | integer | Total events returned |
| generatedAt | ISO 8601 | Response generation time |
Examples
# Latest 10 events
curl "https://strait-of-hormuz-monitor.pages.dev/v1/events?limit=10"
# Incremental poll — events since last check
curl "https://strait-of-hormuz-monitor.pages.dev/v1/events?since=2026-05-14T06:00:00Z"// Incremental polling
let cursor = new Date(Date.now() - 60_000).toISOString();
setInterval(async () => {
const url = `https://strait-of-hormuz-monitor.pages.dev/v1/events?limit=20&since=${cursor}`;
const { events, generatedAt } = await fetch(url).then(r => r.json());
cursor = generatedAt;
events.forEach(e => console.log(e.severity, e.title));
}, 60_000);/v1/metrics60 s cache—Markets, weather and event metricsAggregates Brent crude, WTI, Henry Hub natural gas, marine weather at Bandar Abbas, and 24-hour event delta. Any subsystem that is unreachable returns null rather than failing the whole response.
Response fields
| Field | Type | Description |
|---|---|---|
| markets.brent | Ticker|null | Brent crude (USD/bbl). See Ticker type below. |
| markets.wti | Ticker|null | West Texas Intermediate (USD/bbl) |
| markets.natgas | Ticker|null | Henry Hub natural gas (USD/MMBtu) |
| weather.temperatureC | number | Air temperature at Bandar Abbas approach (°C) |
| weather.wind | object | { speedKn, direction, directionDeg } |
| weather.sea | object | { waveHeightM, wavePeriodS, windWaveM, swellM } |
| weather.navRisk | number | 0–100 navigational risk composite |
| weather.navRiskLabel | enum | CALM | MODERATE | ROUGH | SEVERE |
| events.last24h | integer | Classified events in the past 24 hours |
| events.prev24h | integer | Events in the preceding 24-hour window |
| events.delta | integer | Difference: last24h − prev24h |
Examples
curl https://strait-of-hormuz-monitor.pages.dev/v1/metricsconst { markets, weather, events } = await fetch('https://strait-of-hormuz-monitor.pages.dev/v1/metrics').then(r => r.json());
if (markets?.brent) {
console.log(`Brent: ${markets.brent.price} (${markets.brent.changePercent}%)`);
}
if (weather) {
console.log(`Wind: ${weather.wind.speedKn}kn ${weather.wind.direction} · NavRisk: ${weather.navRiskLabel}`);
}Data Feed Routes
These routes are used by the dashboard internally and are same-origin by default. They are documented here for transparency and local development. Each one implements a multi-source fallback chain — they never return 502 while any cached data exists.
| Route | Sources (priority order) | Cache TTL |
|---|---|---|
| /api/brent | Yahoo Finance → Stooq CSV → EIA → module cache → KV | 5 min |
| /api/markets | EIA (Brent/WTI) + FRED (Henry Hub) → Yahoo Finance | 5 min |
| /api/timeline | 7 RSS feeds (CNN, BBC, Al Jazeera, Google News) → KV | 60 s |
| /api/news | GDELT v2 Doc API → KV fallback | 5 min |
| /api/weather | Open-Meteo Forecast + Marine → KV | 15 min |
| /api/vessels | data/vessels.json (AIS sidecar) → KV fallback | 2 min |
| /api/portwatch | IMF PortWatch → KV | 6 h |
| /api/health | Parallel probes (Yahoo, GDELT, RSS, Open-Meteo) | 30 s |
Subscriptions
Users can subscribe to receive an email when the strait state changes (e.g. OPEN → PARTIALLY_CLOSED). The alert system uses Cloudflare D1 for storage and Resend for transactional email.
/api/subscribe—Register email for status alertsInserts an unconfirmed subscription and sends a confirmation email. Idempotent for unconfirmed addresses — resends the confirmation link.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | string | required | Email address to subscribe | |
| turnstileToken | body | string | optional | Cloudflare Turnstile token — required when TURNSTILE_SECRET_KEY is configured |
Examples
curl -X POST https://strait-of-hormuz-monitor.pages.dev/api/subscribe \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'GET /api/confirm?token=<token> — confirms subscription; redirects to /?subscribed=1
GET /api/unsubscribe?token=<token> — deletes subscription; redirects to /?unsubscribed=1
Types reference
StraitStatus
type StraitStatus =
| 'OPEN' // traffic operating normally
| 'PARTIALLY_CLOSED' // disruption, diversion, or elevated incident signals
| 'CLOSED' // closure keyword detected in recent event headlinesTensionLevel & threat score
type TensionLevel = 'NORMAL' | 'ELEVATED' | 'CRITICAL'
// tensionIndex is a 0–100 composite:
// timeline_score (50%) — severity-weighted events in the last 24 h
// market_score (50%) — Brent Δ% mapped to 0–100 above +2%
// Thresholds
// tensionIndex >= 80 or state === 'CLOSED' → CRITICAL
// tensionIndex >= 40 or state !== 'OPEN' → ELEVATED
// otherwise → NORMALTimelineEvent
interface TimelineEvent {
id: string
date: string // ISO 8601
title: string
description: string
category: 'incident' | 'military' | 'diplomatic' | 'economic'
severity: 'low' | 'medium' | 'high' | 'critical'
source: string // publisher name, e.g. "BBC"
url?: string // original article URL
}Ticker (markets)
interface Ticker {
price: number
change: number
changePercent: number
history: { date: string; price: number }[] // 7-day spark
asOf: string // ISO 8601
label: string // e.g. "Brent Crude"
symbol: string // e.g. "BZ=F"
unit: string // e.g. "USD/bbl"
provider: string
stale?: boolean
error?: string
}OpenAPI specification
A machine-readable OpenAPI 3.0.3 spec is served at /api/openapi. It can be imported into Postman, Insomnia, or any OpenAPI-compatible tool. An interactive Scalar viewer is available at /api-reference.
# Download spec
curl https://strait-of-hormuz-monitor.pages.dev/api/openapi -o openapi.json
# Import into Postman (CLI)
postman import openapi.jsonThe spec is regenerated on each deploy and cached for 1 hour at the edge.