Skip to content

Tracking

PII-free tracking of a shipment by its tracking code. The response deliberately exposes only carrier and status information, never recipient details.

This endpoint requires authentication. It was public until we noticed that shipping_code is the carrier's sequential number — anyone could increment it and walk other merchants' shipments and delivery outcomes. Client credentials scope the lookup to your own licenses. The customer-facing tracker your buyers use is the page linked from the pickup SMS, not this endpoint.

GET /tracking/

Resolve one of your shipments by its tracking code. Auth: client credentials. License: optional — the lookup spans every license you own.

Parameters

Path

FieldTypeRequiredDescription
shipping_codestringyesThe carrier tracking code (shipping_code) or the short tracking code.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/tracking/66747921' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ — credentials required; never ship these to a browser
const code = '66747921'

const response = await fetch(`https://app.shipos.co.il/api/v2/tracking/${code}`, {
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    Accept: 'application/json',
  },
})

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: shipment } = await response.json()

console.log(shipment.tracking_code, shipment.carrier.name)
console.log(shipment.status?.description, shipment.status?.is_delivered)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$shipment = json_decode(
    $client->get('tracking/66747921')->getBody()->getContents(),
    true,
)['data'];

echo $shipment['tracking_code'], ' ', $shipment['carrier']['name'], PHP_EOL;
echo $shipment['status']['description'] ?? 'no status', PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$shipment = Http::acceptJson()
    ->get('https://app.shipos.co.il/api/v2/tracking/66747921')
    ->throw()
    ->json('data');

logger()->info($shipment['tracking_code'].' '.$shipment['carrier']['name']);
logger()->info($shipment['status']['description'] ?? 'no status');
python
# pip install httpx
import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/tracking/66747921",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()
shipment = response.json()["data"]

print(shipment["tracking_code"], shipment["carrier"]["name"])
print((shipment.get("status") or {}).get("description", "no status"))
go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type trackingResponse struct {
	Data struct {
		TrackingCode string `json:"tracking_code"`
		Carrier      struct {
			Name string `json:"name"`
		} `json:"carrier"`
		Status *struct {
			Description string `json:"description"`
			IsDelivered bool   `json:"is_delivered"`
		} `json:"status"`
	} `json:"data"`
}

func main() {
	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/tracking/66747921", nil)
	req.Header.Set("Accept", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var payload trackingResponse
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		panic(err)
	}

	fmt.Println(payload.Data.TrackingCode, payload.Data.Carrier.Name)
	if payload.Data.Status != nil {
		fmt.Println(payload.Data.Status.Description)
	}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsTracking {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/tracking/66747921"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};

var payload = await http.GetFromJsonAsync<JsonDocument>("tracking/66747921")
    ?? throw new InvalidOperationException("Empty response");
var shipment = payload.RootElement.GetProperty("data");

Console.WriteLine($"{shipment.GetProperty("tracking_code").GetString()} " +
    $"{shipment.GetProperty("carrier").GetProperty("name").GetString()}");

if (shipment.TryGetProperty("status", out var status) &&
    status.ValueKind == JsonValueKind.Object)
{
    Console.WriteLine(status.GetProperty("description").GetString());
}
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/tracking/66747921")
request = Net::HTTP::Get.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

shipment = JSON.parse(response.body).fetch("data")

puts "#{shipment["tracking_code"]} #{shipment.dig("carrier", "name")}"
puts shipment.dig("status", "description") || "no status"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let payload: Value = reqwest::Client::new()
        .get("https://app.shipos.co.il/api/v2/tracking/66747921")
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    let shipment = &payload["data"];
    println!(
        "{} {}",
        shipment["tracking_code"], shipment["carrier"]["name"]
    );
    println!("{}", shipment["status"]["description"]);
    Ok(())
}

Response 200

json
{
  "data": {
    "tracking_code": "66747921",
    "carrier": {
      "id": 3,
      "name": "HFD"
    },
    "status": {
      "code": "delivered",
      "description": "Delivered to recipient",
      "is_delivered": true
    },
    "is_active": true,
    "updated_at": "2026-07-28T09:14:33.000000Z"
  }
}

Response fields

FieldTypeDescription
tracking_codestringThe shipment's public tracking code.
carrierobjectThe carrier (Provider): id and name.
statusobject | nullCurrent status: code, description, is_delivered. Null-valued sub-keys are dropped; the whole object is null when no status is known.
is_activeboolWhether the shipment is active.
updated_atstringISO-8601 timestamp of the last update.

Errors

StatuscodeWhen
404not_foundNo shipment matches the given tracking code (No shipment found for this tracking code.).