Home → Blog

You need a reliable way to programmatically look up freight rates without guessing at prices or copy/pasting from spreadsheets. By the end of this guide, you’ll be able to call the FreightPulse freight-rates endpoint from PHP, interpret the response envelope, respect authentication and usage limits, and ship a basic integration you can evolve safely into production.

What you’ll build and what to watch out for

This walkthrough centers on fetching data from the FreightPulse freight-rates API with PHP. You will:

Illustration: How to Get Freight Rates

Important constraints for freight developers:

Endpoint, auth, and envelope in one place

FreightPulse uses simple header-based authentication and a consistent response envelope. This is the minimum you need to reach the service:

If you’re just getting started, create an account to obtain a production API key here: Register. Refer to the parameter reference as you extend beyond the sample lane: Documentation.

Quick connectivity test with curl

Run this request from your terminal to verify connectivity, headers, and the base response envelope. Replace YOUR_API_KEY with your key, or omit the header to test as a guest:

curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/freight-rates?mode=trucking&origin_zip=10001&destination_zip=90210"

This call uses the documented path and query string. Do not treat this lane (10001 → 90210, trucking) as a live quote for real-world pricing.

Understand the JSON envelope (official sample)

The official sample below demonstrates the envelope and the kinds of fields you will parse. Copy it exactly when building mocks or tests:

{
"success": true,
"data": {
"timestamp": "2026-10-22T00:00:15+00:00",
"source": "EIA (U.S. Energy Information Administration)",
"currency": "USD",
"unit": "gallon",
"data": {
"diesel": {
"national_average": 6.276,
"change_week": 0.097,
"change_percent": 1.57,
"updated_at": "2026-10-19",
"regions": {
"east_coast": 5.984,
"midwest": 6.352,
"gulf_coast": 5.903,
"rocky_mountain": 6.422,
"west_coast": 7.394,
"california": 8.242
}
},
"gasoline": {
"national_average": 4.284,
"change_week": -0.047,
"change_percent": -1.09,
"updated_at": "2026-10-19",
"grades": {
"regular": 4.284,
"midgrade": 4.915,
"premium": 5.314
}
},
"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.259,
"diesel_90d_avg": 5.88,
"diesel_yoy_change": 73.4,
"gasoline_30d_avg": 4.359,
"gasoline_90d_avg": 4.213,
"gasoline_yoy_change": 41.9
}
},
"meta": {
"request_id": "72e4d78a-2f2d-4054-81d3-cd716ffe8494"
}
}

Fields you will actually use when building clients:

Do not re-label fields or assume the presence of any value not shown in the official sample. If you need additional attributes, consult the Documentation.

PHP: End-to-end freight-rates lookup

The PHP example below calls the freight-rates endpoint, checks the envelope, and extracts timestamp, currency, and unit. It demonstrates header auth, safe JSON decoding, and basic error handling. Replace YOUR_API_KEY with your actual key for production use.

<?php
$apiKey = getenv('FREIGHTPULSE_API_KEY');
if (!$apiKey) {
$apiKey = 'YOUR_API_KEY';
}

$endpoint = 'https://freightpulsehq.com/api/v1/freight-rates';
$params = http_build_query([
'mode' => 'trucking',
'origin_zip' => '10001',
'destination_zip' => '90210',
]);

$url = $endpoint . '?' . $params;

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

$responseBody = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErrNo = curl_errno($ch);
$curlErr = curl_error($ch);
curl_close($ch);

if ($curlErrNo !== 0) {
// Network or TLS error
http_response_code(502);
echo json_encode([
'error' => 'Transport error',
'detail' => $curlErr,
]);
exit;
}

if ($httpCode < 200 || $httpCode >= 300) {
// Upstream returned a non-2xx code
http_response_code($httpCode);
echo json_encode([
'error' => 'Upstream HTTP ' . $httpCode,
'body' => $responseBody,
]);
exit;
}

$payload = json_decode($responseBody, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(502);
echo json_encode([
'error' => 'Invalid JSON',
'detail' => json_last_error_msg(),
]);
exit;
}

