Courier Tracking REST API Documentation
A single standardized REST JSON API to track shipments across Indian courier networks with normalized statuses, delivery attempt diagnostics, and stuck-shipment detection.
Quickstart
To query any active consignment, make an HTTP GET request to /v1/track/:carrier/:awb passing your provisioned API key in the X-API-Key request header.
curl -s -H "X-API-Key: ct_live_your_api_key_here" \
"https://api.cleartrackpromise.in/v1/track/delhivery/123456789012"
Authentication
All endpoints under /v1/ require authentication. You must supply your live API key via the X-API-Key HTTP request header.
| Header | Format | Description |
|---|---|---|
X-API-Key |
ct_live_[64-character-hex] |
Required on all authenticated API v1 calls. |
Requests missing the X-API-Key header return HTTP 401 with code missing_api_key. Invalid or revoked keys return HTTP 401 with code invalid_api_key.
Track a Shipment by AWB
Retrieve normalized tracking details by specifying the courier slug and the consignment number (AWB) in the request URL path.
GET https://api.cleartrackpromise.in/v1/track/:carrier/:awb
Path Parameters
| Parameter | Type | Description |
|---|---|---|
:carrier |
string | Canonical carrier slug or recognized alias (e.g. delhivery, bluedart, indiapost). |
:awb |
string | Consignment number / Air Waybill (:tracking_number is also accepted as an alias). |
Standard Normalized Response JSON
{
"awb": "CP100000001IN",
"tracking_number": "CP100000001IN",
"carrier": "indiapost",
"status": "delivered",
"status_text": "Item Delivered",
"is_delivered": true,
"is_rto": false,
"origin": {
"office": null,
"city": "New Delhi",
"state": "Delhi",
"pincode": null
},
"destination": {
"office": null,
"city": "Bengaluru",
"state": "Karnataka",
"pincode": null
},
"estimated_delivery_at": null,
"delivered_at": "2026-10-08T14:30:00.000Z",
"first_scan_at": "2026-10-05T10:15:00.000Z",
"last_location": "Bengaluru",
"last_known_location": "Bengaluru",
"last_updated_at": "2026-10-08T14:30:00.000Z",
"last_polled_at": "2026-10-08T14:35:00.000Z",
"cached": true,
"stale": false,
"shipment": {
"service_type": null,
"weight_grams": null,
"origin": {
"office": null,
"city": "New Delhi",
"state": "Delhi",
"pincode": null
},
"destination": {
"office": null,
"city": "Bengaluru",
"state": "Karnataka",
"pincode": null
},
"booked_at": "2026-10-05T10:15:00.000Z"
},
"milestones": {
"booked_at": "2026-10-05T10:15:00.000Z",
"picked_up_at": null,
"in_transit_at": "2026-10-05T18:40:00.000Z",
"out_for_delivery_at": "2026-10-07T08:00:00.000Z",
"first_attempt_at": null,
"delivered_at": "2026-10-08T14:30:00.000Z",
"rto_initiated_at": null,
"rto_delivered_at": null
},
"delivery_attempts": {
"count": 1,
"attempts": [
{
"at": "2026-10-08T14:30:00.000Z",
"outcome": "delivered",
"reason_code": null,
"reason_text": null
}
]
},
"insights": {
"transit_days_elapsed": 3.2,
"hours_since_last_scan": null,
"is_stuck": false,
"is_late_vs_carrier_edd": null,
"delay_days_vs_carrier_edd": null,
"hub_count": 2,
"current_stage": "delivered",
"requires_attention": false,
"attention_reason": null,
"benchmark": {
"median_transit_days_for_route": null,
"sample_size": null,
"expected_delivery_by": null
}
},
"events": [
{
"timestamp": "2026-10-08T14:30:00.000Z",
"status": "delivered",
"status_text": "Item Delivered",
"code": "delivered",
"facility": "Bengaluru",
"location": "Bengaluru",
"description": "Item Delivered"
},
{
"timestamp": "2026-10-07T08:00:00.000Z",
"status": "out_for_delivery",
"status_text": "Item Out for Delivery",
"code": "out_for_delivery",
"facility": "Bengaluru",
"location": "Bengaluru",
"description": "Item Out for Delivery"
},
{
"timestamp": "2026-10-06T09:20:00.000Z",
"status": "in_transit",
"status_text": "Item Received at Bengaluru NSH",
"code": "received",
"facility": "Bengaluru",
"location": "Bengaluru",
"description": "Item Received at Bengaluru NSH"
},
{
"timestamp": "2026-10-05T18:40:00.000Z",
"status": "in_transit",
"status_text": "Item Dispatched to Delhi NSH",
"code": "dispatched",
"facility": "New Delhi",
"location": "New Delhi",
"description": "Item Dispatched to Delhi NSH"
},
{
"timestamp": "2026-10-05T10:15:00.000Z",
"status": "picked_up",
"status_text": "Item Booked at New Delhi GPO",
"code": "booked",
"facility": "New Delhi",
"location": "New Delhi",
"description": "Item Booked at New Delhi GPO"
}
]
}
Normalized Status Values
Top-level status values are normalized to a consistent enum across all carriers:
Supported Couriers
ClearTrack supports 11 Indian courier networks directly via verified canonical slugs and accepted aliases:
| Canonical Slug | Courier Network | Common Aliases |
|---|---|---|
delhivery | Delhivery | delhivery |
bluedart | Blue Dart | blue-dart, blue_dart |
trackon | Trackon | trackon |
shiprocket | Shiprocket | shiprocket |
shreemaruti | Shree Maruti Courier | maruti, shree-maruti |
indiapost | India Post | india-post, india_post |
speedpost | Speed Post | speed-post, speed_post |
shadowfax | Shadowfax | shadowfax |
xpressbees | Xpressbees | expressbees, xpress-bees |
ekart | Ekart Logistics | ekart-logistics, ekart_logistics |
professional | The Professional Couriers | tpc, professional-courier |
Errors & Rate Limits
The API returns standard HTTP status codes accompanied by a structured JSON error payload:
| Status | Error Code | Description |
|---|---|---|
400 |
invalid_awb |
AWB string is missing or shorter than 4 characters. |
400 |
unsupported_carrier |
Specified carrier slug is not recognized. |
401 |
missing_api_key |
X-API-Key request header is absent. |
401 |
invalid_api_key |
API key is malformed, suspended, or revoked. |
402 |
payment_required |
Plan validity has expired. Contact support to renew. |
404 |
shipment_not_found |
No tracking scans exist for this consignment in carrier networks. |
429 |
rate_limit_exceeded |
Per-second burst rate exceeded (retry after backoff). |
429 |
plan_quota_exceeded |
Monthly period lookup quota exhausted. |
429 |
daily_quota_exceeded |
Daily abuse quota reached (resets at UTC midnight). |
502 |
upstream_error |
Carrier network temporarily unreachable and no cached record exists. |
Lookup Counting & Billing
ClearTrack bills strictly on lookups, not on distinct consignments:
- Every HTTP request to
/v1/track/...consumes 1 lookup from your period quota. - Active in-transit shipments are cached for 30 minutes; calls within that window return cached data in milliseconds and consume 1 lookup.
- Terminal consignments (
delivered,returned,cancelled) are permanently cached. - You can monitor your remaining lookups and period validity via
GET /v1/usage.
For full plan options and rates, review our dedicated Pricing Page.
Code Examples: cURL
Use cURL to track shipments from shell scripts or command-line pipelines:
# Track a Blue Dart consignment
curl -s -X GET "https://api.cleartrackpromise.in/v1/track/bluedart/12345678901" \
-H "X-API-Key: ct_live_your_api_key_here" \
-H "Accept: application/json"
Code Examples: Node.js
Native fetch example compatible with Node.js 18+ and modern JavaScript runtimes:
async function trackShipment(carrier, awb) {
const apiKey = process.env.CLEARTRACK_API_KEY || "ct_live_your_api_key_here";
const url = `https://api.cleartrackpromise.in/v1/track/${encodeURIComponent(carrier)}/${encodeURIComponent(awb)}`;
const response = await fetch(url, {
method: "GET",
headers: {
"X-API-Key": apiKey,
"Accept": "application/json",
},
});
const data = await response.json();
if (!response.ok) {
throw new Error(`Tracking API Error (${response.status}): ${data.error?.message || "Unknown error"}`);
}
console.log("Status:", data.status);
console.log("Delivered:", data.is_delivered);
console.log("Current Stage:", data.insights?.current_stage);
return data;
}
// Example usage
trackShipment("delhivery", "123456789012").catch(console.error);
Code Examples: Python
Integration using the popular Python requests library:
import os
import requests
def track_shipment(carrier: str, awb: str) -> dict:
api_key = os.getenv("CLEARTRACK_API_KEY", "ct_live_your_api_key_here")
url = f"https://api.cleartrackpromise.in/v1/track/{carrier}/{awb}"
headers = {
"X-API-Key": api_key,
"Accept": "application/json"
}
response = requests.get(url, headers=headers, timeout=15)
response.raise_for_status()
data = response.json()
print(f"Status: {data.get('status')}")
print(f"Delivered: {data.get('is_delivered')}")
print(f"Stage: {data.get('insights', {}).get('current_stage')}")
return data
if __name__ == "__main__":
track_shipment("indiapost", "CP100000001IN")
Code Examples: PHP
Standard PHP cURL implementation for WooCommerce stores and PHP backends:
<?php
function trackShipment(string $carrier, string $awb): array {
$apiKey = getenv('CLEARTRACK_API_KEY') ?: 'ct_live_your_api_key_here';
$url = "https://api.cleartrackpromise.in/v1/track/" . rawurlencode($carrier) . "/" . rawurlencode($awb);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-API-Key: {$apiKey}",
"Accept: application/json",
],
CURLOPT_TIMEOUT => 15,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode >= 400) {
$message = $data['error']['message'] ?? 'API error';
throw new Exception("ClearTrack API Error ({$httpCode}): {$message}");
}
return $data;
}
// Example usage
$result = trackShipment('delhivery', '123456789012');
echo "Status: " . $result['status'] . PHP_EOL;
echo "Stage: " . ($result['insights']['current_stage'] ?? 'unknown') . PHP_EOL;