Skip to content

Ship to a pickup point

This tutorial walks the full pickup-point flow: browse the carrier's points, find the one nearest your customer, and create a shipment that delivers to it. For the full parameter tables see the Pickup points reference and Shipments reference.

All requests carry your client credentials and, when your account owns more than one active license, a license_key (query param on GET, body field on POST). The points you get back belong to the selected license's carrier — an HFD license lists HFD points, a Cargo license lists Cargo points.

Step 1 — Browse the carrier's points

Start with a plain listing to see what the carrier network offers:

bash
curl --location 'https://app.shipos.co.il/api/v2/pickup-points' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://app.shipos.co.il/api/v2/pickup-points', {
  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: points } = await response.json()

console.log(`${points.length} pickup points in the carrier network`)
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',
    ],
]);

$points = json_decode(
    $client->get('pickup-points')->getBody()->getContents(),
    true,
)['data'];

echo count($points), ' pickup points', PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$points = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get('https://app.shipos.co.il/api/v2/pickup-points')
    ->throw()
    ->json('data');

logger()->info(count($points).' pickup points');
python
# pip install httpx
import os

import httpx

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

print(len(points), "pickup points")
go
package main

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

type pickupPointsResponse struct {
	Data []struct {
		ID   string `json:"id"`
		Name string `json:"name"`
		Type string `json:"type"`
	} `json:"data"`
}

func main() {
	req, _ := http.NewRequest("GET", "https://app.shipos.co.il/api/v2/pickup-points", nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

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

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

	fmt.Println(len(payload.Data), "pickup points")
}
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 ShipOsPickupPoints {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/pickup-points"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("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":[...]} — map with Jackson/Gson
    }
}
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/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>("pickup-points")
    ?? throw new InvalidOperationException("Empty response");
var points = payload.RootElement.GetProperty("data");

Console.WriteLine($"{points.GetArrayLength()} pickup points");
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/pickup-points")
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)

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

puts "#{points.size} pickup points"
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/pickup-points")
        .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 total = payload["data"].as_array().map_or(0, |points| points.len());
    println!("{total} pickup points");
    Ok(())
}

Narrow it down with a case-insensitive city substring filter and a limit (capped at 500):

bash
curl --location 'https://app.shipos.co.il/api/v2/pickup-points?city=Tel%20Aviv&limit=50' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const params = new URLSearchParams({ city: 'Tel Aviv', limit: '50' })

const response = await fetch(
  `https://app.shipos.co.il/api/v2/pickup-points?${params}`,
  {
    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: points } = await response.json()

for (const point of points) {
  console.log(point.id, point.name, point.type)
}
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',
    ],
]);

$response = $client->get('pickup-points', [
    'query' => ['city' => 'Tel Aviv', 'limit' => 50],
]);

$points = json_decode($response->getBody()->getContents(), true)['data'];

foreach ($points as $point) {
    echo $point['id'], ' ', $point['name'], ' ', $point['type'], PHP_EOL;
}
php
<?php

use Illuminate\Support\Facades\Http;

$points = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get('https://app.shipos.co.il/api/v2/pickup-points', [
        'city' => 'Tel Aviv',
        'limit' => 50,
    ])
    ->throw()
    ->json('data');

foreach ($points as $point) {
    logger()->info($point['id'].' '.$point['name'].' '.$point['type']);
}
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/pickup-points",
    params={"city": "Tel Aviv", "limit": 50},
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()

for point in response.json()["data"]:
    print(point["id"], point["name"], point["type"])
go
package main

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

type pickupPointsResponse struct {
	Data []struct {
		ID   string `json:"id"`
		Name string `json:"name"`
		Type string `json:"type"`
	} `json:"data"`
}

