Get a normalized catalog aggregate
curl --request GET \
--url https://salespilot.crossup.ai/v1/catalog/items/{item_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://salespilot.crossup.ai/v1/catalog/items/{item_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://salespilot.crossup.ai/v1/catalog/items/{item_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://salespilot.crossup.ai/v1/catalog/items/{item_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://salespilot.crossup.ai/v1/catalog/items/{item_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://salespilot.crossup.ai/v1/catalog/items/{item_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://salespilot.crossup.ai/v1/catalog/items/{item_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}{
"type": "https://api.crossup.ai/salespilot/errors/unauthorized",
"title": "Authentication required",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/forbidden-scope",
"title": "Key is missing the required scope",
"status": 403,
"detail": "<string>",
"code": "forbidden_scope",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/not-found",
"title": "Resource not found",
"status": 404,
"detail": "<string>",
"code": "not_found",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "<string>",
"code": "rate_limited",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/internal-error",
"title": "Internal server error",
"status": 500,
"detail": "<string>",
"code": "internal_error",
"request_id": "<string>"
}Operations
Read a catalog item
Returns the canonical aggregate: item, variants and availability.
GET
/
v1
/
catalog
/
items
/
{item_id}
Get a normalized catalog aggregate
curl --request GET \
--url https://salespilot.crossup.ai/v1/catalog/items/{item_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://salespilot.crossup.ai/v1/catalog/items/{item_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://salespilot.crossup.ai/v1/catalog/items/{item_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://salespilot.crossup.ai/v1/catalog/items/{item_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://salespilot.crossup.ai/v1/catalog/items/{item_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://salespilot.crossup.ai/v1/catalog/items/{item_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://salespilot.crossup.ai/v1/catalog/items/{item_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}{
"type": "https://api.crossup.ai/salespilot/errors/unauthorized",
"title": "Authentication required",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/forbidden-scope",
"title": "Key is missing the required scope",
"status": 403,
"detail": "<string>",
"code": "forbidden_scope",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/not-found",
"title": "Resource not found",
"status": 404,
"detail": "<string>",
"code": "not_found",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "<string>",
"code": "rate_limited",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/internal-error",
"title": "Internal server error",
"status": 500,
"detail": "<string>",
"code": "internal_error",
"request_id": "<string>"
}Returns a
No language is guaranteed: pick one and have a fallback.
CatalogAggregate: the item, its variants and its availability, as
they stand after the ingested signals.
Three shapes that surprise you the first time
title is not a string, it's a LocalizedText
title is not a string, it's a LocalizedText
item.title and variant.title are { [lang]: string } objects with at
least one key:{ "en": "Demo T-shirt" }
primary_media.alt
has the same shape.sellable is a string, not a boolean
sellable is a string, not a boolean
availability[].sellable is an open string (the fixture uses
"in_stock"). A boolean can’t say “I don’t know”, and CrossUp doesn’t guess
what the provider didn’t say: the unknown stays unknown. Treat it as
opaque and display it as is.An absent quantity is not zero
An absent quantity is not zero
If the provider didn’t report inventory,
quantity isn’t there: it’s
unknown. quantity ?? 0 turns “I don’t know” into “out of stock”, a
business decision the API didn’t make.An item from another store returns 404, not 403
A missingitem_id and one from another store are indistinguishable: both
return not_found. An unexpected 404 with a
valid token is almost always another store’s token.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
CrossUp catalog item identifier.
Minimum string length:
1Response
Catalog aggregate found
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Which look at the store this aggregate came from. A client that caches can tell a newer read from a stale one without comparing every field, and a bug report that quotes it says exactly what the caller saw.
Required range:
x >= 1