Merchant API: products
About this article
This article explains how to create, update, retrieve, and delete products via the Merchant API, which fields the product payload accepts, and how to structure a (grand)parent-child relationship.
Table of contents
Best practices: bulk your requests
Introduction
The Merchant API gives you full control over your product catalog on ChannelEngine. Use it to add products, keep product content up to date, and build (grand)parent-child hierarchies.
Stock and price are offer data and are managed separately through the offers endpoints. Where this article refers to stock or price fields, it does so in the context of product management, not offer management. The content vs. offer data section explains the split.
Overview
Refer to the flowchart below to visualize how product data travels between your system, ChannelEngine, and the connected channels.
Requirements
- A ChannelEngine account with a Merchant API key. Find it under Settings, Merchant API keys.
- Your base API URL:
https://{your-subdomain}.channelengine.net/api. - A unique Merchant product number for every product, which is usually your product SKU. This is the primary key ChannelEngine uses to match incoming data to an existing product.
Pass your API key as a query parameter on every request:
GET https://{your-subdomain}.channelengine.net/api/v2/products?apikey=your-merchant-api-keyAvailable API endpoints
The endpoints below cover product content. For stock and price, use the offers endpoints instead.
-
Create or fully update products:
POST /v2/products
Use this endpoint to add products or to perform a full update on existing ones. This is a purge-and-replace operation. -
Retrieve multiple products:
GET /v2/products
Use this endpoint to retrieve a paginated list of products, with optional filters. -
Retrieve a single product:
GET /v2/products/{merchantProductNo}
Use this endpoint to retrieve one product by its Merchant product number. -
Update standard fields in bulk:
PATCH /v2/products
Use this endpoint to update selected fields for one or more products in a single call. -
Update standard fields for one product:
PATCH /v2/products/{merchantProductNo}
Use this endpoint to update selected fields for a single product, using JSON Patch operations. -
Deactivate a single product:
DELETE /v2/products/{merchantProductNo}
Use this endpoint to deactivate one product and stop exporting it to channels. -
Deactivate multiple products:
POST /v2/products/bulkdelete
Use this endpoint to deactivate several products in one call.
Best practices: bulk your requests
The product endpoints are designed to handle large payloads. Batch your products into a single request rather than sending one product at a time. This reduces the number of API calls, lowers the risk of hitting rate limits, and lets ChannelEngine process your catalog consistently.
The recommended batch size for POST /v2/products is up to 5,000 products per call. If your catalog is larger, split it across multiple calls. E.g.: a catalog of 12,000 products becomes three calls: products 1 to 5,000, products 5,001 to 10,000, and products 10,001 to 12,000.
The same principle applies to all other write endpoints: PATCH, extra data bulk, bulk delete, and product bundles. Always prefer one large call over many small ones.
PATCH call.Create and update products
POST /v2/products: create or fully update
Use POST /v2/products to add products to ChannelEngine or to fully update existing ones. The endpoint is purge and replace: every call overwrites the entire product record with the data you send. Any field you omit is cleared.
POST replaces the full product, always include every field you want to keep, not only the ones you are changing. If you send just Name and Price, all other fields, such as description, images, and EAN, are cleared.There are two exceptions to the purge-and-replace rule:
- Custom fields (extra data) are never overwritten by
POST /v2/products. Manage them via the extra data endpoints. - You can protect stock and price from being overwritten with the
ignoreStockandignorePricequery parameters.
Query parameters
| Parameter | Type | Default | Description |
ignoreStock |
boolean | false | When set to true, the Stock field in the payload is ignored and the existing stock value is preserved. |
ignorePrice |
boolean | false | When set to true, the Price field in the payload is ignored and the existing price value is preserved. |
These parameters are useful when your product feed comes from a content system, such as a PIM or ERP, that does not manage live stock or pricing. Pass ?ignoreStock=true&ignorePrice=true to sync content-only updates without touching offer data.
The only required field is MerchantProductNo, your unique internal product identifier.
Example request
The example below shows the most common fields. For content-only syncs, pass ?ignoreStock=true&ignorePrice=true and manage the offer fields via the offers endpoints.
[
{
"MerchantProductNo": "SKU-001",
"Name": "Blue Running Shoe",
"Description": "Lightweight running shoe for everyday training.",
"Brand": "AcmeSport",
"Ean": "1234567890123",
"ManufacturerProductNumber": "MFR-RS-001",
"Size": "42",
"Color": "Blue",
"Price": 79.99,
"Stock": 50,
"MinPrice": 69.99,
"MaxPrice": 89.99,
"MSRP": 99.99,
"PurchasePrice": 42.00,
"VatRateType": "STANDARD",
"ShippingCost": 4.95,
"ShippingTime": "1-3 business days",
"Url": "https://www.example.com/products/sku-001",
"ImageUrl": "https://cdn.example.com/sku001.jpg",
"ExtraImageUrl1": "https://cdn.example.com/sku001-angle.jpg",
"CategoryTrail": "Sports > Shoes > Running",
"IsFrozen": false,
"ParentMerchantProductNo": "SKU-RUNNING-42",
"ParentMerchantProductNo2": "SKU-RUNNING"
}
]Product fields
| Field | Type | Notes |
MerchantProductNo (required) |
string | Your unique SKU or other internal product identifier. Cannot be reused once the product is deleted. |
Name |
string | Product title shown on channels. |
Description |
string | Product description. Supports a limited set of HTML tags, such as div, p, ul, li, b, and i. |
Brand |
string | Brand name. |
Ean |
string | EAN or GTIN. Required by most channels. |
ManufacturerProductNumber |
string | The manufacturer's own article number, also called the vendor product number. |
Size, Color
|
string | Variation values. E.g.: 'M' or 'White'. |
Price |
decimal | Regular selling price. Offer data. Use ?ignorePrice=true to prevent it from overwriting an existing price. |
Stock |
integer | Available quantity. Offer data. Use ?ignoreStock=true to prevent it from overwriting existing stock. |
MinPrice, MaxPrice
|
decimal | Floor and ceiling prices required for price mapping and repricing. ChannelEngine never goes below MinPrice or above MaxPrice. |
MSRP |
decimal | Manufacturer's suggested retail price. A reference price, used by some channels for strikethrough pricing. |
PurchasePrice |
decimal | Your cost price. Used for margin calculations on ChannelEngine and not exported to channels. |
VatRateType |
enum | Applicable VAT rate. I.e.: STANDARD, REDUCED, SUPER_REDUCED, or EXEMPT. |
ShippingCost |
decimal | Shipping cost for the product. Used by some channels to calculate the total price. |
ShippingTime |
string | Free-text delivery promise. E.g.: '1-3 business days'. |
Url |
string (URL) | Link to the product page in your own webstore. |
ImageUrl |
string (URL) | Link to the main product image. |
ExtraImageUrl1 to ExtraImageUrl9
|
string (URL) | Up to nine additional images. Channels typically display them in the order provided. |
CategoryTrail |
string | Category path. E.g.: 'Electronics > Audio > Headphones'. |
IsFrozen |
boolean | When set to true, ChannelEngine exports a stock of 0 to all connected channels. To toggle freeze on an existing product, use the dedicated freeze endpoint. |
ParentMerchantProductNo |
string | Set on a child to link it to its parent. See product hierarchy. |
ParentMerchantProductNo2 |
string | Set on a parent to point to its grandparent. Creates the third level of the hierarchy. |
ExtraData |
array | Custom fields as key-value pairs. Not affected by purge and replace. |
PATCH /v2/products: update standard fields
Use the PATCH endpoints when you only need to update one or a few standard ChannelEngine fields. Unlike POST, PATCH leaves every field you do not specify unchanged.
-
PATCH /v2/products: update fields for one or more products in a single call. Define which fields to update in thePropertiesToUpdatearray. -
PATCH /v2/products/{merchantProductNo}: update one or more fields for a single product, using JSON Patch format.
Update the name and description for several products at once:
{
"PropertiesToUpdate": ["name", "description"],
"MerchantProductRequestModels": [
{
"MerchantProductNo": "SKU-001",
"Name": "Blue Running Shoe",
"Description": "Updated description for the running shoe."
},
{
"MerchantProductNo": "SKU-002",
"Name": "Red Training Shoe",
"Description": "Updated description for the training shoe."
}
]
}Update fields on a single product with JSON Patch operations. The supported operations are replace and add, and you can include several operations in the same array:
[
{
"op": "replace",
"path": "ShippingTime",
"value": "2-4 business days"
}
]PATCH /v2/products or PATCH /v2/products/{merchantProductNo}. Use the dedicated extra data endpoints instead. Check out Merchant API: custom fields.
Retrieve products
Use the GET endpoints to verify what ChannelEngine has stored, to check a product's status, or to read current field values.
GET /v2/products: retrieve multiple products
Use this endpoint to retrieve your full catalog or to filter down to a specific set of products. The response is paginated.
| Parameter | Type | Description |
Search |
string | Free-text search across name, Merchant product number, EAN, and brand. Applied after the other filters. |
EanList |
array | Filter by one or more EANs. |
MerchantProductNoList |
array | Filter by one or more Merchant product numbers. |
PageSize |
integer | Number of products per page. If you do not provide it, all products are returned. |
Page |
integer | Page number to retrieve. |
The response includes Count, TotalCount, ItemsPerPage, and a Content array. Each product in Content mirrors the request model, plus an IsActive flag that shows whether the product is still active, and an IsFrozen flag that shows whether it is frozen.
After a POST or PATCH call, use GET /v2/products?MerchantProductNoList=SKU-001,SKU-002 to confirm the values were stored as intended. This is especially useful during onboarding.
GET /v2/products/{merchantProductNo}: retrieve a single product
Use this endpoint when you know exactly which product you need to retrieve. Pass the Merchant product number as a path parameter. The response is a single product object, not a paginated collection.
GET /v2/products/SKU-001
Content vs. offer data
On ChannelEngine, product data is split into two categories. Understanding the split helps you decide which fields belong in your product payload and which belong in your offer sync.
| Content data | Offer data |
| Descriptive attributes that generally do not change per channel or country: name, description, brand, EAN or GTIN, images, category trail, manufacturer product number, size, and color. | Commercial attributes that can vary per channel, country, or seller agreement: price, stock, shipping time and cost, minimum and maximum price, VAT rate type, and MSRP. |
For most merchants, the Price field in the product payload is a single default price. If you sell in several markets with different prices, do not create separate product fields for them. Add them as custom fields instead, using keys such as Price_NL and Price_DE, and map those keys to the relevant channel's price configuration on ChannelEngine.
Product hierarchy
ChannelEngine supports a three-level product hierarchy: grandparent, parent, and child. This structure maps to the way most marketplaces group variations. E.g.: a shoe model (grandparent) available in several sizes (parent) and colors (child).
Two fields control the hierarchy:
-
ParentMerchantProductNo: set on a child to point to its parent. -
ParentMerchantProductNo2: set on a parent to point to its grandparent. This creates the third level.
The sellable unit is always the child. Grandparent and parent records are grouping containers: they typically carry no stock or individual pricing, but they hold the shared content that channels inherit.
Set up the structure
Send all levels in the same POST /v2/products payload, in order: grandparent first, then the parents, then the children. This makes sure the relationships are set up correctly.
- Create the grandparent. Do not set
ParentMerchantProductNoorParentMerchantProductNo2. Include the shared content: name, description, brand, and main image. - Create the parents. Set
ParentMerchantProductNo2to the Merchant product number of the grandparent. Include any variation-level content. - Create the children. Set
ParentMerchantProductNoto the Merchant product number of the parent. Include the EAN and the variation attributes, such as color and size.
[
{
"MerchantProductNo": "TSHIRT-CLASSIC",
"Name": "Classic Crew T-Shirt",
"Brand": "AcmeWear",
"Description": "Our bestselling everyday t-shirt."
},
{
"MerchantProductNo": "TSHIRT-CLASSIC-M",
"Name": "Classic Crew T-Shirt, size M",
"ParentMerchantProductNo2": "TSHIRT-CLASSIC"
},
{
"MerchantProductNo": "TSHIRT-CLASSIC-M-WHT",
"Name": "Classic Crew T-Shirt, size M, white",
"ParentMerchantProductNo": "TSHIRT-CLASSIC-M",
"Ean": "1234567890123",
"Color": "White",
"Size": "M"
},
{
"MerchantProductNo": "TSHIRT-CLASSIC-M-BLK",
"Name": "Classic Crew T-Shirt, size M, black",
"ParentMerchantProductNo": "TSHIRT-CLASSIC-M",
"Ean": "1234567890124",
"Color": "Black",
"Size": "M"
}
]Not every catalog needs three levels. For a simple size and color variation structure, use ParentMerchantProductNo to link children directly to a parent. Add the grandparent level only when the channel requires a higher grouping layer. To learn more, check out ChannelEngine: parent-child relationships.
Delete products
Delete a single product via DELETE /v2/products/{merchantProductNo}, or several at once via POST /v2/products/bulkdelete.
When you delete a product, ChannelEngine marks it as inactive and stops exporting it on the next sync. Channels delist the product according to their own processing cycle, so there can be a delay.
Note that ChannelEngine does not fully remove a product record, because the product can still be referenced in historical orders. Instead, it only deactivates the product.
You can re-activate the product by adding it via POST /v2/products with the same Merchant product number. However, only do so if you re-activate the same product. Do not assign that Merchant product number to a different product.
Why zeroing stock first is safer
In most cases, set the product's stock to 0 via the offers endpoint first, and delete the product only if you no longer need it on ChannelEngine:
- Setting stock to 0 triggers an immediate offer update to all channels, so the product becomes unavailable for purchase right away.
- Deletion removes the product from the sync entirely, and delisting can take longer depending on the channel.
- Zeroing stock is reversible. Deletion is not, and the Merchant product number cannot be reused.
If the situation is temporary, freeze the product instead. Check out ChannelEngine: freeze products.
Common issues
| Issue | Cause | Solution |
| Product fields are empty after an update. | A partial payload was sent via POST /v2/products, which is purge and replace. |
Send the complete payload, or use PATCH for partial updates. |
| Stock or price is overwritten by a content sync. | The payload includes Stock or Price values from a system that does not manage them. |
Pass ?ignoreStock=true&ignorePrice=true and manage offers separately. |
| Custom fields disappear from the product. | Custom fields were expected to be managed through the product payload. | Custom fields are not affected by POST. Check whether they were removed via the extra data endpoints. |
| The hierarchy is not nested correctly. |
ParentMerchantProductNo2 was set on the grandparent instead of the parent. |
Set ParentMerchantProductNo2 on the parent record, pointing to the grandparent. |
| Requests are throttled. | One API call is sent per product. | Batch products into fewer, larger calls. |
| A deleted product cannot be recreated with the same SKU. | Deletion deactivates the record and reserves the Merchant product number. | Use a new Merchant product number, or freeze products instead of deleting them. |
Next steps
- Merchant API: custom fields: add product data that falls outside ChannelEngine's default attributes.
- Merchant API: product bundles: sell two or more products together at one price.
- Merchant API: rate limits and Merchant API: best practices.
Comments
0 comments
Article is closed for comments.