func main() {
	query := url.Values{"city": {"Tel Aviv"}, "limit": {"50"}}
	endpoint := "https://app.shipos.co.il/api/v2/pickup-points?" + query.Encode()

	req, _ := http.NewRequest("GET", endpoint, nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

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

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

	for _, point := range payload.Data {
		fmt.Println(point.ID, point.Name, point.Type)
	}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class ShipOsPickupPointsByCity {
    public static void main(String[] args) throws Exception {
        String city = URLEncoder.encode("Tel Aviv", StandardCharsets.UTF_8);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/pickup-points"
                + "?city=" + city + "&limit=50"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("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":[...]} — map with Jackson/Gson
    }
}
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/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var query = $"?city={Uri.EscapeDataString("Tel Aviv")}&limit=50";
var payload = await http.GetFromJsonAsync<JsonDocument>($"pickup-points{query}")
    ?? throw new InvalidOperationException("Empty response");

foreach (var point in payload.RootElement.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{point.GetProperty("id").GetString()} " +
        $"{point.GetProperty("name").GetString()} " +
        $"{point.GetProperty("type").GetString()}");
}
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/pickup-points")
uri.query = URI.encode_www_form(city: "Tel Aviv", limit: 50)

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)

JSON.parse(response.body).fetch("data").each do |point|
  puts "#{point["id"]} #{point["name"]} #{point["type"]}"
end
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/pickup-points")
        .query(&[("city", "Tel Aviv"), ("limit", "50")])
        .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?;

    if let Some(points) = payload["data"].as_array() {
        for point in points {
            println!("{} {} {}", point["id"], point["name"], point["type"]);
        }
    }
    Ok(())
}

Carriers without a pickup network

A carrier that has no pickup network (or no synced point data yet) returns an empty list — that is not an error. Self-pickup and courier-only licenses simply have nothing to show here.

Step 2 — Find the nearest point

To rank points nearest-first, give the API an origin. There are two ways:

  • Explicit coordinates — pass lat and lng (both required together). No API key of any kind is needed.
  • Free-text address — pass address (optionally with city). The address is geocoded with the merchant's own key: google_api_key if configured, otherwise geoapify_api_key. If neither key is set, no geocoding call is made and you get the unranked list back — same as Step 1, no distance_km.

With an origin, each point gains a distance_km field and the list is sorted closest-first; a limit then gives you the N closest.

bash
curl --location 'https://app.shipos.co.il/api/v2/pickup-points?lat=32.0853&lng=34.7818&limit=5' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const params = new URLSearchParams({ lat: '32.0853', lng: '34.7818', limit: '5' })

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

const { data: points } = await res.json()
const nearest = points[0]

console.log(nearest.id, nearest.name, nearest.distance_km)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client(['base_uri' => 'https://app.shipos.co.il/api/v2/']);

$response = $client->get('pickup-points', [
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
    'query' => [
        'lat' => 32.0853,
        'lng' => 34.7818,
        'limit' => 5,
    ],
]);

$points = json_decode((string) $response->getBody(), true)['data'];
$nearest = $points[0] ?? null;
php
<?php

use Illuminate\Support\Facades\Http;

$points = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get('https://app.shipos.co.il/api/v2/pickup-points', [
        'lat' => 32.0853,
        'lng' => 34.7818,
        'limit' => 5,
    ])
    ->throw()
    ->json('data');

$nearest = $points[0] ?? null;
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/pickup-points",
    params={"lat": 32.0853, "lng": 34.7818, "limit": 5},
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()

points = response.json()["data"]
nearest = points[0] if points else None

print(nearest["id"], nearest["name"], nearest["distance_km"])
go
package main

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

type nearestResponse struct {
	Data []struct {
		ID         string   `json:"id"`
		Name       string   `json:"name"`
		DistanceKm *float64 `json:"distance_km"`
	} `json:"data"`
}

func main() {
	query := url.Values{"lat": {"32.0853"}, "lng": {"34.7818"}, "limit": {"5"}}
	endpoint := "https://app.shipos.co.il/api/v2/pickup-points?" + query.Encode()

	req, _ := http.NewRequest("GET", endpoint, nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

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

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

	nearest := payload.Data[0]
	fmt.Println(nearest.ID, nearest.Name, *nearest.DistanceKm)
}
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 ShipOsNearestPoint {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/pickup-points"
                + "?lat=32.0853&lng=34.7818&limit=5"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("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());
        }

        // data[0] is the nearest point — map with Jackson/Gson
        System.out.println(response.body());
    }
}
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/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>(
    "pickup-points?lat=32.0853&lng=34.7818&limit=5")
    ?? throw new InvalidOperationException("Empty response");

var nearest = payload.RootElement.GetProperty("data")[0];

