Skip to main content

Bulk Catalog Sync

You can define or update up to 50 products in a single request using this service. Each product is processed independently, so one invalid product does not prevent the others from being written.

When to use this

Use Create Product when you add a single product. Use this service when you need to define or refresh many products at once, for example when you first connect your catalog or when you send your daily additions.

Request​

curl --location --request POST '<BASE_URL>/merchant-products/catalog/sync' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <jwtToken>' \
--data '{
"products": [
{
"productCode": "PRD-001",
"name": "iPhone 15 Pro"
},
{
"productCode": "PRD-002",
"name": "MacBook Pro 14",
"brand": "Apple",
"price": 89999.00
}
]
}'

Request Body​

ParameterTypeRequired
Description
productsArrayYesBetween 1 and 50 products. Each entry is described below.

Product Object​

productCode is always required. name is required only when the product does not exist yet; for a product that already exists you may omit it.

ParameterTypeRequired
Description
productCodeStringYesUnique product code in your system. Max 255 characters.
nameStringOn createProduct name. Max 255 characters. Required when the product does not exist yet.
brandStringNoBrand name. Max 255 characters. On create, a general brand is assigned when you omit it.
warrantyYearIntegerNoManufacturer warranty in years, between 2 and 10. On create, defaults to 2 when you omit it.
setupRequiredBooleanNoWhether the product requires setup. On create, defaults to false when you omit it.
modelStringNoProduct model. Max 255 characters.
descriptionStringNoProduct description. Max 1000 characters.
priceDecimalNoProduct price in TRY. A value of 0 counts as "not provided".
mainCategoryNameStringNoMain category name. Max 255 characters. Must be sent together with the two fields below.
categoryNameStringNoCategory name. Max 255 characters. Must be sent together with the two category fields.
subCategoryNameStringNoSub category name. Max 255 characters. Must be sent together with the two category fields.
updateMaskArrayNoNames of the fields to write for this product. Changes how the product is updated — see below.
Defaults apply on create only

brand, warrantyYear and setupRequired get a default value only when the product is being created. When you update an existing product and omit a field, its current value is kept — nothing is reset to a default.

Response​

200 OK
{
"summary": { "total": 2, "succeeded": 2, "failed": 0 },
"results": [
{ "index": 0, "productCode": "PRD-001", "outcome": "CREATED", "merchantProductId": 1234 },
{ "index": 1, "productCode": "PRD-002", "outcome": "UPDATED", "merchantProductId": 1235 }
]
}
207 Multi-Status
{
"summary": { "total": 2, "succeeded": 1, "failed": 1 },
"results": [
{ "index": 0, "productCode": "PRD-001", "outcome": "CREATED", "merchantProductId": 1234 },
{
"index": 1,
"productCode": "PRD-002",
"outcome": "FAILED",
"errorCode": "REQUIRED_FIELD_MISSING",
"field": "name",
"retryable": false,
"message": "name is required for a new product"
}
]
}

Status Codes​

Code
Meaning
200Every product was written successfully.
207Some products were written and some failed. Check results.
400No product was written. The body still carries results unless the request itself was malformed — read it before deciding what to resend.
429Too many concurrent requests for your account. Retry after a moment.
A 400 is not always a rejected request

When every product in the batch fails, the status is 400 but the body still reports each product separately, including any with retryable: true. Do not treat 400 as "nothing to see" and discard the batch — read results and resend only the retryable products.

Outcomes​

Outcome
Meaning
CREATEDThe product did not exist and was created.
UPDATEDThe product already existed and its fields were updated.
REVIVEDThe product had been deleted earlier and is active again. No action needed.
FAILEDThe product was not written. See errorCode, field and retryable.

Error Codes​

errorCoderetryable
Meaning
REQUIRED_FIELD_MISSINGfalseA field required for a new product is missing. field names it.
FIELD_VALIDATION_FAILEDfalseA field breaks a rule, for example it is too long or out of range.
CATEGORY_FIELDS_INCOMPLETEfalseSome but not all three category names were sent.
DELETED_IN_PANELfalseThe product was removed from the panel and is not revived automatically.
TRANSIENT_ERRORtrueA temporary problem. The same product can be sent again unchanged.
UNEXPECTED_ERRORfalseAn unexpected problem. Sending the same data again will not help.
Read the retryable field

When retryable is false, sending the same product again produces the same failure. The data has to be corrected first. Only retry items where retryable is true.

Behaviour Notes​

One failed product does not affect the others​

Each product is written on its own. If one product in a batch of 50 is invalid, the other 49 are still written and only the invalid one is reported as FAILED.

Category names travel together​

Send all three of mainCategoryName, categoryName and subCategoryName, or none of them. Sending only one or two returns CATEGORY_FIELDS_INCOMPLETE for that product.

When you send none of them, a product being created is placed in a general category and works normally. A product being updated keeps the category it already has — omitting the category names never moves a product to the general category.

updateMask​

By default, the fields you populated are written and nothing is ever cleared. This covers almost every case: send the product, and what you sent is what gets stored.

updateMask exists for the one thing the default cannot do — clearing a field. List the field in updateMask and leave it out of the product:

{
"products": [
{ "productCode": "PRD-001", "name": "iPhone 15 Pro", "price": 54999.00 },
{ "productCode": "PRD-002", "updateMask": ["model"] },
{ "productCode": "PRD-003", "name": "MacBook Air", "updateMask": ["name", "model"] }
]
}

In this batch:

  • PRD-001 has no mask, so its name and price are written and nothing is cleared.
  • PRD-002 has ["model"] and sends no model, so its model is cleared. Every other field is untouched.
  • PRD-003 has ["name", "model"] and sends name, so its name is written and its model is cleared. Its price is untouched.

Three rules matter here:

  1. updateMask belongs to one product, not the request. Each product carries its own; a mask on one product never affects its neighbours in the same batch.
  2. The mask replaces the default, it does not add to it. When a product has a mask, only the listed fields are written. Any other field you populated on that product is ignored. If you send {"productCode": "X", "name": "New", "updateMask": ["model"]}, the name is not written.
  3. updateMask only applies to products that already exist. A product being created always takes every field you populated.

Only model, description and price can be cleared. The other fields are mandatory in our system, so listing one in updateMask without a value returns FIELD_VALIDATION_FAILED for that product.

A mask entry that is not one of the product fields in the table above (name, brand, warrantyYear, setupRequired, model, description, price, mainCategoryName, categoryName, subCategoryName) fails that product with FIELD_VALIDATION_FAILED and field: "updateMask". The rest of the batch is unaffected. Note that productCode identifies the product and cannot appear in a mask.

Empty values​

An empty string and a price of 0 both count as "not provided":

  • Without a mask they leave the existing value alone. Sending "model": "" never clears a stored model.
  • With a mask the field is listed for writing and has no value, so it is cleared. {"productCode": "X", "model": "", "updateMask": ["model"]} clears the model, exactly like omitting model would.

The same product code twice in one request​

If a product code appears more than once in the same products list, the entries are processed in order against the same product row, and each one writes the fields it carries. The result is a merge, not a replacement: a field set by the first entry survives unless a later entry writes it too. Only one product row exists at the end. Sending a product code once per request avoids this entirely.

Concurrency​

At most 2 requests per account are processed at the same time. Further requests receive 429 and can be retried after a short wait.