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:
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'// 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
// 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
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');# 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")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 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
}
}// .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");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"// [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):
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'// 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
// 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
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']);
}# 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"])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 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
}
}// .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()}");
}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// [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
latandlng(both required together). No API key of any kind is needed. - Free-text address — pass
address(optionally withcity). The address is geocoded with the merchant's own key:google_api_keyif configured, otherwisegeoapify_api_key. If neither key is set, no geocoding call is made and you get the unranked list back — same as Step 1, nodistance_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.
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'// 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
// 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
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;# 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"])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 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());
}
}// .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");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"]}"// [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:
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'// 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
// 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
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;# 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"))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 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
}
}// .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()}");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"]}"// [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:
{
"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.streetandship_data.cityare not required whenship_data.pickupis present (they arerequired_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.
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"
}
}
}'// 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
// 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
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');# 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"]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 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":{...}}
}
}// .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());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")// [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:
{
"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
| Status | code | When |
|---|---|---|
| 403 | forbidden | The license_key is inactive/expired/not owned, or the account has no active license. |
| 409 | duplicate_request | A create for the same logical shipment is already in flight — retry shortly. |
| 422 | validation_failed | Body failed validation (e.g. missing street/city and no pickup), or multiple active licenses and no license_key. |
| 424 | carrier_error | The carrier rejected the shipment (e.g. an id of a point that no longer exists). |