Home → Blog

You need to programmatically check congestion for Colombo (LKCMB) and display it reliably in your logistics app. By the end of this guide, you’ll have a production-ready PHP integration that queries FreightPulse for LKCMB, handles the current “no data” state gracefully, and renders fields you care about (congestion, ratio_vs_baseline, as_of) when data becomes available.

What the FreightPulse LKCMB endpoint returns (and what it doesn’t)

The FreightPulse Port Congestion endpoint for Colombo (LKCMB) returns an envelope of {"success":true,"data":{...}}. Data is derived from IMF PortWatch and is lagged about 6–7 days. It represents a 7‑day average versus a 90‑day baseline; it is not a live AIS wait time feed.

Illustration: Colombo (LKCMB) Congestion

At the time of writing, there is no official congestion JSON published for LKCMB. The API responds successfully but with no available results for this port. You should still build your integration to read the canonical field names you’ll use when data becomes available: congestion, ratio_vs_baseline, and as_of.

Use the Documentation for the broader schema and changes over time, but keep your code tolerant to fields being missing when data is unavailable.

Authentication, headers, and quotas

Send your API key in the X-API-Key header. If you don’t have a key yet, the endpoint can be called unauthenticated, but you will be rate-limited to 100 requests per day per IP. In production, authenticate every call and add client-side rate limiting or caching to avoid unnecessary retries.

Quick test with cURL (copy and run)

Run this exactly as shown to verify connectivity and see the current state for LKCMB.

Live FreightPulse congestion for LKCMB.
Source: live

curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=LKCMB"
{
    "success": true,
    "data": {
        "results": [],
        "available": false,
        "reason": "Error querying IMF PortWatch"
    },
    "meta": {
        "request_id": "9c327678-dfa3-4de6-9c01-af39f069e063"
    }
}

Interpretation: the request succeeded but no congestion series is currently available for LKCMB. Your integration must handle available=false and an empty results array.

Schema you should code against

When available, congestion data for a port typically includes:

The endpoint envelopes data inside success and data. Always check:

PHP guide: end-to-end LKCMB lookup

Below is a complete PHP example using native cURL. It handles the current “no data” case, prints a developer-friendly message if data.available=false, and will automatically start rendering congestion, ratio_vs_baseline, and as_of once FreightPulse publishes an official series for LKCMB.

PHP example (cURL + robust parsing)

<?php
declare(strict_types=1);

// Configuration
$endpoint = 'https://freightpulsehq.com/api/v1/port-congestion?port=LKCMB';
$apiKey = 'YOUR_API_KEY'; // Set to your key. Calls without a key are limited.

// Basic HTTP GET with cURL
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'X-API-Key: ' . $apiKey,
],
]);

$responseBody = curl_exec($ch);
$curlErrNo = curl_errno($ch);
$curlErrMsg = curl_error($ch);
$httpStatus = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Transport errors
if ($curlErrNo !== 0) {
http_response_code(502);
echo "Transport error calling FreightPulse: {$curlErrMsg}\n";
exit(1);
}

// Non-2xx status handling (the API uses a success: true envelope for logical outcomes)
if ($httpStatus < 200 || $httpStatus >= 300) {
http_response_code($httpStatus);
echo "Unexpected HTTP status from FreightPulse: {$httpStatus}\n";
echo $responseBody . "\n";
exit(1);
}

// Decode JSON
$payload = json_decode($responseBody, true);
if (!is_array($payload)) {
http_response_code(502);
echo "Invalid JSON from FreightPulse.\n";
exit(1);
}

// Envelope checks
$success = $payload['success'] ?? false;
$data = $payload['data'] ?? null;
$meta = $payload['meta'] ?? null;

if (!$success || !is_array($data)) {
echo "FreightPulse signaled failure or missing data envelope.\n";
if (is_array($meta) && isset($meta['request_id'])) {
echo "request_id: " . $meta['request_id'] . "\n";
}
exit(1);
}

// Current LKCMB availability
$available = (bool) ($data['available'] ?? false);
$results = $data['results'] ?? [];
$reason = $data['reason'] ?? null;

