# Parcels API Track any package and shipment worldwide. Base URL: https://parcelsapp.com/api/v3 Authentication: Pass `apiKey` as a body field (POST) or query parameter (GET). Get your API key at: https://parcelsapp.com/dashboard --- ## How Tracking Works (Two-Step Flow) Tracking is asynchronous. You must: 1. POST to create a tracking request → receive a `uuid` 2. GET with that `uuid` repeatedly until `done: true` Do NOT supply the tracking number to the GET endpoint. Only use `uuid`. UUIDs expire after 30 minutes. If you POST a tracking number that is already being tracked, the API returns the existing request's UUID. --- ## Endpoints ### POST /shipments/tracking - Create Tracking Request Creates a new tracking request. May return cached results immediately (fromCache: true). Request body (JSON): apiKey (string, required) - Your API key shipments (array, required) - List of shipments to track (see Shipment Input below) language (string, optional) - 2-letter ISO language code, e.g. "en", "de" webhookUrl (string, optional) - HTTPS URL to receive a POST callback when tracking completes Shipment Input object fields: trackingId (string, required) - The tracking number destinationCountry (string, optional) - Full English country name, e.g. "United States", "Germany" zipcode (string, optional) - Postal code, required by some carriers slugs (array, optional) - Carrier slugs to force-use instead of auto-detection Response: { "uuid": "abc123", "done": false, "shipments": [] } If results are cached, `done` may be true and `shipments` will be populated immediately. --- ### GET /shipments/tracking - Read Tracking Results Poll this endpoint after creating a tracking request. Repeat every few seconds until done is true. Query parameters: uuid (required) - UUID returned from the POST call apiKey (required) - Your API key Response: { "uuid": "abc123", "done": true, "shipments": [ ... ] } When done is false, some shipments may still be tracking. Keep polling. When done is true, all shipments have finished tracking. --- ### GET /account - Get Account Information Returns subscription plan details and current usage. Query parameters: apiKey (required) - Your API key Response: { "plan": "Pro", "limit": 300, "current": 142, "resetDate": "2025-12-04T10:30:00.000Z" } Fields: plan - Subscription plan name limit - Max shipments allowed per billing period current - Shipments tracked so far this period resetDate - When the counter resets (UTC ISO 8601) Error responses: { "error": "MISSING_API_KEY", "description": "No .apiKey found" } { "error": "INVALID_API_KEY" } --- ## Response Data Structures ### Shipment object (items in the `shipments` array) trackingId - Tracking number status - One of: transit | arrived | pickup | delivered | archive origin - Origin country name (localized to requested language) destination - Destination country name (localized) originCode - Origin country 2-letter ISO code destinationCode - Destination country 2-letter ISO code states - Array of tracking events (see Event below) services - Array of all carriers queried (see Carrier below) detectedCarrier - Main carrier with the most events (see Carrier below) detected - Array of indexes into `services` where events were found attributes - Extra metadata: weight, dimensions, etc. (see Attribute below) externalTracking - Direct tracking links per carrier (see OutgoingLink below) ### Event object (items in `states`) state - Human-readable event description location - Where the event occurred date - ISO 8601 datetime in UTC carrier - Index into `services` array indicating which carrier reported this event ### Carrier object slug - Carrier identifier (e.g. "usps", "dhl", "fedex") name - Carrier display name ### Attribute object n - Attribute title/name l - Attribute label val - Attribute value (string) ### OutgoingLink object slug - Carrier slug url - Direct tracking URL on the carrier's website trackingId - Tracking number used for this carrier method - HTTP method: GET or POST --- ## Shipment Status Values transit - Package is in transit between locations arrived - Package arrived in the destination country pickup - Package is ready for pickup at a location delivered - Package was delivered to the recipient archive - Old or inactive tracking, no recent updates --- ## Webhooks Pass webhookUrl in the POST body to receive a callback instead of polling. The webhook receives the same JSON payload as the GET polling response. The webhook fires once when tracking is complete (done: true). For local testing: webhook.site, ngrok, localtunnel, or Cloudflare Tunnel. --- ## Code Examples ### Python - full polling loop import requests, time API_KEY = "your_api_key" URL = "https://parcelsapp.com/api/v3/shipments/tracking" # Step 1: Create tracking request resp = requests.post(URL, json={ "apiKey": API_KEY, "language": "en", "shipments": [ {"trackingId": "EE10021942088880001030003D0N", "destinationCountry": "Canada"} ] }) uuid = resp.json()["uuid"] # Step 2: Poll until done while True: resp = requests.get(URL, params={"apiKey": API_KEY, "uuid": uuid}) data = resp.json() if data["done"]: print(data["shipments"]) break time.sleep(3) ### Node.js - full polling loop const axios = require('axios'); const API_KEY = 'your_api_key'; const URL = 'https://parcelsapp.com/api/v3/shipments/tracking'; const { data: { uuid } } = await axios.post(URL, { apiKey: API_KEY, language: 'en', shipments: [{ trackingId: 'EE10021942088880001030003D0N', destinationCountry: 'Canada' }] }); const poll = async () => { const { data } = await axios.get(URL, { params: { apiKey: API_KEY, uuid } }); if (data.done) return data.shipments; await new Promise(r => setTimeout(r, 3000)); return poll(); }; const shipments = await poll(); console.log(shipments); ### cURL # Step 1 - create tracking request curl -X POST https://parcelsapp.com/api/v3/shipments/tracking \ -H 'Content-Type: application/json' \ -d '{"apiKey":"","language":"en","shipments":[{"trackingId":"EE10021942088880001030003D0N","destinationCountry":"Canada"}]}' # Step 2 - poll for results (replace UUID) curl "https://parcelsapp.com/api/v3/shipments/tracking?apiKey=&uuid=" ### Node.js - webhook approach const axios = require('axios'); axios.post('https://parcelsapp.com/api/v3/shipments/tracking', { apiKey: 'your_api_key', language: 'en', webhookUrl: 'https://your-domain.com/webhook', shipments: [{ trackingId: 'EE10021942088880001030003D0N', destinationCountry: 'Canada' }] }); // Your webhook endpoint receives the full result payload when done: // POST https://your-domain.com/webhook // Body: { uuid, done: true, shipments: [ ... ] } --- ## Key Rules to Remember 1. Always create a tracking request (POST) before reading results (GET). 2. Poll GET every 2-5 seconds; stop when done is true. 3. Do not pass the tracking number to GET - only the uuid. 4. UUIDs are temporary and expire after 30 minutes. 5. destinationCountry is the full English country name, not a 2-letter code. 6. Multiple tracking numbers can be submitted in one request via the shipments array. 7. Use the slugs field to force a specific carrier if auto-detection picks the wrong one. 8. The carrier field inside each Event is an array index into services, not a carrier name.