How to Add Logistics Intelligence to Your SaaS in 30 Minutes
You're building a supply chain SaaS. Users want rates and port context inside your product. Aggregating ten vendor APIs is a long project. FreightPulse is a smaller surface: EIA fuel, US FTL, PortWatch activity, FMCSA lookup — one key.
This tutorial wires those live endpoints. It does not add a disruption news feed or a global ocean index; those are not in v1.
What You'll Build
- Display a US FTL quote for two ZIP codes
- Show PortWatch congestion for a LOCODE (lag ~6–7 days, not wait time)
- Optionally show EIA national diesel
- Skip disruptions until that endpoint returns real items
Minute 0-5: Get Your API Key
Register at freightpulsehq.com/register. Free tier: 100 API calls/month. No credit card.
Copy the key from the dashboard:
export FREIGHTPULSE_API_KEY="fp_live_xxxxxxxxxxxx"
Minute 5-15: Backend Wrapper
// services/logistics.js
const API_BASE = 'https://freightpulsehq.com/api/v1';
const API_KEY = process.env.FREIGHTPULSE_API_KEY;
const headers = {
'X-API-Key': API_KEY,
'Accept': 'application/json'
};
export async function getFreightRates(originZip, destinationZip) {
const params = new URLSearchParams({
mode: 'trucking',
origin_zip: originZip,
destination_zip: destinationZip,
});
const res = await fetch(`${API_BASE}/freight-rates?${params}`, { headers });
if (!res.ok) throw new Error(`FreightPulse API error: ${res.status}`);
return res.json();
}
export async function getPortCongestion(locode) {
const params = new URLSearchParams({ port: locode });
const res = await fetch(`${API_BASE}/port-congestion?${params}`, { headers });
if (!res.ok) throw new Error(`FreightPulse API error: ${res.status}`);
return res.json();
}
Minute 15-20: App Routes
router.get('/api/logistics/rates', async (req, res) => {
const { origin_zip, destination_zip } = req.query;
const data = await getFreightRates(origin_zip, destination_zip);
res.json(data);
});
router.get('/api/logistics/congestion/:locode', async (req, res) => {
const data = await getPortCongestion(req.params.locode);
res.json(data);
});
Cache and available
Warp quotes are cached ~30 minutes on our side. Still cache in your app. If data.available === false, show the note — do not read price_usd.
Minute 20-28: A Rate Card
function FreightRateCard({ originZip, destinationZip }) {
const [body, setBody] = useState(null);
useEffect(() => {
const q = new URLSearchParams({
origin_zip: originZip,
destination_zip: destinationZip,
});
fetch(`/api/logistics/rates?${q}`)
.then(r => r.json())
.then(setBody);
}, [originZip, destinationZip]);
if (!body) return <Skeleton />;
if (body.data?.available === false) {
return <p>{body.data.note}</p>;
}
return (
<div className="rate-card">
<h3>FTL {originZip} → {destinationZip}</h3>
<div className="rate-value">${body.data.price_usd}</div>
<span>{body.data.transit_days} days · {body.data.service}</span>
</div>
);
}
Minute 28-30: Production Habits
- Degrade — never crash the dashboard if FreightPulse is down
- Loading states — PortWatch country queries can take tens of seconds; prefer
port=USLAX - Error boundaries — isolate the logistics widgets
What Not to Build Yet
- Webhooks — not offered. Poll and diff.
- Disruption inbox —
/disruptionsreturnsdata: [] - Official SDKs — use HTTP
Cost
Free 100 calls, Basic $49 / 1,000, Pro $99 / 10,000. Shared quota across endpoints.
Add Logistics Data to Your SaaS
EIA fuel, US FTL, port activity, FMCSA lookup. Start free.
Get Your API Key →Resources
- API documentation
- Freight rate guide — Warp FTL params
- Port congestion guide — PortWatch, not wait time