Console.WriteLine($"{nearest.GetProperty("id").GetString()} " +
    $"{nearest.GetProperty("name").GetString()} " +
    $"{nearest.GetProperty("distance_km").GetDouble()} km");
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/pickup-points")
uri.query = URI.encode_www_form(lat: 32.0853, lng: 34.7818, limit: 5)

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)

nearest = JSON.parse(response.body).fetch("data").first

puts "#{nearest["id"]} #{nearest["name"]} #{nearest["distance_km"]}"
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/pickup-points")
        .query(&[("lat", "32.0853"), ("lng", "34.7818"), ("limit", "5")])
        .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 nearest = &payload["data"][0];
    println!(
        "{} {} {}",
        nearest["id"], nearest["name"], nearest["distance_km"]
    );
    Ok(())
}

The same request by address instead of coordinates:

bash
curl --location 'https://app.shipos.co.il/api/v2/pickup-points?address=Herzl%2010%2C%20Tel%20Aviv&limit=5' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const params = new URLSearchParams({ address: 'Herzl 10, Tel Aviv', limit: '5' })

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

const { data: points } = await res.json()

// distance_km is absent when no merchant geocoding key is configured
const nearest = points[0]
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client(['base_uri' => 'https://app.shipos.co.il/api/v2/']);

$response = $client->get('pickup-points', [
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
    'query' => [
        'address' => 'Herzl 10, Tel Aviv',
        'limit' => 5,
    ],
]);

$points = json_decode((string) $response->getBody(), true)['data'];
$nearest = $points[0] ?? null;
php
<?php

use Illuminate\Support\Facades\Http;

$points = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get('https://app.shipos.co.il/api/v2/pickup-points', [
        'address' => 'Herzl 10, Tel Aviv',
        'limit' => 5,
    ])
    ->throw()
    ->json('data');

$nearest = $points[0] ?? null;
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/pickup-points",
    params={"address": "Herzl 10, Tel Aviv", "limit": 5},
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()

points = response.json()["data"]
nearest = points[0] if points else None

print(nearest["id"], nearest.get("distance_km"))
go
package main

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

type geocodedResponse struct {
	Data []struct {
		ID         string   `json:"id"`
		Name       string   `json:"name"`
		DistanceKm *float64 `json:"distance_km"`
	} `json:"data"`
}

func main() {
	query := url.Values{"address": {"Herzl 10, Tel Aviv"}, "limit": {"5"}}
	endpoint := "https://app.shipos.co.il/api/v2/pickup-points?" + query.Encode()

	req, _ := http.NewRequest("GET", endpoint, nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

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

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

	nearest := payload.Data[0]
	fmt.Println(nearest.ID, nearest.Name, nearest.DistanceKm)
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class ShipOsNearestByAddress {
    public static void main(String[] args) throws Exception {
        String address = URLEncoder.encode("Herzl 10, Tel Aviv", StandardCharsets.UTF_8);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/pickup-points"
                + "?address=" + address + "&limit=5"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("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":[...]} — map with Jackson/Gson
    }
}
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/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var address = Uri.EscapeDataString("Herzl 10, Tel Aviv");
var payload = await http.GetFromJsonAsync<JsonDocument>(
    $"pickup-points?address={address}&limit=5")
    ?? throw new InvalidOperationException("Empty response");

var nearest = payload.RootElement.GetProperty("data")[0];

Console.WriteLine($"{nearest.GetProperty("id").GetString()} " +
    $"{nearest.GetProperty("name").GetString()}");
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/pickup-points")
uri.query = URI.encode_www_form(address: "Herzl 10, Tel Aviv", limit: 5)

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)

nearest = JSON.parse(response.body).fetch("data").first

puts "#{nearest["id"]} #{nearest["name"]} #{nearest["distance_km"]}"
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/pickup-points")
        .query(&[("address", "Herzl 10, Tel Aviv"), ("limit", "5")])
        .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 nearest = &payload["data"][0];
    println!("{} {}", nearest["id"], nearest["name"]);
    Ok(())
}

Response — nearest first, distance_km in kilometres from your origin:

