Products
The shop's catalog: titles and text in every language, prices, stock, categories and images.
Reach them through $admin->products. Reading needs the products:read scope, changing them products:write. To set stock for many products at once, use inventory.
List products
$admin->products->list(array $query = []): array
One page of the shop's products, by words in the title, SKU or part number, by status, or by an exact SKU; or oldest change first with updatedSince. Read the next page with meta.nextCursor as cursor. Scope: products:read.
Parameters
| Parameter |
Type |
|
$query |
array |
The query fields below, by name. |
Query fields
| Parameter | Type | Description |
|---|
search | string | At most 200 characters. |
sku | string | An exact SKU. At most 255 characters. |
status | string | One of active, hidden. |
limit | integer | At least 1. At most 100. Default 50. |
cursor | string | meta.nextCursor (or the cursor in links.next) of the previous page. At most 1000 characters. |
updatedSince | string (date-time) | Records changed at or after this time (ISO 8601, any offset or Z), oldest change first, for incremental sync. |
Returns
An array with data (a list of AdminProduct), links and meta:
| Field | Type | Description |
|---|
links | object | |
links.first | null | |
links.last | null | |
links.prev | string (uri) or null | |
links.next | string (uri) or null | |
meta | object | |
meta.path | string (uri) | |
meta.perPage | integer | |
meta.nextCursor | string or null | |
meta.prevCursor | string or null | |
data | array of AdminProduct | |
data[].id | integer | |
data[].slug | string | |
data[].title | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data[].shortDescription | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data[].description | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data[].sku | string or null | |
data[].partNumber | string or null | |
data[].type | string | One of physical, digital. |
data[].downloadLimit | integer or null | |
data[].documentUrl | string or null | |
data[].status | boolean | True when published. |
data[].isFeatured | boolean or null | |
data[].noindex | boolean or null | |
data[].price | number or null | |
data[].salePrice | number or null | |
data[].isSaleScheduled | boolean or null | |
data[].saleStartsAt | string (date-time) or null | |
data[].saleEndsAt | string (date-time) or null | |
data[].wholesalePrice | number or null | |
data[].manageStock | boolean or null | |
data[].stockQuantity | integer or null | |
data[].lowStockAmount | integer or null | |
data[].stockStatus | string or null | One of in_stock, out_of_stock. |
data[].unitId | integer or null | |
data[].taxClassId | integer or null | |
data[].weight | number or null | |
data[].length | number or null | |
data[].width | number or null | |
data[].height | number or null | |
data[].taxonomyTermIds | array of integer | |
data[].relatedProductIds | array of integer | The products shown as applicable products on the product page, in order. |
data[].recommendedProductIds | array of integer | The merchant's picks for the product page's You May Also Like section, in order. Without picks, the page shows the closest matches. |
data[].specTemplateId | integer or null | |
data[].variableProductId | integer or null | |
data[].thumbnail | string (uri) or null | |
data[].gallery | array of string | |
data[].createdAt | string (date-time) or null | |
data[].updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
$page = $admin->products->list(['sku' => 'MUG-01']);
$product = $page['data'][0] ?? null;
Iterate over every product
$admin->products->all(array $query = []): Generator
Every product matching the query, one at a time, fetching the next page as you read. It takes the same query as list, without cursor. Scope: products:read.
Returns
A generator of AdminProduct arrays, each:
| Field | Type | Description |
|---|
[].id | integer | |
[].slug | string | |
[].title | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
[].shortDescription | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
[].description | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
[].sku | string or null | |
[].partNumber | string or null | |
[].type | string | One of physical, digital. |
[].downloadLimit | integer or null | |
[].documentUrl | string or null | |
[].status | boolean | True when published. |
[].isFeatured | boolean or null | |
[].noindex | boolean or null | |
[].price | number or null | |
[].salePrice | number or null | |
[].isSaleScheduled | boolean or null | |
[].saleStartsAt | string (date-time) or null | |
[].saleEndsAt | string (date-time) or null | |
[].wholesalePrice | number or null | |
[].manageStock | boolean or null | |
[].stockQuantity | integer or null | |
[].lowStockAmount | integer or null | |
[].stockStatus | string or null | One of in_stock, out_of_stock. |
[].unitId | integer or null | |
[].taxClassId | integer or null | |
[].weight | number or null | |
[].length | number or null | |
[].width | number or null | |
[].height | number or null | |
[].taxonomyTermIds | array of integer | |
[].relatedProductIds | array of integer | The products shown as applicable products on the product page, in order. |
[].recommendedProductIds | array of integer | The merchant's picks for the product page's You May Also Like section, in order. Without picks, the page shows the closest matches. |
[].specTemplateId | integer or null | |
[].variableProductId | integer or null | |
[].thumbnail | string (uri) or null | |
[].gallery | array of string | |
[].createdAt | string (date-time) or null | |
[].updatedAt | string (date-time) or null | |
Example
foreach ($admin->products->all(['updatedSince' => $lastSyncedAt]) as $product) {
$erp->save($product);
$lastSyncedAt = $product['updatedAt'] ?? $lastSyncedAt;
}
Get a product
$admin->products->get(int $id): array
One product by its id, with every language's text. Scope: products:read.
Parameters
| Parameter |
Type |
|
$id |
int |
The product's id. |
Returns
An AdminProduct:
| Field | Type | Description |
|---|
id | integer | |
slug | string | |
title | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
shortDescription | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
description | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
sku | string or null | |
partNumber | string or null | |
type | string | One of physical, digital. |
downloadLimit | integer or null | |
documentUrl | string or null | |
status | boolean | True when published. |
isFeatured | boolean or null | |
noindex | boolean or null | |
price | number or null | |
salePrice | number or null | |
isSaleScheduled | boolean or null | |
saleStartsAt | string (date-time) or null | |
saleEndsAt | string (date-time) or null | |
wholesalePrice | number or null | |
manageStock | boolean or null | |
stockQuantity | integer or null | |
lowStockAmount | integer or null | |
stockStatus | string or null | One of in_stock, out_of_stock. |
unitId | integer or null | |
taxClassId | integer or null | |
weight | number or null | |
length | number or null | |
width | number or null | |
height | number or null | |
taxonomyTermIds | array of integer | |
relatedProductIds | array of integer | The products shown as applicable products on the product page, in order. |
recommendedProductIds | array of integer | The merchant's picks for the product page's You May Also Like section, in order. Without picks, the page shows the closest matches. |
specTemplateId | integer or null | |
variableProductId | integer or null | |
thumbnail | string (uri) or null | |
gallery | array of string | |
createdAt | string (date-time) or null | |
updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
429 | Rate limited. |
Create a product
$admin->products->create(array $input, string|false|null $idempotencyKey = null): array
A new product, written with the panel's rules and plan limits; hidden unless status is true. Images are https URLs or base64; one that can't be stored is skipped with a message in warnings. Scope: products:write.
Parameters
| Parameter |
Type |
|
$input |
array |
The product's fields, below. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. One is made for you when null. |
Request fields
| Field | Type | Description |
|---|
title required | string | The title in the shop's default language. At most 255 characters. |
translations | object (map) or null | Titles in the shop's other languages. |
shortDescription | string or null | In the default language. Bold, underline, strikethrough, overline and lists are kept. |
shortDescriptionTranslations | object (map) or null | |
description | string or null | In the default language. HTML is kept; plain text becomes paragraphs. |
descriptionTranslations | object (map) or null | |
taxonomyTermIds | array of integer or null | Category, brand, tag and other term ids, from GET /api/admin/v1/taxonomies/{taxonomy}/terms. At most 50 items. |
images | array of object or null | The first is the thumbnail. At most 6 items. |
images[].url | string (uri) or null | An https URL. Required without data. At most 2048 characters. |
images[].data | string or null | The image in base64, at most 5 MB. |
imageUrls | array of string (uri) or null | Older form of images. At most 6 items. |
sku | string or null | At most 100 characters. |
partNumber | string or null | At most 100 characters. |
price | number or null | At least 0. At most 99999999.99. |
salePrice | number or null | At least 0. At most 99999999.99. |
isSaleScheduled | boolean or null | |
saleStartsAt | string (date-time) or null | |
saleEndsAt | string (date-time) or null | |
wholesalePrice | number or null | At least 0. At most 99999999.99. |
manageStock | boolean or null | |
stockQuantity | integer or null | At least 0. |
stockStatus | string or null | One of in_stock, out_of_stock. |
lowStockAmount | integer or null | At least 0. |
unitId | integer or null | The unit it is sold in, from GET /api/admin/v1/units. |
taxClassId | integer or null | |
type | string | One of physical, digital. Default "physical". |
digitalUrl | string (uri) or null | Required for a digital product; a public https file URL. At most 2048 characters. |
downloadLimit | integer or null | At least 1. At most 1000. |
documentUrl | string (uri) or null | At most 2048 characters. |
status | boolean | Published. Defaults to false (hidden) when creating. |
isFeatured | boolean or null | |
noindex | boolean or null | |
weight | number or null | At least 0. At most 999999.99. |
length | number or null | At least 0. At most 999999.99. |
width | number or null | At least 0. At most 999999.99. |
height | number or null | At least 0. At most 999999.99. |
specTemplateId | integer or null | Plans with specification templates only. From GET /api/admin/v1/spec-templates. |
specs | object (map) or null | Specification values by property id, as GET /api/admin/v1/spec-templates/{template} lists the properties; a text property's string is its default-language value. |
relatedProductIds | array of integer or null | The shop's own products shown as applicable products on the product page, in order; replaces the current list. |
recommendedProductIds | array of integer or null | Up to 8 of the shop's own products for the product page's You May Also Like section, in order, shown in place of the closest matches; replaces the current list. At most 8 items. |
wholesalePrices | array of object or null | B2B plans only. |
wholesalePrices[].wholesaleMembershipId required | integer | |
wholesalePrices[].price | number or null | At least 0. At most 99999999.99. |
quantityTiers | array of object or null | B2B plans only. At most 20 items. |
quantityTiers[].minQuantity required | integer | At least 1. At most 1000000. |
quantityTiers[].price required | number | At least 0. At most 99999999.99. |
Returns
A ProductWriteResult array: the new AdminProduct in data, and the images that couldn't be stored in warnings.
| Field | Type | Description |
|---|
data | AdminProduct | |
data.id | integer | |
data.slug | string | |
data.title | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.shortDescription | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.description | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.sku | string or null | |
data.partNumber | string or null | |
data.type | string | One of physical, digital. |
data.downloadLimit | integer or null | |
data.documentUrl | string or null | |
data.status | boolean | True when published. |
data.isFeatured | boolean or null | |
data.noindex | boolean or null | |
data.price | number or null | |
data.salePrice | number or null | |
data.isSaleScheduled | boolean or null | |
data.saleStartsAt | string (date-time) or null | |
data.saleEndsAt | string (date-time) or null | |
data.wholesalePrice | number or null | |
data.manageStock | boolean or null | |
data.stockQuantity | integer or null | |
data.lowStockAmount | integer or null | |
data.stockStatus | string or null | One of in_stock, out_of_stock. |
data.unitId | integer or null | |
data.taxClassId | integer or null | |
data.weight | number or null | |
data.length | number or null | |
data.width | number or null | |
data.height | number or null | |
data.taxonomyTermIds | array of integer | |
data.relatedProductIds | array of integer | The products shown as applicable products on the product page, in order. |
data.recommendedProductIds | array of integer | The merchant's picks for the product page's You May Also Like section, in order. Without picks, the page shows the closest matches. |
data.specTemplateId | integer or null | |
data.variableProductId | integer or null | |
data.thumbnail | string (uri) or null | |
data.gallery | array of string | |
data.createdAt | string (date-time) or null | |
data.updatedAt | string (date-time) or null | |
warnings | array of string | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | The key's refusals (see AdminForbidden), or plan_limit_reached. |
409 | idempotency_in_progress: a request with this Idempotency-Key is still running. Retry with the same key after Retry-After seconds; the first answer is then replayed. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
$result = $admin->products->create([
'title' => 'Ceramic mug',
'translations' => ['ar' => 'كوب سيراميك'],
'sku' => 'MUG-01',
'price' => 24,
'salePrice' => 19.5,
'manageStock' => true,
'stockQuantity' => 40,
'taxonomyTermIds' => [4],
'imageUrls' => ['https://cdn.example.com/mug.jpg'],
]);
$product = $result['data'];
Update a product
$admin->products->update(
int $id,
array $changes,
DateTimeInterface|string|null $unmodifiedSince = null,
string|false|null $idempotencyKey = null,
): array
Changes only the fields sent. Images are added after the current ones, or replace them with imageMode replace. Scope: products:write.
Parameters
| Parameter |
Type |
|
$id |
int |
The product's id. |
$changes |
array |
The fields to change, below. |
$unmodifiedSince |
DateTimeInterface|string|null |
The product's updatedAt as you read it. If it changed since, the change is refused with 409 edit_conflict. |
$idempotencyKey |
string|false|null |
Your own Idempotency-Key, or false for none. |
Request fields
| Field | Type | Description |
|---|
title | string | The title in the shop's default language. At most 255 characters. |
translations | object (map) or null | Titles in the shop's other languages. |
shortDescription | string or null | In the default language. Bold, underline, strikethrough, overline and lists are kept. |
shortDescriptionTranslations | object (map) or null | |
description | string or null | In the default language. HTML is kept; plain text becomes paragraphs. |
descriptionTranslations | object (map) or null | |
taxonomyTermIds | array of integer or null | Category, brand, tag and other term ids, from GET /api/admin/v1/taxonomies/{taxonomy}/terms. At most 50 items. |
images | array of object or null | The first is the thumbnail. At most 6 items. |
images[].url | string (uri) or null | An https URL. Required without data. At most 2048 characters. |
images[].data | string or null | The image in base64, at most 5 MB. |
imageUrls | array of string (uri) or null | Older form of images. At most 6 items. |
sku | string or null | At most 100 characters. |
partNumber | string or null | At most 100 characters. |
price | number or null | At least 0. At most 99999999.99. |
salePrice | number or null | At least 0. At most 99999999.99. |
isSaleScheduled | boolean or null | |
saleStartsAt | string (date-time) or null | |
saleEndsAt | string (date-time) or null | |
wholesalePrice | number or null | At least 0. At most 99999999.99. |
manageStock | boolean or null | |
stockQuantity | integer or null | At least 0. |
stockStatus | string or null | One of in_stock, out_of_stock. |
lowStockAmount | integer or null | At least 0. |
unitId | integer or null | The unit it is sold in, from GET /api/admin/v1/units. |
taxClassId | integer or null | |
type | string | One of physical, digital. Default "physical". |
digitalUrl | string (uri) or null | Required for a digital product; a public https file URL. At most 2048 characters. |
downloadLimit | integer or null | At least 1. At most 1000. |
documentUrl | string (uri) or null | At most 2048 characters. |
status | boolean | Published. Defaults to false (hidden) when creating. |
isFeatured | boolean or null | |
noindex | boolean or null | |
weight | number or null | At least 0. At most 999999.99. |
length | number or null | At least 0. At most 999999.99. |
width | number or null | At least 0. At most 999999.99. |
height | number or null | At least 0. At most 999999.99. |
specTemplateId | integer or null | Plans with specification templates only. From GET /api/admin/v1/spec-templates. |
specs | object (map) or null | Specification values by property id, as GET /api/admin/v1/spec-templates/{template} lists the properties; a text property's string is its default-language value. |
relatedProductIds | array of integer or null | The shop's own products shown as applicable products on the product page, in order; replaces the current list. |
recommendedProductIds | array of integer or null | Up to 8 of the shop's own products for the product page's You May Also Like section, in order, shown in place of the closest matches; replaces the current list. At most 8 items. |
wholesalePrices | array of object or null | B2B plans only. |
wholesalePrices[].wholesaleMembershipId required | integer | |
wholesalePrices[].price | number or null | At least 0. At most 99999999.99. |
quantityTiers | array of object or null | B2B plans only. At most 20 items. |
quantityTiers[].minQuantity required | integer | At least 1. At most 1000000. |
quantityTiers[].price required | number | At least 0. At most 99999999.99. |
imageMode | string or null | One of add, replace. Default "add". |
updatedAt | string (date-time) or null | The record's updatedAt as you read it; a newer record gets 409 edit_conflict. |
Returns
A ProductWriteResult array: the changed AdminProduct in data, and the images that couldn't be stored in warnings.
| Field | Type | Description |
|---|
data | AdminProduct | |
data.id | integer | |
data.slug | string | |
data.title | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.shortDescription | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.description | LocalizedText | Text by language code. An empty value is returned as an empty array []. |
data.sku | string or null | |
data.partNumber | string or null | |
data.type | string | One of physical, digital. |
data.downloadLimit | integer or null | |
data.documentUrl | string or null | |
data.status | boolean | True when published. |
data.isFeatured | boolean or null | |
data.noindex | boolean or null | |
data.price | number or null | |
data.salePrice | number or null | |
data.isSaleScheduled | boolean or null | |
data.saleStartsAt | string (date-time) or null | |
data.saleEndsAt | string (date-time) or null | |
data.wholesalePrice | number or null | |
data.manageStock | boolean or null | |
data.stockQuantity | integer or null | |
data.lowStockAmount | integer or null | |
data.stockStatus | string or null | One of in_stock, out_of_stock. |
data.unitId | integer or null | |
data.taxClassId | integer or null | |
data.weight | number or null | |
data.length | number or null | |
data.width | number or null | |
data.height | number or null | |
data.taxonomyTermIds | array of integer | |
data.relatedProductIds | array of integer | The products shown as applicable products on the product page, in order. |
data.recommendedProductIds | array of integer | The merchant's picks for the product page's You May Also Like section, in order. Without picks, the page shows the closest matches. |
data.specTemplateId | integer or null | |
data.variableProductId | integer or null | |
data.thumbnail | string (uri) or null | |
data.gallery | array of string | |
data.createdAt | string (date-time) or null | |
data.updatedAt | string (date-time) or null | |
warnings | array of string | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
409 | edit_conflict (the record changed after If-Unmodified-Since or updatedAt), or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
$product = $admin->products->get(12);
$admin->products->update(12, ['price' => 19.5], unmodifiedSince: $product['updatedAt']);
Delete a product
$admin->products->delete(int $id): void
Deletes a product. Scope: products:write.
Parameters
| Parameter |
Type |
|
$id |
int |
The product's id. |
Returns
Nothing.
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
409 | idempotency_in_progress: a request with this Idempotency-Key is still running. Retry with the same key after Retry-After seconds; the first answer is then replayed. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |