Home → Blog

You need to programmatically check congestion at Tianjin Xin Gang (Tianjin) and surface a clear status, the ratio vs baseline, and the date those figures apply to. By the end of this guide you will have a PHP integration that queries the FreightPulse congestion endpoint for Tianjin, handles the response envelope, and extracts the exact fields you need for your application.

What the Tianjin endpoint returns and how to read it

FreightPulse exposes congestion for Tianjin Xin Gang (Tianjin) via a single read-only endpoint. Responses are wrapped in an envelope where success is a boolean and the data payload contains results. The congestion level is derived from IMF PortWatch and reflects a 7-day average versus a 90-day baseline, lagged roughly 6–7 days and not a live AIS wait-time feed.

Illustration: Tianjin Xin Gang (Tianjin) Congestion

For endpoint specifics and broader options, see the Documentation.

Quick test: cURL and the official JSON

The following cURL is copy-pasteable and targets Tianjin directly using the port filter. Replace YOUR_API_KEY with your own key when you have one; you can also test as a guest (subject to the guest quota).

Live FreightPulse congestion for Tianjin.
Source: live
cURL:

curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=Tianjin"

JSON:

{
    "success": true,
    "data": {
        "results": [
            {
                "port": "Tianjin Xin Gang",
                "locode": null,
                "country": "China",
                "congestion": "moderate",
                "as_of": "2026-10-23",
                "metrics": {
                    "recent_avg_portcalls_per_day": 26.4,
                    "baseline_avg_portcalls_per_day": 29.4,
                    "ratio_vs_baseline": 0.9
                }
            }
        ],
        "global_summary": {
            "ports_high_congestion": 0,
            "ports_moderate": 1,
            "ports_low": 0
        },
        "meta": {
            "source": "IMF PortWatch (portwatch.imf.org) - UN Global Platform AIS data",
            "data_lag_days": "~6-7 (daily updates, not real-time)",
            "methodology": "congestion level is an approximation: last 7 days' avg port calls vs 90-day baseline for the same port. Does not measure vessel wait/anchorage time."
        }
    },
    "meta": {
        "request_id": "8c30b0a0-9abb-4181-b5a1-a8b90cc592ae"
    }
}

What you’ll actually read for Tianjin:

PHP implementation: production-ready lookup for Tianjin

This section walks through a minimal, dependency-free PHP client using cURL. It queries the Tianjin port, checks the envelope, handles basic HTTP failures, and extracts congestion, ratio_vs_baseline, and as_of for display or downstream logic.

Step 1 — Basic request with cURL

<?php
declare(strict_types=1);

// Configuration
$apiUrl = 'https://freightpulsehq.com/api/v1/port-congestion?port=Tianjin';
$apiKey = 'YOUR_API_KEY'; // Replace with your key when available

// Build request
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'X-API-Key: ' . $apiKey,
],
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 20,
]);

$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);

if ($err) {
http_response_code(502);
echo 'Transport error calling FreightPulse: ' . htmlspecialchars($err);
exit;
}

if ($httpCode < 200 || $httpCode >= 300) {
http_response_code($httpCode);
echo 'FreightPulse returned HTTP ' . $httpCode . ' with body: ' . htmlspecialchars((string) $responseBody);
exit;
}

// Parse JSON
$data = json_decode((string) $responseBody, true, 512, JSON_THROW_ON_ERROR);

// Validate envelope
if (!isset($data['success']) || !$data['success'] || !isset($data['data']['results'][0])) {
http_response_code(502);
echo 'Unexpected FreightPulse envelope or missing results.';
exit;
}

$result = $data['data']['results'][0];

// Extract fields for Tianjin
$port = $result['port'] ?? 'Tianjin Xin Gang';
$congestion = $result['congestion'] ?? null;
$asOf = $result['as_of'] ?? null;
$ratio = $result['metrics']['ratio_vs_baseline'] ?? null;

// Optional additional metrics available
$recentAvg = $result['metrics']['recent_avg_portcalls_per_day'] ?? null;
$baselineAvg = $result['metrics']['baseline_avg_portcalls_per_day'] ?? null;

// Render a compact summary
header('Content-Type: application/json');
echo json_encode([
'port' => $port,
'congestion' => $congestion,
'ratio_vs_baseline' => $ratio,
'as_of' => $asOf,
'recent_avg_portcalls_per_day' => $recentAvg,
'baseline_avg_portcalls_per_day' => $baselineAvg,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

Step 2 — Notes on error handling and quotas

Step 3 — Output shaping for your UI

Most UIs only need three fields for Tianjin:

JavaScript example: Node.js fetch for Tianjin

If you prefer JavaScript for backend or scripts, here is a minimal Node.js example using fetch. It reads the exact same fields as the PHP sample.

import fetch from 'node-fetch';

const API_URL = 'https://freightpulsehq.com/api/v1/port-congestion?port=Tianjin';
const API_KEY = 'YOUR_API_KEY'; // Replace with your key or omit header to test as guest

async function getTianjinCongestion() {
const res = await fetch(API_URL, {
headers: {
'Accept': 'application/json',
'X-API-Key': API_KEY,
},
timeout: 20000,
});

if (!res.ok) {
const body = await res.text();
throw new Error(`FreightPulse HTTP ${res.status}: ${body}`);
}

const json = await res.json();

if (!json.success || !json.data || !Array.isArray(json.data.results) || json.data.results.length === 0) {
throw new Error('Unexpected envelope or empty results');
}

const r = json.data.results[0];
return {
port: r.port,
congestion: r.congestion,
ratio_vs_baseline: r.metrics?.ratio_vs_baseline ?? null,
as_of: r.as_of,
};
}

getTianjinCongestion()
.then(console.log)
.catch(err => {
console.error('Error fetching Tianjin congestion:', err.message);
process.exitCode = 1;
});

Field semantics, timing, and caching

FreightPulse congestion for Tianjin is derived from IMF PortWatch and intentionally lagged. This section clarifies how to present and cache values without implying live conditions.

Recommended caching:

Production integration checklist for PHP

Troubleshooting and observability

Here are pragmatic steps that avoid common pitfalls when shipping to production:

Testing strategy

Manual and automated checks help ensure reliable deployments:

FAQ

Is this data real-time AIS?
No. It is derived from IMF PortWatch and lagged roughly 6–7 days. It measures relative port-call activity, not anchorage wait time.

What authentication should I use?
Send X-API-Key: YOUR_API_KEY in the request header. Guests without a key are limited to 100 requests/day per IP.

Which fields should I display to summarize Tianjin?
Show congestion, metrics.ratio_vs_baseline (formatted as a percentage), and as_of. Optionally show recent_avg_portcalls_per_day and baseline_avg_portcalls_per_day.

How often should I refresh the data?
Daily is sufficient for most use cases. Cache for 12–24 hours and surface the as_of date so users understand recency.

Does this endpoint include freight rates?
No. This endpoint returns congestion metrics for Tianjin Xin Gang (Tianjin). Do not present any freight-rate USD fare as a current price when showing congestion.

Get started

Spin up your Tianjin congestion checker in minutes. Create an API key and move from guest limits to a stable integration: Register. For endpoint behaviors, response envelopes, and additional options, review the Documentation.