json
{
  "data": [
    {
      "id": "12345",
      "name": "SuperPharm Dizengoff",
      "type": "locker",
      "address": {
        "city": "Tel Aviv",
        "street": "Dizengoff",
        "house": "50"
      },
      "coordinates": {
        "latitude": 32.0791,
        "longitude": 34.7742
      },
      "distance_km": 0.842
    },
    {
      "id": "67890",
      "name": "Cofix Allenby",
      "type": "store",
      "address": {
        "city": "Tel Aviv",
        "street": "Allenby",
        "house": "3"
      },
      "coordinates": {
        "latitude": 32.0737,
        "longitude": 34.7688
      },
      "distance_km": 1.735
    }
  ]
}

Keep the point's id — that is what the shipment will reference.

TIP

distance_km is present only when you supplied an origin (explicit lat/lng, or an address that geocoded successfully). Points that have no coordinates of their own get distance_km: null and sort last. Geocoding failures never error — they degrade to the unranked list.

Step 3 — Create the pickup-point shipment

A pickup-point delivery is a normal POST /shipments where ship_data.pickup carries the point id from Step 2. Two things change compared with a home delivery:

  • ship_data.pickup (string, max 64) — the pickup point id. This is what routes the parcel to the point.
  • ship_data.pickup_address (string, optional) — a human-readable label of the point, stored alongside.
  • ship_data.street and ship_data.city are not required when ship_data.pickup is present (they are required_without:ship_data.pickup).

Everything else follows the standard create body: ship_data.type (1 = regular delivery, 2 = collection), ship_data.return (1 = single, 2 = round-trip), ship_data.packages, the required contact fields, and the required order envelope with its order.shipping recipient snapshot.

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
  "license_key": "{license_key}",
  "ship_data": {
    "contact_name": "Dana Levi",
    "contact_phone": "0521234567",
    "contact_mail": "dana.levi@example.co.il",
    "type": "1",
    "return": "1",
    "packages": 1,
    "pickup": "12345",
    "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
    "note": "Fragile"
  },
  "order": {
    "id": "1042",
    "number": "1042",
    "currency": "ILS",
    "total": 249.90,
    "shipping": {
      "first_name": "Dana",
      "last_name": "Levi",
      "phone": "0521234567",
      "email": "dana.levi@example.co.il",
      "city": "Tel Aviv",
      "country": "IL"
    }
  }
}'
js
// Node.js 18+ / browsers — no dependencies
const res = await fetch('https://app.shipos.co.il/api/v2/shipments', {
  method: 'POST',
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    license_key: licenseKey,
    ship_data: {
      contact_name: 'Dana Levi',
      contact_phone: '0521234567',
      contact_mail: 'dana.levi@example.co.il',
      type: '1',
      return: '1',
      packages: 1,
      pickup: '12345',
      pickup_address: 'SuperPharm Dizengoff, Dizengoff 50, Tel Aviv',
      note: 'Fragile',
    },
    order: {
      id: '1042',
      number: '1042',
      currency: 'ILS',
      total: 249.9,
      shipping: {
        first_name: 'Dana',
        last_name: 'Levi',
        phone: '0521234567',
        email: 'dana.levi@example.co.il',
        city: 'Tel Aviv',
        country: 'IL',
      },
    },
  }),
})

const { data: shipment } = await res.json()
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client(['base_uri' => 'https://app.shipos.co.il/api/v2/']);

$response = $client->post('shipments', [
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
    'json' => [
        'license_key' => $licenseKey,
        'ship_data' => [
            'contact_name' => 'Dana Levi',
            'contact_phone' => '0521234567',
            'contact_mail' => 'dana.levi@example.co.il',
            'type' => '1',
            'return' => '1',
            'packages' => 1,
            'pickup' => '12345',
            'pickup_address' => 'SuperPharm Dizengoff, Dizengoff 50, Tel Aviv',
            'note' => 'Fragile',
        ],
        'order' => [
            'id' => '1042',
            'number' => '1042',
            'currency' => 'ILS',
            'total' => 249.90,
            'shipping' => [
                'first_name' => 'Dana',
                'last_name' => 'Levi',
                'phone' => '0521234567',
                'email' => 'dana.levi@example.co.il',
                'city' => 'Tel Aviv',
                'country' => 'IL',
            ],
        ],
    ],
]);

$shipment = json_decode((string) $response->getBody(), true)['data'];
php
<?php