// Envelope check
if (!isset($payload['success']) || $payload['success'] !== true) {
http_response_code(502);
echo json_encode([
'error' => 'API indicated failure',
'body' => $payload,
]);
exit;
}

$data = isset($payload['data']) ? $payload['data'] : [];
$meta = isset($payload['meta']) ? $payload['meta'] : [];

// Extract safe, documented fields from the sample
$timestamp = isset($data['timestamp']) ? $data['timestamp'] : null;
$currency = isset($data['currency']) ? $data['currency'] : null;
$unit = isset($data['unit']) ? $data['unit'] : null;
$requestId = isset($meta['request_id']) ? $meta['request_id'] : null;

// You can pass these to your UI; do not present them as a binding, same-day freight quote.
$result = [
'timestamp' => $timestamp,
'currency' => $currency,
'unit' => $unit,
'request_id' => $requestId,
// If needed, forward the entire nested structure to downstream services:
// 'raw' => $data,
];

header('Content-Type: application/json');
echo json_encode($result);

Notes:

JavaScript fetch example

This Node/Browser-compatible snippet calls the same endpoint and extracts the same core fields. If you run it in Node, set FREIGHTPULSE_API_KEY in your environment or hardcode YOUR_API_KEY while testing.

async function getFreightRates() {
const apiKey = process.env.FREIGHTPULSE_API_KEY || 'YOUR_API_KEY';
const url = 'https://freightpulsehq.com/api/v1/freight-rates?mode=trucking&origin_zip=10001&destination_zip=90210';

const res = await fetch(url, {
method: 'GET',
headers: {
'X-API-Key': apiKey,
'Accept': 'application/json'
}
});

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

const payload = await res.json();
if (!payload || payload.success !== true) {
throw new Error('API indicated failure or missing success flag');
}

const data = payload.data || {};
const meta = payload.meta || {};

// Extract safe fields from the official sample
const summary = {
timestamp: data.timestamp || null,
currency: data.currency || null,
unit: data.unit || null,
request_id: meta.request_id || null
};

return summary;
}

getFreightRates()
.then(summary => {
console.log('FreightPulse summary:', summary);
})
.catch(err => {
console.error('Error:', err.message);
});

Practical integration details that save time

Units and currency:

Timestamps and timezone:

Caching:

Usage limits:

Pagination and field availability:

Non-trading days equivalent:

On port congestion and why it’s not a “live wait time”

If you display port congestion metrics alongside freight rates, annotate them clearly. FreightPulse congestion references IMF PortWatch and is lagged about 6–7 days, using a 7‑day average versus a 90‑day baseline. It is not sourced from live AIS vessel waits.

Presenting rates responsibly

Do not quote any USD freight rate as “today’s price” from the freight-rates endpoint. Treat responses as reference data or inputs to your internal calculators. Where you need a binding price, implement a separate quoting workflow that stores terms, validity windows, and vendor/contract attribution outside of the read-only FreightPulse lookup.

Troubleshooting and observability

Quick checks when something looks off:

Validation and safe rollout checklist

FAQ

Can I call the freight-rates endpoint without an API key?
Yes. Guests are allowed 100 requests per day per IP. Add the X-API-Key header when you’re ready to exceed that or move to production.

Is the port congestion metric live?
No. FreightPulse port congestion references IMF PortWatch and is lagged roughly 6–7 days (7‑day average vs 90‑day baseline), not live AIS waits.

Can I present the returned USD values as a same-day quote?
No. Do not quote a freight-rates USD fare as today’s price. Treat responses as reference data and always display timestamps.

What response shape should I code against?
Use the envelope: success (boolean), data (object), and optional meta (object). Null-check fields and avoid assuming undocumented attributes.

Where do I find parameters and extended fields?
See the product docs for the latest query parameters and field definitions: Documentation.

Ready to integrate with a proper API key and ship your first freight lookup safely? Create your account here: Register. Keep the envelope contract, respect the lag on congestion metrics, and avoid claiming same-day quoted prices, and you’ll have a dependable pipeline from sandbox to production.