You need to programmatically detect cyclone-related weather disruptions for a given seaport and turn that into a reliable signal inside your freight or visibility workflow. By the end of this guide, you will call the FreightPulse “Cyclone Disruptions” lookup from PHP, parse the envelope, handle auth, and ship a production-safe integration that respects caching and data freshness constraints (e.g., port congestion lag). You will also get ready-to-copy code, cURL, and response examples.
Scope and outcome
This article focuses exclusively on Weather disruptions—specifically the “Cyclone Disruptions” lookup for a given port code. All examples, endpoints, and fields in this post come from FreightPulse and are scoped to weather hazards. You will:
- Make a weather disruptions request filtered to cyclone signals for a target port (e.g., USLAX).
- Authenticate with the X-API-Key header and adhere to the documented envelope.
- Consume responses with a stable success/data envelope and a request_id for observability.
- Design caching around well-known lags (e.g., IMF PortWatch 6–7 day lag for port congestion; hazards are sourced from GDACS/USGS/NOAA).
Authentication, quotas, and the response envelope
FreightPulse uses header authentication and a simple JSON envelope you can depend on across weather categories. Keep these facts in mind as you wire your client:
- Auth: header X-API-Key: YOUR_API_KEY. Guest without key: 100 requests/day per IP.
- Envelope: {"success":true,"data":{...}}.
- Natural hazards only: GDACS/USGS/NOAA. Do not conflate with labor strikes or canal closures when you examine or display “disruptions.”
- Congestion context: FreightPulse port congestion originates from IMF PortWatch and is lagged ~6–7 days (7-day average vs 90-day baseline) — it is not live AIS-based wait times.
Important: Do not quote a freight-rates USD fare as today’s price from this endpoint. Cyclone disruptions are weather signals, not live rate quotes.
Weather disruptions endpoint for cyclone signals
The cyclone-focused lookup uses a simple query. Replace the port parameter with your target UN/LOCODE or supported port code. Keep the auth header intact, and preserve the type filter to cyclone.
Official cURL (copy-paste):
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/disruptions?port=USLAX&type=cyclone"
Notes you should implement in your client:
- Use HTTPS. Do not log YOUR_API_KEY in plain text or ship it to client-side code.
- Set a conservative timeout (e.g., 10–20s) and a retry with jitter for transient 5xx only.
- Cache successful responses for an interval consistent with your workflow. For hazards, treat them as near-real or event-driven; for congestion context in your downstream logic, remember the lag (6–7 days).
PHP implementation: step-by-step
This is a PHP-first integration, with explicit choices around timeouts, headers, and JSON parsing. The example uses cURL, which is standard in most PHP environments.
1) Build a small client function
Use a dedicated function to encapsulate headers, query, and error handling. Keep the endpoint and the required query parameters explicit to avoid category drift.
<?php
function fetchCycloneDisruptions(string $apiKey, string $portCode): array {
$url = 'https://freightpulsehq.com/api/v1/disruptions?port=' . urlencode($portCode) . '&type=cyclone';
$ch = curl_init($url);
$headers = [
'Accept: application/json',
'X-API-Key: ' . $apiKey
];
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 20
]);
$raw = curl_exec($ch);
$errNo = curl_errno($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errNo !== 0) {
throw new RuntimeException('Network error (cURL errno=' . $errNo . ')');
}
if ($status < 200 || $status >= 300) {
throw new RuntimeException('HTTP error status=' . $status);
}
$json = json_decode($raw, true);
if (!is_array($json)) {
throw new RuntimeException('Invalid JSON');
}
if (!array_key_exists('success', $json) || $json['success'] !== true) {
// Envelope is defined as {"success":true,"data":{...}}; treat otherwise as an error.
throw new RuntimeException('API indicated unsuccessful response');
}
if (!array_key_exists('data', $json) || !is_array($json['data'])) {
throw new RuntimeException('Missing data envelope');
}
return $json; // Keep meta/request_id for observability if present.
}
// Example usage:
try {
$apiKey = 'YOUR_API_KEY';
$port = 'USLAX';
$response = fetchCycloneDisruptions($apiKey, $port);
// Access the envelope:
$success = $response['success'];
$data = $response['data'];
$meta = isset($response['meta']) ? $response['meta'] : null;
// Downstream: inspect $data according to your weather disruption fields.
// Always guard against fields being absent or null as schemas can evolve.
if ($meta && isset($meta['request_id'])) {
error_log('FreightPulse request_id=' . $meta['request_id']);
}
// Your business logic here (e.g., store $data for the port & show signals in UI).
print_r($data);
} catch (Throwable $e) {
// Production: map to your error handling infra (e.g., Sentry, Honeycomb, CloudWatch).
error_log('FreightPulse cyclone disruptions error: ' . $e->getMessage());
}
2) Time and timezone handling
When you work with weather disruptions or related metadata, treat timestamps as ISO-8601 with explicit offsets (e.g., +00:00 for UTC). Store them as UTC internally to keep comparisons and SLA windows predictable across regions.
3) Caching policy
- Weather disruptions: prioritize freshness. Apply short TTLs or event-triggered invalidation if your system subscribes to updates.
- Port congestion used in your downstream logic: assume IMF PortWatch lag (~6–7 days; 7-day average vs 90-day baseline). Do not call these “live AIS wait times.”
Python quickstart (optional helper)
If your team also uses Python for data processing, here is a lightweight example that mirrors the same endpoint and envelope handling. Keep the header name and query string unchanged.
import os
import requests
def fetch_cyclone_disruptions(api_key: str, port_code: str) -> dict:
url = "https://freightpulsehq.com/api/v1/disruptions"
params = {"port": port_code, "type": "cyclone"}
headers = {"X-API-Key": api_key, "Accept": "application/json"}
r = requests.get(url, headers=headers, params=params, timeout=20)
r.raise_for_status()
payload = r.json()
if not isinstance(payload, dict) or payload.get("success") is not True:
raise RuntimeError("Unsuccessful response envelope")
data = payload.get("data", {})
meta = payload.get("meta", {})
# Use request_id for tracing
if "request_id" in meta:
print(f"request_id={meta['request_id']}")
return payload
if __name__ == "__main__":
api_key = "YOUR_API_KEY"
port = "USLAX"
result = fetch_cyclone_disruptions(api_key, port)
print(result.get("data", {}))
Field usage and guardrails
Across FreightPulse Weather endpoints, the top-level envelope is consistent: success (boolean), data (object), and optional meta (object). Treat unknown or missing fields as expected in evolving weather taxonomies. In your ingestion layer:
- Always check success first and reject or quarantine payloads that do not set success=true.
- Persist the meta.request_id when present. It is useful for correlating your logs with FreightPulse support if you need to debug.
- Normalize timestamps to UTC before diffing or windowing event times.
- Do not derive or imply “live freight rates” from this data; never present a USD fare as “today’s price.”
Official sample response and important notes
The following official sample block is provided as-is and demonstrates the envelope shape you should code against. Copy it verbatim if you are testing JSON parsing. Your cyclone disruptions response will follow the same envelope pattern.
{
"success": true,
"data": {
"timestamp": "2026-10-10T00:00:20+00:00",
"source": "EIA (U.S. Energy Information Administration)",
"currency": "USD",
"unit": "gallon",
"data": {
"diesel": {
"national_average": 6.199,
"change_week": -0.183,
"change_percent": -2.87,
"updated_at": "2026-10-05",
"regions": {
"east_coast": 5.951,
"midwest": 6.286,
"gulf_coast": 5.819,
"rocky_mountain": 6.267,
"west_coast": 7.229,
"california": 8.082
}
},
"gasoline": {
"national_average": 4.354,
"change_week": -0.111,
"change_percent": -2.49,
"updated_at": "2026-10-05",
"grades": {
"regular": 4.354,
"midgrade": 4.99,
"premium": 5.4
}
},
"bunker_fuel": {
"available": false,
"note": "No known free source (Rotterdam/Singapore/Houston) — requires a paid provider (e.g. Ship & Bunker, Argus)."
}
},
"historical": {
"diesel_30d_avg": 6.349,
"diesel_90d_avg": 5.686,
"diesel_yoy_change": 67,
"gasoline_30d_avg": 4.404,
"gasoline_90d_avg": 4.155,
"gasoline_yoy_change": 39.4
}
},
"meta": {
"request_id": "b089ad89-1827-4363-990a-b66fc2a31258"
}
}
What you will use operationally:
- success: gate your logic on this boolean.
- data: parse the object your weather endpoint returns; the schema will follow this envelope.
- meta.request_id: keep it for cross-system tracing and support diagnostics.
Additional JSON examples of the envelope
These examples illustrate the success/data/meta envelope shape you should code against for cyclone disruption lookups. They use only documented fields, allowing you to unit-test your parsing and error handling paths.
{
"success": true,
"data": {},
"meta": {
"request_id": "example-request-id-1"
}
}
{
"success": true,
"data": {},
"meta": {
"request_id": "example-request-id-2"
}
}
Operational guidance: freshness, units, and hazard scope
Implementers often lose time reconciling timeliness and measurement conventions. Save yourself cycles by adopting the following defaults from day one:
- Timestamps: treat all timestamps as ISO-8601 with explicit offset. Normalize to UTC internally.
- Hazard scope: “Cyclone Disruptions” are Weather-only and sourced from GDACS/USGS/NOAA. Do not include labor strikes, canal transits, or other non-weather disruptions in your UI when showing cyclone results.
- Congestion data in context: where you surface congestion alongside cyclone context, communicate that congestion is IMF PortWatch-based and lagged ~6–7 days (7-day average vs 90-day baseline). It is not live AIS wait time.
- Currency and units: if your downstream uses any price or measurement fields from other endpoints, document the base currency/units explicitly for your team. Do not present any freight-rate USD fare as “today’s price.”
Testing strategy, retries, and monitoring
Follow a minimal but concrete regimen so your integration stays predictable under load and when network conditions degrade:
- Smoke test: run the official cURL request once per environment with X-API-Key: YOUR_API_KEY replaced by your real key. Validate HTTP 200 and a JSON body with success and data.
- Schema guards: deploy contract tests that assert the presence of success and data keys before every release. Treat a missing data object as a failure.
- Retries: apply limited retries with exponential backoff + jitter on 5xx only. Do not retry on 4xx authorization errors.
- Observability: log meta.request_id when present and tie it to your trace/span ID. Attach port and type=cyclone to each log line so you can filter quickly.
- Caching: align cache TTLs with how quickly weather hazards change for your use-case. Avoid excessive revalidation that could push you into guest-rate limits if you are unauthenticated.
Putting it together in your application
Developers typically integrate cyclone weather disruptions in one of three patterns:
- Inline request from a backend service for ad-hoc user queries (on-demand visibility pages).
- Cron or scheduled jobs to refresh a cache/table for a fixed set of ports (e.g., every 30–60 minutes for hazards, with a downstream UI consuming from your store).
- Event-driven refresh when a shipment’s ETA window intersects a cyclone’s geospatial envelope (internal business logic drives a fetch).
Start with a scheduled job if you serve a known list of ports. Trigger on-demand fallbacks only when a user drills into a previously uncached port to stay well within guest quotas or your allocated plan limits.
Reference: cURL to verify your environment
Use this exact command to validate your server’s egress, SSL bundle, and header propagation. Keep the URL and header names identical:
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/disruptions?port=USLAX&type=cyclone"
Troubleshooting checklist
- 401/403-like failures: confirm X-API-Key header uses YOUR_API_KEY in server-side code only. Do not send keys to the browser.
- Non-JSON responses: ensure Accept: application/json is set and your client does not downgrade TLS.
- Unexpected fields: code defensively; ignore unknown keys and treat missing fields as nulls rather than hard failures.
- Over-fetching: deploy caching to avoid hitting the guest 100 requests/day per IP limit if you are unauthenticated.
FAQ
Q: Can I call the cyclone disruptions endpoint without an API key?
A: Yes, but as a guest you are limited to 100 requests/day per IP. For production, set X-API-Key: YOUR_API_KEY.
Q: Are the congestion signals live AIS wait times?
A: No. FreightPulse congestion uses IMF PortWatch and is lagged ~6–7 days (7-day average vs 90-day baseline). Treat it as delayed, not live.
Q: What hazard sources back the cyclone category?
A: Natural hazards are sourced from GDACS/USGS/NOAA. The endpoint is Weather-specific—do not conflate it with strikes or canal disruptions.
Q: How should I handle timestamps?
A: Expect ISO-8601 with timezone offset. Normalize to UTC internally to keep comparisons and windows consistent.
Q: Can I present a USD freight rate as today’s price based on this endpoint?
A: No. Do not quote a freight-rates USD fare as today’s price from this endpoint.
Ready to implement cyclone disruption lookups in your stack? Create an account and get an API key here: Register. For field-by-field details and additional Weather examples, see the Documentation.