use Illuminate\Support\Facades\Http;

$shipment = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->post('https://app.shipos.co.il/api/v2/shipments', [
        'license_key' => $licenseKey,
        'ship_data' => [
            'contact_name' => 'Dana Levi',
            'contact_phone' => '0521234567',
            'contact_mail' => 'dana.levi@example.co.il',
            'type' => '1',
            'return' => '1',
            'packages' => 1,
            'pickup' => '12345',
            'pickup_address' => 'SuperPharm Dizengoff, Dizengoff 50, Tel Aviv',
            'note' => 'Fragile',
        ],
        'order' => [
            'id' => '1042',
            'number' => '1042',
            'currency' => 'ILS',
            'total' => 249.90,
            'shipping' => [
                'first_name' => 'Dana',
                'last_name' => 'Levi',
                'phone' => '0521234567',
                'email' => 'dana.levi@example.co.il',
                'city' => 'Tel Aviv',
                'country' => 'IL',
            ],
        ],
    ])
    ->throw()
    ->json('data');
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://app.shipos.co.il/api/v2/shipments",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
    json={
        "license_key": license_key,
        "ship_data": {
            "contact_name": "Dana Levi",
            "contact_phone": "0521234567",
            "contact_mail": "dana.levi@example.co.il",
            "type": "1",
            "return": "1",
            "packages": 1,
            "pickup": "12345",
            "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
            "note": "Fragile",
        },
        "order": {
            "id": "1042",
            "number": "1042",
            "currency": "ILS",
            "total": 249.90,
            "shipping": {
                "first_name": "Dana",
                "last_name": "Levi",
                "phone": "0521234567",
                "email": "dana.levi@example.co.il",
                "city": "Tel Aviv",
                "country": "IL",
            },
        },
    },
)
response.raise_for_status()
shipment = response.json()["data"]
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	body := fmt.Sprintf(`{
  "license_key": %q,
  "ship_data": {
    "contact_name": "Dana Levi",
    "contact_phone": "0521234567",
    "contact_mail": "dana.levi@example.co.il",
    "type": "1",
    "return": "1",
    "packages": 1,
    "pickup": "12345",
    "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
    "note": "Fragile"
  },
  "order": {
    "id": "1042",
    "number": "1042",
    "currency": "ILS",
    "total": 249.90,
    "shipping": {
      "first_name": "Dana",
      "last_name": "Levi",
      "phone": "0521234567",
      "email": "dana.levi@example.co.il",
      "city": "Tel Aviv",
      "country": "IL"
    }
  }
}`, os.Getenv("SHIPOS_LICENSE_KEY"))

	req, _ := http.NewRequest("POST",
		"https://app.shipos.co.il/api/v2/shipments", strings.NewReader(body))
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

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

	shipment, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(shipment))
}
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 ShipOsCreatePickupShipment {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "license_key": "%s",
              "ship_data": {
                "contact_name": "Dana Levi",
                "contact_phone": "0521234567",
                "contact_mail": "dana.levi@example.co.il",
                "type": "1",
                "return": "1",
                "packages": 1,
                "pickup": "12345",
                "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
                "note": "Fragile"
              },
              "order": {
                "id": "1042",
                "number": "1042",
                "currency": "ILS",
                "total": 249.90,
                "shipping": {
                  "first_name": "Dana",
                  "last_name": "Levi",
                  "phone": "0521234567",
                  "email": "dana.levi@example.co.il",
                  "city": "Tel Aviv",
                  "country": "IL"
                }
              }
            }""".formatted(System.getenv("SHIPOS_LICENSE_KEY"));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/shipments"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Content-Type", "application/json")
            .header("Accept", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

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

        if (response.statusCode() != 201) {
            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;
using System.Text.Json;

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var licenseKey = Environment.GetEnvironmentVariable("SHIPOS_LICENSE_KEY");
var body = $$"""
    {
      "license_key": "{{licenseKey}}",
      "ship_data": {
        "contact_name": "Dana Levi",
        "contact_phone": "0521234567",
        "contact_mail": "dana.levi@example.co.il",
        "type": "1",
        "return": "1",
        "packages": 1,
        "pickup": "12345",
        "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
        "note": "Fragile"
      },
      "order": {
        "id": "1042",
        "number": "1042",
        "currency": "ILS",
        "total": 249.90,
        "shipping": {
          "first_name": "Dana",
          "last_name": "Levi",
          "phone": "0521234567",
          "email": "dana.levi@example.co.il",
          "city": "Tel Aviv",
          "country": "IL"
        }
      }
    }
    """;

var response = await http.PostAsync("shipments",
    new StringContent(body, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();

var payload = await response.Content.ReadFromJsonAsync<JsonDocument>()
    ?? throw new InvalidOperationException("Empty response");
var shipment = payload.RootElement.GetProperty("data");

Console.WriteLine(shipment.GetProperty("tracking_code").GetString());
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/shipments")
request = Net::HTTP::Post.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
request.body = JSON.generate({
  license_key: ENV.fetch("SHIPOS_LICENSE_KEY"),
  ship_data: {
    contact_name: "Dana Levi",
    contact_phone: "0521234567",
    contact_mail: "dana.levi@example.co.il",
    type: "1",
    return: "1",
    packages: 1,
    pickup: "12345",
    pickup_address: "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
    note: "Fragile",
  },
  order: {
    id: "1042",
    number: "1042",
    currency: "ILS",
    total: 249.90,
    shipping: {
      first_name: "Dana",
      last_name: "Levi",
      phone: "0521234567",
      email: "dana.levi@example.co.il",
      city: "Tel Aviv",
      country: "IL",
    },
  },
})

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")
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let body = json!({
        "license_key": std::env::var("SHIPOS_LICENSE_KEY")?,
        "ship_data": {
            "contact_name": "Dana Levi",
            "contact_phone": "0521234567",
            "contact_mail": "dana.levi@example.co.il",
            "type": "1",
            "return": "1",
            "packages": 1,
            "pickup": "12345",
            "pickup_address": "SuperPharm Dizengoff, Dizengoff 50, Tel Aviv",
            "note": "Fragile"
        },
        "order": {
            "id": "1042",
            "number": "1042",
            "currency": "ILS",
            "total": 249.90,
            "shipping": {
                "first_name": "Dana",
                "last_name": "Levi",
                "phone": "0521234567",
                "email": "dana.levi@example.co.il",
                "city": "Tel Aviv",
                "country": "IL"
            }
        }
    });

    let payload: Value = reqwest::Client::new()
        .post("https://app.shipos.co.il/api/v2/shipments")
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .json(&body)
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    println!("{}", payload["data"]["tracking_code"]);
    Ok(())
}

Response 201 — note pickup_point_id echoing the point you chose:

json
{
  "data": {
    "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "tracking_code": "66747921",
    "carrier": { "id": 3, "name": "HFD" },
    "status": null,
    "service_type": "1",
    "is_active": true,
    "recipient": {
      "name": "Dana Levi",
      "phone": "0521234567",
      "company": null,
      "address": {
        "street": null,
        "number": null,
        "city": "Tel Aviv",
        "state": null,
        "zip": null,
        "country": "IL"
      }
    },
    "pickup_point_id": "12345",
    "packages": 1,
    "cod": null,
    "order": { "id": "1042", "number": "1042" },
    "references": { "external_id": null },
    "short_tracking_code": null,
    "label_generated": false,
    "collection_status": null,
    "collected_at": null,
    "ready_at": null,
    "created_at": "2026-07-29T09:14:00.000000Z",
    "updated_at": "2026-07-29T09:14:00.000000Z"
  }
}

Make the create retry-safe

Add an Idempotency-Key header to the create request so a network retry can never produce a second parcel. See Idempotency.

Step 4 — Track it

Use the returned uuid for authenticated reads — GET /shipments/{shipment}, GET /shipments/{shipment}/status (refreshes from the carrier), GET /shipments/{shipment}/label — or the public GET /tracking/{code} with the tracking_code. Details in the Shipments reference.

Common errors

StatuscodeWhen
403forbiddenThe license_key is inactive/expired/not owned, or the account has no active license.
409duplicate_requestA create for the same logical shipment is already in flight — retry shortly.
422validation_failedBody failed validation (e.g. missing street/city and no pickup), or multiple active licenses and no license_key.
424carrier_errorThe carrier rejected the shipment (e.g. an id of a point that no longer exists).