You need to programmatically detect weather-driven flood disruptions that may impact shipments moving through a specific port, and you want a copy-pasteable implementation you can ship today. By the end of this guide, you’ll be able to query FreightPulse for flood-related disruptions at a port (e.g., USLAX), handle authentication with X-API-Key, validate the standard response envelope, and integrate it into a PHP workflow with production-minded error handling, plus a lightweight JavaScript example.
What you’ll query: Flood disruptions (Weather-only)
This guide focuses strictly on weather-related disruption signals—specifically floods—so you can alert, pause bookings, or reroute before impacts cascade. The lookup you’ll implement is Flood Disruptions, which surfaces natural-hazard events. Natural hazards are sourced from GDACS/USGS/NOAA only. This post does not cover strikes, labor actions, canal closures, or any non-weather category.
Do not quote a spot freight rate or a USD fare as today’s price in any downstream UI you build. Also, FreightPulse port congestion metrics (when you use them) are IMF PortWatch-derived, lagged approximately 6–7 days, presented as a 7-day average vs a 90-day baseline, and are not live AIS wait times.
Auth, quotas, and the response envelope
Authentication is via a header:
- Header: X-API-Key: YOUR_API_KEY
- Guest without a key: 100 requests/day per IP
Every response is wrapped in a consistent envelope:
- Envelope: {"success":true,"data":{...}}
You should always check success before reading fields in data. When writing parsers, code defensively to handle nulls or unexpected shapes, and keep a copy of meta.request_id in your logs for troubleshooting with support.
Quick start: cURL for Flood Disruptions
Use this exact request to retrieve flood-related disruptions for the Los Angeles (USLAX) port. Replace the API key value with your own when you have one; for guest usage, omit the header and stay within the guest quota.
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/disruptions?port=USLAX&type=flood"
Notes:
- Method: GET
- Parameters:
- port: a port code such as USLAX
- type: flood
- Header: X-API-Key as shown above
For more details, see the Documentation.
PHP implementation: request, validation, and safe parsing
The following PHP snippet uses cURL to call the Flood Disruptions lookup, validates the standard response envelope, and returns the decoded data for your application. It avoids hard assumptions about inner fields, since disruption payloads can evolve. Extend the data handling only after inspecting the actual fields you receive in your environment.
PHP: Minimal client with timeouts and basic error handling
<?php
function fetchFloodDisruptions(string $port, ?string $apiKey = null): array {
$url = "https://freightpulsehq.com/api/v1/disruptions?port=" . urlencode($port) . "&type=flood";
$ch = curl_init($url);
$headers = [];
if ($apiKey !== null && $apiKey !== "") {
$headers[] = "X-API-Key: " . $apiKey;
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 5, // seconds
CURLOPT_TIMEOUT => 15, // total timeout
CURLOPT_HTTPHEADER => $headers,
]);
$raw = curl_exec($ch);
$curlErr = curl_error($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($raw === false) {
throw new RuntimeException("HTTP request failed: " . $curlErr);
}
if ($status < 200 || $status >= 300) {
throw new RuntimeException("Unexpected HTTP status: $status; body: $raw");
}
$json = json_decode($raw, true);
if (!is_array($json)) {
throw new RuntimeException("Response was not valid JSON");
}
// Validate standard envelope
if (!array_key_exists('success', $json) || $json['success'] !== true) {
// Optionally, log meta.request_id if provided
$reqId = $json['meta']['request_id'] ?? 'unknown';
throw new RuntimeException("API indicated failure (request_id=$reqId)");
}
if (!array_key_exists('data', $json)) {
throw new RuntimeException("Missing data field in response");
}
// Return the data payload as-is; your app can then inspect the fields it needs.
return $json['data'];
}
try {
// Provide YOUR_API_KEY if you have one; otherwise call with null for guest mode.
$data = fetchFloodDisruptions('USLAX', 'YOUR_API_KEY');
// Example: log top-level keys and keep a copy for audit
error_log('Received disruption keys: ' . implode(',', array_keys($data)));
// TODO: Map $data to your alerting or routing logic once you review live fields.
print_r($data);
} catch (Throwable $e) {
// Surface errors to your logging/observability
error_log('Disruption fetch failed: ' . $e->getMessage());
http_response_code(502);
echo json_encode(['ok' => false, 'error' => 'Upstream fetch failed']);
}
Implementation tips:
- Set short connect and overall timeouts so a stuck upstream call doesn’t block your web thread.
- Do not assume the presence of any field beyond success and data in the top-level envelope.
- Preserve meta.request_id if present to speed up support investigations.
PHP: Retrying transient failures
If you run this in a background job, consider exponential backoff for 429 or 5xx statuses. Keep total retry time bounded to protect SLAs.
<?php
function httpGetWithBackoff(string $url, array $headers = [], int $maxAttempts = 4): array {
$delayMs = 200; // initial backoff
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => $headers,
]);
$raw = curl_exec($ch);
$err = curl_error($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($raw === false) {
if ($attempt === $maxAttempts) {
throw new RuntimeException("HTTP error: " . $err);
}
} elseif ($status >= 200 && $status < 300) {
$json = json_decode($raw, true);
if (is_array($json)) return $json;
if ($attempt === $maxAttempts) {
throw new RuntimeException("Invalid JSON on final attempt");
}
} elseif (in_array($status, [429, 500, 502, 503, 504], true)) {
// retryable
} else {
throw new RuntimeException("Non-retryable status $status; body: $raw");
}
usleep($delayMs * 1000);
$delayMs = min(4000, (int)($delayMs * 2.0));
}
throw new RuntimeException("Exhausted retries");
}
function fetchFloodWithRetry(string $port, ?string $apiKey = null): array {
$url = "https://freightpulsehq.com/api/v1/disruptions?port=" . urlencode($port) . "&type=flood";
$headers = [];
if ($apiKey) $headers[] = "X-API-Key: " . $apiKey;
$json = httpGetWithBackoff($url, $headers);
if (($json['success'] ?? null) !== true) {
$reqId = $json['meta']['request_id'] ?? 'unknown';
throw new RuntimeException("API indicated failure (request_id=$reqId)");
}
if (!array_key_exists('data', $json)) {
throw new RuntimeException("Missing data in envelope");
}
return $json['data'];
}
JavaScript example: fetch flood disruptions in Node
This example fetches disruptions for USLAX and performs the same envelope checks. It prints the top-level keys of data so you can quickly see what’s available in your environment.
import fetch from 'node-fetch';
async function getFloodDisruptions(port, apiKey) {
const url = `https://freightpulsehq.com/api/v1/disruptions?port=${encodeURIComponent(port)}&type=flood`;
const headers = {};
if (apiKey) headers['X-API-Key'] = apiKey;
const res = await fetch(url, { headers, redirect: 'follow' });
if (!res.ok) {
const body = await res.text();
throw new Error(`HTTP ${res.status}: ${body}`);
}
const json = await res.json();
if (json.success !== true) {
const reqId = json.meta?.request_id ?? 'unknown';
throw new Error(`API indicated failure (request_id=${reqId})`);
}
if (!Object.prototype.hasOwnProperty.call(json, 'data')) {
throw new Error('Missing data in response');
}
return json.data;
}
(async () => {
try {
const data = await getFloodDisruptions('USLAX', 'YOUR_API_KEY');
console.log('data keys:', Object.keys(data));
} catch (err) {
console.error('Fetch failed:', err.message);
process.exitCode = 1;
}
})();
Official JSON sample: envelope and field handling
The following official sample shows the standard envelope returned by FreightPulse. Copy it as-is if you need a mocked fixture while wiring up your parser. Do not substitute your own fields in tests until your integration is complete.
{
"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"
}
}
Field notes you will actually use during integration:
- success: Boolean gate you must check before processing.
- data: The payload container. Always present when success is true.
- data.timestamp: An ISO 8601 timestamp with offset (UTC shown here). Use this to reason about staleness and caching. Timezone: +00:00 in the example.
- data.source: Source attribution string.
- data.currency and data.unit: Units metadata for any numeric values in the payload (base currency in this sample is USD; unit is gallon). Always render units alongside values in UIs.
- meta.request_id: Stable identifier for logging and support tickets. Include this in error logs.
While the inner fields above illustrate units, time, and structure, you should still code to the envelope so your client remains robust for flood disruptions.
Operational details that will save you time
- Natural hazards scope: Only GDACS/USGS/NOAA. Do not expect labor or canal events in the flood feed.
- Rate limits: Guests without a key get 100 requests/day per IP. Obtain an API key to lift that cap.
- Caching: Cache-positive responses for at least several minutes to protect your quota and reduce latency to your users. Use data.timestamp as a freshness signal.
- Timestamps and timezones: Timestamps are ISO 8601 with explicit UTC offset. Normalize to UTC in your backend, localize in the UI if needed.
- Units and currency: Respect and display units and currency metadata from data when present. Never convert or re-denominate without labeling.
- Port congestion: When you later combine disruptions with congestion context, remember FreightPulse congestion is IMF PortWatch-derived, lagged by roughly 6–7 days and computed as a 7-day average vs a 90-day baseline. Do not present it as live AIS wait times.
Testing locally and promoting to production
For local development, start with the official cURL and confirm you receive the standard envelope. Next, run the PHP client and log the keys present under data for your target port. Avoid schema-guessing—introspect the payload you receive and then explicitly map the fields you want to alert on.
In production:
- Set connect and total timeouts and use retries with backoff for transient errors only (429/5xx).
- Record meta.request_id on all failures to accelerate debugging.
- Cache successful responses keyed by the full URL, including port and type parameters.
- Establish health checks that verify success is true and that data.timestamp is not overly stale relative to your business needs.
Common pitfalls and how to avoid them
- Skipping the success gate: Always verify success === true before reading data. If absent, fail fast and log request_id.
- Overfitting to fields: Code to the envelope first; inspect then map. Don’t hardcode inner paths you haven’t validated.
- Mixing categories: Keep this integration weather-only. Don’t blend strikes or canal events into flood alerts.
- Confusing congestion with live waits: Congestion is lagged IMF PortWatch data, not live AIS. Don’t display it as real-time waits.
- Exceeding guest quota: Without an API key, limit yourself to 100 requests/day per IP or acquire a key.
FAQ
Q: Do I need an API key?
A: You can call the endpoint without a key, but you’re limited to 100 requests/day per IP. Add X-API-Key: YOUR_API_KEY to raise limits.
Q: What parameters are supported for flood lookups?
A: Use port (e.g., USLAX) and type=flood as shown in the quick-start cURL. See the Documentation for any additional options.
Q: How should I handle the response schema?
A: First validate the envelope (success and data). Then introspect data in your environment and map only the fields you need. Keep meta.request_id for logs.
Q: Are the signals real-time?
A: Natural hazard feeds can be near-real-time depending on the source, but port congestion context is IMF PortWatch, lagged roughly 6–7 days and not live AIS wait times.
Q: Can I display freight rates alongside disruptions?
A: Do not quote a freight-rates USD fare as today’s price in conjunction with this data.
Ready to put flood disruption detection into production? Create your account and get an API key now: Register. For endpoint details and integration patterns, keep the docs open: Documentation.