Developer guides

Package tracking API in PHP with cURL

For PHP applications that need tracking data without adopting an ecommerce plugin.

By Trace · Reviewed

Use PHP on the server to call Trace and return an authorized, minimal tracking view to your customer. The integration does not require a native plugin, but your application still owns order authorization, refresh policy and error presentation.

A cURL transport error and an API error are different. A timeout may mean no response reached you; a structured HTTP failure can explain authentication, quota or source availability. Preserve that distinction so your support team can investigate it.

Send JSON with a bounded timeout

The example requires the PHP cURL extension and an environment-provided key and tracking number. It uses a fixed API URL rather than a caller-supplied host, and does not follow redirects with the authorization header. Run it behind your own order authorization, not as an unrestricted public tracking proxy.

PHP · minimal server-side lookup

<?php
$key = getenv("TRACE_API_KEY");
$number = getenv("TRACKING_NUMBER");
if (!$key || !$number) throw new RuntimeException("Missing configuration");
$ch = curl_init("https://api.traceapi.dev/v1/track");
curl_setopt_array($ch, [
  CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 95,
  CURLOPT_FOLLOWLOCATION => false,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode(["tracking_number" => $number], JSON_THROW_ON_ERROR),
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($raw === false) { curl_close($ch); throw new RuntimeException("Tracking transport failed"); }
curl_close($ch);
$body = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if ($status < 200 || $status >= 300) {
  throw new RuntimeException("Tracking API HTTP " . $status);
}
// Validate fields your view uses; do not return the entire response blindly.
echo htmlspecialchars($body["status"], ENT_QUOTES, "UTF-8");

Choose a different response for each failure

A 401 requires fixing the server credential rather than asking the buyer to retry. A monthly quota response needs a plan or reset decision. A transient source failure can retain the last verified snapshot in your database with an explicit age label. Never claim a new carrier update when a request failed.

FailureApplication action
Transport timeoutRecord a correlation ID and offer a bounded later retry.
401 authenticationCheck server configuration; keep the key out of logs.
402 monthly quotaPause refreshes and inspect Billing.
429 rate limitHonor Retry-After and coordinate requests.
503 source unavailableShow temporary unavailability; do not invent a tracking status.

Keep slow carrier work out of a busy PHP request pool

A 95-second client timeout does not extend your PHP execution limit, reverse proxy deadline or hosting request limit. Align all of those limits deliberately. For a page with many parcels, prefer a stored shipment snapshot or an asynchronous tracking job, and have a worker refresh it. One buyer reload should not fan out to dozens of carrier requests.

Validate escaping, empty fields and partial outages

Test a sandbox fixture before a live number. Escape all carrier descriptions and locations when rendering HTML, including text that looks harmless in a sample. Check null timestamps, missing locations and an empty estimate. Test a non-JSON upstream response as a failure so an HTML error page never becomes a successful shipment view.

Common questions

Does this install a WooCommerce extension?

No. This is a PHP server integration. A store still needs its own order lookup, access checks and fulfillment mapping; the WooCommerce guide covers those decisions.

References & next steps

Implementation details were checked against Trace’s current API. External product details may change; verify the linked documentation before deciding.