Product Variant API
Use the REST API to interact with EverShop product variant.
Create a variant group
Use this endpoint to create a variant group.
| Field Name | Field Type | Required |
|---|---|---|
| attribute_codes | array of string | Yes |
| attribute_group_id | integer or string | No |
- cURL
- JavaScript
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
--data-raw '<JSON DATA>'
https://<your domain>/api/variantGroups
fetch('https://<your domain>/api/variantGroups', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer <admin JWT token>'
},
body: <JSON DATA>
})
.then(response => response.json())
.then(data => {
if(data.error) {
// Handle the error
} else {
// Handle the data
}
})
.catch(error => {
// Handle the error
});
{
"data": {
"variant_group_id": 8,
"uuid": "99a7b39ca63211edb46b60d819134f39",
"attribute_group_id": 1,
"attribute_one": 4,
"attribute_two": 2,
"attribute_three": 3,
"attribute_four": 5,
"attribute_five": 6
}
}
Search variant candidates
Use this endpoint to find products that can still be added to a variant group. It backs the product picker in the admin variant editor.
The result set is deliberately narrow: only products whose variant_group_id is null are returned — a product already belonging to a group is never a candidate. There is no status or visibility filter, and no pagination; every unassigned product is returned in one response.
- cURL
- JavaScript
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
https://<your domain>/api/variants?keyword=blue
fetch('https://<your domain>/api/variants?keyword=blue', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer <admin JWT token>'
}
})
.then(response => response.json())
.then(data => {
if(data.error) {
// Handle the error
} else {
// Handle the data
}
})
.catch(error => {
// Handle the error
});
{
"data": {
"variants": [
{
"variant_product_id": 281,
"sku": "TSHIRT-BLUE-M",
"name": "Blue T-Shirt (M)",
"status": 1,
"price": 45,
"qty": 123,
"gallery": "/assets/catalog/281/blue-back.jpg",
"image": {},
"images": [
{
"url": "/assets/catalog/281/blue-front.jpg",
"path": "/assets/catalog/281/blue-front.jpg"
},
{
"url": "/assets/catalog/281/blue-back.jpg",
"path": "/assets/catalog/281/blue-back.jpg"
}
],
"attributes": [
{
"product_attribute_value_index_id": 902,
"uuid": "3f2a1b7c-9d4e-4a10-8b52-6c7d8e9f0a1b",
"product_id": 281,
"attribute_id": 2,
"option_id": 5,
"option_text": "Blue"
},
{
"product_attribute_value_index_id": 903,
"uuid": "4a3b2c8d-0e5f-4b21-9c63-7d8e9f0a1b2c",
"product_id": 281,
"attribute_id": 3,
"option_id": 6,
"option_text": "M"
}
]
}
]
}
}
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| keyword | string | No | Case-sensitive LIKE '%keyword%' match against the product name or sku. Omit it to list every unassigned product. |
Response Fields
| Field | Description |
|---|---|
variant_product_id | The numeric product_id, renamed. This is the value to send as product_id when adding the product to a group. |
images | The product's main image followed by each gallery image, each an object carrying url and path. Rows are collapsed per product, so a product with three gallery images produces one entry with four images, not four entries. |
attributes | Raw product_attribute_value_index rows for the product — one per attribute that has a value, carrying attribute_id, option_id and option_text. |
image is always an empty objectThe handler builds image from a column name that the query aliases away, so the value is always undefined and serializes as {}. Use images[0] for the main image; the raw gallery column also leaks through as a top-level gallery string.
Add variant item
Use this endpoint to add a product to a variant group.
| Field Name | Field Type | Required |
|---|---|---|
| product_id | string | Yes |
- cURL
- JavaScript
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
--data-raw '<JSON DATA>'
https://<your domain>/api/variantGroups/{id}/items
fetch('https://<your domain>/api/variantGroups/{id}/items', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer <admin JWT token>'
},
body: <JSON DATA>
})
.then(response => response.json())
.then(data => {
if(data.error) {
// Handle the error
} else {
// Handle the data
}
})
.catch(error => {
// Handle the error
});
{
"data": {
"id": "r57hfgh5656",
"attributes": [
{
"attribute_id": 2,
"attribute_code": "color",
"option_id": 5
},
{
"attribute_id": 3,
"attribute_code": "size",
"option_id": 6
}
],
"product": {
"product_id": 281,
"uuid": "99a7b39ca63211edb46b60d819134f39",
"variant_group_id": 2,
"visibility": 1,
"group_id": 4,
"image": null,
"sku": "skuUpdated",
"price": 45,
"qty": 123,
"weight": 17,
"manage_stock": 1,
"stock_availability": 1,
"tax_class": null,
"status": 1,
"created_at": "2023-02-07 00:01:46",
"updated_at": "2023-02-07 00:01:46"
}
}
}
Unlink Variant
Removes a product from its variant group. The product is not deleted, but it will no longer be associated with the variant group.
Despite the /api/variants/{id} route shape, the handler reads request.body.id — the
:id path segment is ignored. Send the product id in the request body; a request that
relies on the path parameter alone unlinks nothing.
- cURL
- JavaScript
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
https://<your domain>/api/variants/{id}
fetch('https://<your domain>/api/variants/{id}', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer <admin JWT token>'
}
})
.then(response => response.json())
.then(data => {
if(data.error) {
// Handle the error
} else {
// Handle the data
}
})
.catch(error => {
// Handle the error
});
{
"data": {}
}