if (!$available || empty($results)) {
// Graceful, developer-friendly output
echo "Colombo (LKCMB) congestion is currently unavailable from IMF PortWatch." . "\n";
if (is_string($reason) && $reason !== '') {
echo "Reason: {$reason}\n";
}
if (is_array($meta) && isset($meta['request_id'])) {
echo "request_id: " . $meta['request_id'] . "\n";
}
// Optionally, cache a null to avoid repeated calls for a short TTL.
exit(0);
}

// When data becomes available, iterate and render the latest observation.
// Expect fields: congestion, ratio_vs_baseline, as_of
// Sort by as_of if needed; here we just take the last element.
$latest = end($results);
$congestion = $latest['congestion'] ?? null;
$ratioVsBaseline = $latest['ratio_vs_baseline'] ?? null;
$asOf = $latest['as_of'] ?? null;

// Validate and print
if ($congestion === null || $ratioVsBaseline === null || $asOf === null) {
echo "LKCMB data present but missing required fields. Check docs and payload.\n";
exit(1);
}

// Output for your UI layer
echo "Port: Colombo (LKCMB)\n";
echo "As of: {$asOf} (lagged ~6–7 days, 7d avg vs 90d baseline)\n";
echo "Congestion: {$congestion}\n";
echo "Ratio vs Baseline: {$ratioVsBaseline}\n";

Drop this into a CLI job, a queue worker, or a controller action. Cache the entire JSON for a short TTL (for example, 30–60 minutes) to conserve your daily allotment if you are still testing without an API key.

JavaScript example (Node.js fetch)

The following script hits the same endpoint, inspects data.available, and reads congestion, ratio_vs_baseline, and as_of if provided.

import fetch from 'node-fetch';

const endpoint = 'https://freightpulsehq.com/api/v1/port-congestion?port=LKCMB';
const apiKey = 'YOUR_API_KEY';

async function getLkcmb() {
const res = await fetch(endpoint, {
headers: {
'Accept': 'application/json',
'X-API-Key': apiKey
},
timeout: 15000
});

if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}

const payload = await res.json();
if (!payload || payload.success !== true || !payload.data) {
throw new Error('Bad envelope from FreightPulse');
}

const { available = false, results = [], reason } = payload.data;
if (!available || results.length === 0) {
const rid = payload.meta && payload.meta.request_id ? payload.meta.request_id : 'n/a';
console.log('LKCMB congestion not available. Reason:', reason || 'n/a', 'request_id:', rid);
return null;
}

const latest = results[results.length - 1];
const { congestion, ratio_vs_baseline, as_of } = latest || {};
if (congestion == null || ratio_vs_baseline == null || as_of == null) {
throw new Error('LKCMB data present but required fields missing');
}

return { congestion, ratio_vs_baseline, as_of };
}

getLkcmb()
.then(data => {
if (!data) return;
console.log('Colombo (LKCMB) latest:', data);
})
.catch(err => {
console.error('Error:', err.message);
process.exitCode = 1;
});

Operational details that will save you time

Handling “no data” cleanly in UI

Given LKCMB currently returns available=false with an empty results array, present a clear, non-blocking notice to users. Recommended flow:

Testing strategies

Because LKCMB currently has no published series, you should unit-test parsing and rendering using dependency injection or fixtures in your test environment. In production code, do not hardcode fake congestion values. Instead, test logic paths by:

Security and reliability notes

Production checklist for Colombo (LKCMB)

FAQ

Q: Is this congestion live AIS data?
A: No. It is derived from IMF PortWatch, lagged ~6–7 days, reflecting a 7‑day average versus a 90‑day baseline.

Q: Can I call the endpoint without an API key?
A: Yes, but you’re limited to 100 requests per day per IP. Use X-API-Key: YOUR_API_KEY for consistent throughput.

Q: What fields should I persist in my database?
A: Store as_of (ISO date/time), congestion (number), and ratio_vs_baseline (number), plus the port code (LKCMB). Keep meta.request_id only in logs.

Q: How often should I poll?
A: Because data is lagged and updates infrequently, polling hourly or a few times per day is usually enough. Add caching to minimize duplicate requests.

Q: Why does LKCMB show no data?
A: The current response indicates an error querying IMF PortWatch for that port. Your code should treat this as a normal “temporarily unavailable” state and retry on your next scheduled poll.

Ready to implement this in your stack? Create an account to get an API key and move from guest limits to authenticated throughput. Register and then revisit the Documentation to finalize field mappings for Colombo (LKCMB).