Version: 1.0Updated on: September 21st, 2026
This manual is for brand partners publishing images for their own products. It describes what a brand account can do and the limits that apply.
Read Manual for Brand Partners: Icecat Push-API (API-IN) first. This page continues it, and assumes you have it behind you: what a product is, how international and local content differ, how you authenticate, how you find brand, category, and locale IDs, and what a brand account may see. None of that is repeated here. What is here is everything specific to images.
The gallery is the one asset with a service of its own. It runs on a different address and answers in a different shape from the rest of the Brand API — so read the error section even if you have integrated other assets already.
The full field-by-field reference is the Icecat API swagger, tag product-gallery. This page explains what the swagger cannot: the order to do things in, which choice to make, what each choice costs you, and where integrations trip.
product-gallery
Every gallery request goes to https://api.icecat.biz, and every gallery path starts with /product-gallery/v1/. Put together, a real request URL looks like this:
https://api.icecat.biz
/product-gallery/v1/
https://api.icecat.biz/product-gallery/v1/imageshttps://api.icecat.biz/product-gallery/v1/products/{productId}/gallerieshttps://api.icecat.biz/product-gallery/v1/jobs/{jobId}
Elsewhere on this page the host is left out and paths are written from /product-gallery/v1/…. Always prefix them with https://api.icecat.biz.
/product-gallery/v1/…
The same header as everywhere else in the Brand API, on every request:
Api-Token: your_api_token
The token is not tied to an address, and it works on an account that has two-factor authentication switched on – no second factor is ever asked for on this path. That is what makes an unattended import possible under a protected account.
⚠️Older integrations authenticate with a session key instead, sent as the Access-Key header. That still works. If a request carries both, the session key is used and the token is ignored – so when you migrate an integration to the token, stop sending the key rather than sending both.
Access-Key
A refused credential and a missing one are told apart deliberately:
401 Missing credentials
401 Invalid API token
503 Auth service unavailable
Invalid API token is the only thing you are told. Whether the token never existed, was revoked, or belongs to an account that can no longer use it is deliberately not disclosed. Repeatedly sending a token that is not accepted gets your address throttled for a while, and the answer stays the same, so treat the first 401 as final rather than retrying in a loop.
401
Everything on this page uses placeholders in curly braces. This is what each one is and where it comes from:
{productId}
{taxonomyId}
0
{languageId}
{imageId}
{sourceId}
image.source.sourceId
{jobId}
POST …/batch
The product has to be yours. Every call below works on products of the brands your account is assigned to. Another brand’s product answers 403, whatever else is in the request.
403
You give Icecat a URL, and Icecat fetches the file. The picture has to be reachable from the public internet, not only from your own network.
Nothing here is tied to a particular tool or language. Every example on this page is written as the request itself — the method and path, the headers, the body — because that is what your side has to produce, whatever produces it: a script in any language, the REST connector your PIM or DAM already has, an integration platform, or a client you try things in by hand.
As a brand, you may look at and change the galleries of your own products. You may not delete anything — not an image, not a gallery. That is deliberate: to stop a picture from being distributed to channel partners, you deactivate it, and it stays recoverable. See section 11.
GET /product-gallery/v1/products/{productId}/galleries?view=FULL
GET /product-gallery/v1/products/{productId}/galleries/{taxonomyId}
GET /product-gallery/v1/images/{imageId}
POST /product-gallery/v1/images
PATCH /product-gallery/v1/images/{imageId}
POST /product-gallery/v1/images/{imageId}/copies
PATCH /product-gallery/v1/images
GET /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}
PATCH /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}
DELETE /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}
POST /product-gallery/v1/products/{productId}/batch
POST /product-gallery/v1/products/{productId}/batch-sync
GET /product-gallery/v1/jobs/{jobId}
Every field of every one of these is in the swagger, tag product-gallery. Section 16 covers every size and length limit, and section 12 covers the error messages you are most likely to see.
The API has five things in it. Once these are clear, everything else on this page is detail.
Product. Your product in Icecat, addressed by its Icecat product ID.
Gallery. A product does not have one gallery. A gallery is the pair (product, taxonomy):
A partner gallery gives one partner a set of pictures of their own. If you know that a particular retailer sells this product better with different pictures — lifestyle shots instead of packshots, a different first image, images in their house format — you publish those into their taxonomy, and only they receive them. Everyone else keeps the Icecat gallery. Section 8 covers this.
A gallery has no ID of its own. The two numbers are its address, and both are required on every write — there is no default. Pass 0 explicitly when you mean the Icecat gallery.
Image. One picture in one gallery, for one locale. languageId: 0 means INT — the picture is shown in every locale. languageId: 1 (English), 3 (French) and so on means it belongs to that locale alone.
languageId: 0
languageId: 1
3
The same picture published in English, French, and Dutch is three images — three entries you can change, deactivate, or reorder independently.
Source. The physical file behind an image. Icecat stores the file once; if you send a file it already holds, it reuses the stored one instead of storing it again. One file can therefore stand behind several images — the locales of this product, and any other product you add it to. The practical effect: a file you send twice costs nothing the second time.
Order. Where each picture stands. A gallery keeps two kinds of order side by side — one base order and, optionally, an order per locale. Section 6 covers it in full.
Add one international picture to the Icecat gallery of one of your products.
The request:
POST https://api.icecat.biz/product-gallery/v1/images
its headers:
Api-Token: your_api_tokenContent-Type: application/json
and its body:
{ "productId": {productId}, "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/front.jpg", "imageType": "ProductImage", "fileName": "your_product_front" }
productId, taxonomyId and languageId are required and have no defaults. Passing 0 for the last two is a decision you make explicitly, not something you can leave out.
productId
taxonomyId
languageId
Every later example on this page shows the method, the path and the body only. The headers are the same every time — Api-Token, and Content-Type: application/json on anything with a body.
Api-Token
Content-Type: application/json
The answer carries the picture that was created, the physical file behind it with all five sizes, and where it lives:
{ "productId": {productId}, "taxonomyId": 0, "image": { "imageId": {imageId}, "languageId": 0, "imageType": "ProductImage", "fileName": "your_product_front", "isActive": true, "isPrivate": false, "expiryDate": "", "expired": false, "source": { "sourceId": {sourceId}, "md5": "9f2c…", "width": 2973, "height": 2033, "isCropped": false, "origin": { "link": "…", "width": 2973, "height": 2033, "sizeBytes": 836052 }, "high": { "link": "…", "width": 2973, "height": 2033, "sizeBytes": 872039 }, "medium": { "link": "…", "width": 500, "height": 342, "sizeBytes": 51002 }, "low": { "link": "…", "width": 200, "height": 137, "sizeBytes": 9905 }, "thumbnail": { "link": "…", "width": 75, "height": 51, "sizeBytes": 2210 } } }, "metadata": { "serverDate": "2026-09-20", "allowedLanguageIds": [0, 1, 3, 4], "canDelete": false } }
Keep the imageId and the sourceId — the first addresses this picture in this locale, the second addresses the file itself, and you need both later: imageId to change or deactivate it, sourceId to place it in an order.
imageId
sourceId
Two things in metadata are worth reading every time:
metadata
allowedLanguageIds
canDelete
false
To verify what a product now holds, ask for the whole picture in one call:
GET https://api.icecat.biz/product-gallery/v1/products/{productId}/galleries?view=FULL
view=BASIC (the default) returns each gallery with a count only. view=FULL returns every gallery of the product with its images — already sorted — and its orders.
view=BASIC
view=FULL
A picture belongs to exactly one locale, and 0 (INT) is not “no locale” — it is “every locale”.
languageId: 4
A locale keeps the INT pictures and adds its own. Adding a German picture does not hide the international ones. If Germany should receive different pictures rather than more pictures, publish the German ones and deactivate the international ones for that gallery — or use a batch with a policy that does both in one call (section 7).
Add it once, then reuse it. This does not upload or fetch the file again — it gives the same physical file an entry in each locale you name:
POST https://api.icecat.biz/product-gallery/v1/images/{imageId}/copies
{ "languageIds": [1, 3, 4] }
Each locale gets its own entry over the same file. Two rules:
[0]
[0, 1]
imageUrl
http
https
ftp
ftps
imageType
ProductImage
fileName
_
-
isActive
isPrivate
taxonomyId: 0
expiryDate
YYYY-MM-DD
""
The type says what the picture shows, and partners use it to lay the product page out — which picture is the hero, which are the packaging shots, which is the annotated one. It is one of the few fields that changes how your gallery is presented rather than just stored, so it is worth setting deliberately.
imageType is a string, and it has to match one of the values below exactly — the spelling, the capitals and the hyphens. An unknown value is refused.
ProductImageFront-Center
ProductImageFront-Left
ProductImageFront-Right
ProductImageLeft
ProductImageRight
ProductImageRear
ProductImageTop
ProductImageBottom
ProductDetailImage
ProductPackagingImage
PackshotFront-Center
PackshotFront-Left
PackshotFront-Right
PackshotRear
PackshotBottom
LifestyleImage
ProductImageAnnotated
PackageContents
Schema/Chart
Screenshot
ProductAward
Logo
Other
PackshotFullFlat
ProductImagePEGI
ProductImageUSK
ProductImageESRB
Twenty-eight values in all. The dictionary is an open set — Icecat can add a type without a release — so treat an unknown value as “not added yet” rather than “never possible”, and ask us if you need one.
Four of them are tied to a category. The four video-game types above are accepted only on products in the video-games category; everywhere else they are refused, with a message naming both the type and the category. Every other type is open to every category.
This matters for how you build your mapping: map your image roles to Icecat types per category, not once for the whole catalog. A mapping that works for your games will be refused on your accessories. If a type you need is refused on a category where it makes sense, ask us rather than falling back to Other.
What each type is for, with examples of galleries that use them well, is in How to Make the Product Gallery Great Again — . Read it before you decide how to map your own image roles onto Icecat types; it is written for the people choosing the pictures rather than for the integration.
Format is decided by reading the file, not by its extension or the name you gave it. If a file arrives but isn’t an image, or is damaged, it’s refused with a message naming the reason.
A picture sent with isPrivate: true is not distributed to every channel partner. It reaches only the partners who are authorized for your brand in Icecat — the ones you invited or whose request you approved. Every other partner receives the gallery without it, and never learns it exists.
isPrivate: true
Use it for material you are willing to give your own retail network but not the whole catalog: campaign imagery, packaging for a product that is not on sale yet, pictures under a license that does not cover general distribution.
Three things to know:
expiryDate is a day, not a moment, and the calendar is Europe/Amsterdam — the platform’s business timezone. The expiry day itself already counts as expired: a picture dated 2026-12-31 stops being published at the first moment of 31 December in Amsterdam, not at the end of it.
2026-12-31
A date that is already today or earlier in that calendar is refused. To stop a picture now, deactivate it.
Every read gives you metadata.serverDate in the same calendar, so you can work out expired yourself without guessing which day the server means.
metadata.serverDate
expired
The order decides which picture a partner sees first, which second, and so on.
Only the relative order matters. When your gallery reaches a partner, the pictures are numbered from 1 with no gaps, in the sequence your numbers put them in. So an order of 1, 2, 5, 9 and an order of 1, 2, 3, 4 arrive as exactly the same gallery. You never have to tidy your numbers for the sake of the output.
Take a gallery of four pictures — three international, one German only — in the Icecat gallery of a product. This is the number each picture holds in each order:
languageId 0
languageId 1
languageId 4
Reading the columns:
So an English partner receives front, back, detail, and a German partner receives de_packshot, front, back, detail.
Note the gaps in the base order — 1, 2, 5, 9. They change nothing: both partners receive a gallery numbered 1, 2, 3, 4.
A gallery keeps:
languageId > 0
A locale that has no order of its own follows the base order, filtered to what it receives. When you read an order, isInherited: true tells you exactly that.
isInherited: true
So one picture can hold several numbers at once: one in the base order, and one in each locale that has been arranged deliberately.
When you write an order, you name pictures by sourceId — the file itself — and not by imageId.
The reason is the base order. It covers the whole gallery, and one picture can be present in several locales at once. If an order named locale entries, a picture published in English, French and Dutch would take three places in one sequence instead of one. Naming the picture keeps it in a single place, no matter which locales it appears in.
Inside one locale the distinction does not arise: there a picture has exactly one entry, so the two mean the same position.
Instead of adding a picture and then correcting its position, name the position in the same call:
{ "productId": {productId}, "taxonomyId": 0, "languageId": {languageId}, "imageUrl": "https://images.example.com/your-product/de_front.jpg", "placements": [ { "languageId": 0, "orderNumber": 3 }, { "languageId": 4, "orderNumber": 1 } ] }
Filled in for a German picture, that says: third in the gallery’s base order, first for Germany.
In a placement, languageId names which order you are writing into, not the locale of the picture. Which orders a picture may name depends on the locale it was added to:
Three more rules, and they are short:
PATCH https://api.icecat.biz/product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}
The three placeholders say which order you are writing: the product, which of its galleries, and which order inside that gallery — 0 for the base order, or a locale id for that locale’s own.
{ "placements": [ { "sourceId": {sourceId}, "orderNumber": 1 }, { "sourceId": {sourceId}, "orderNumber": 2 } ] }
Filled in, …/products/152257994/galleries/0/image-orders/4 writes Germany’s order in the Icecat gallery of product 152257994.
…/products/152257994/galleries/0/image-orders/4
You only list the pictures you care about; the rest of the locale’s pictures are arranged around them automatically. There is nothing to wait for: when the call answers, the order is in place.
Every picture you name has to be in the order you are writing. A sourceId identifies a file, not a product, so nothing stops you from sending one that belongs elsewhere — but the call refuses it with 400 and picture N is not in this sequence. That covers both cases: a file that is on another product, and a file that is in this gallery but not visible in the locale you are writing.
400
Two more refusals, each a 400 before anything is written:
orderNumber
DELETE https://api.icecat.biz/product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}
That locale stops having an order of its own and follows the base order again. The answer shows the order it falls back to, marked isInherited: true. Only locale orders can be reset — the base order is the ground everything else stands on.
Adding pictures one at a time is fine for a handful. When your system owns the gallery and wants Icecat to match it, use a batch: one submission that adds, updates, deactivates and orders, under a stated policy.
POST https://api.icecat.biz/product-gallery/v1/products/{productId}/batch
{ "policy": "BATCH_POLICY_REPLACE_ALL_FOR_ALL_LOCALES", "items": [ { "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/front.jpg", "imageType": "ProductImage", "placements": [ { "languageId": 0, "orderNumber": 1 } ] }, { "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/back.jpg", "imageType": "ProductImage", "placements": [ { "languageId": 0, "orderNumber": 2 } ] } ] }
Up to 2000 items in one submission.
This is the rule that costs integrations the most time. An item is not a partial update: a field you leave out means its default, not “leave what is there”. Re-submitting a picture without imageType resets its type to ProductImage; without fileName clears the name it had.
So build each item as the complete state you want that picture to have, and send every field you care about every time.
The policy is required — there is no default — and it decides what happens to the pictures that are already there. It is applied within each (gallery, locale) group present in your items: galleries your items do not mention are never touched.
BATCH_POLICY_CREATE_OR_REPLACE
BATCH_POLICY_REPLACE_ALL
BATCH_POLICY_REPLACE_ALL_FOR_ALL_LOCALES
Read the middle row carefully: it replaces one locale, not the gallery. If you send only German items under REPLACE_ALL, only German pictures are deactivated — the international ones stay.
REPLACE_ALL
A gallery whose items all failed is left alone. A refused item names nothing, so its gallery is never reached, and neither the policy nor anything else touches what is there. A feed that breaks overnight cannot take your gallery down — it simply changes nothing. That holds as long as the submission reaches the gallery only through its items; the next section is about the one way to reach it without them, and about what REPLACE_ALL_FOR_ALL_LOCALES then does.
REPLACE_ALL_FOR_ALL_LOCALES
extraTaxonomyIds
Before the policy does anything, the batch works out which galleries it is acting on. There are exactly two ways in:
Galleries it does not reach are not touched at all, whatever the policy says.
extraTaxonomyIds takes up to 500 ids. Listing a gallery your items already cover changes nothing, and an id Icecat does not know is ignored rather than refused.
This is the case extraTaxonomyIds exists for, and it is worth walking through, because on its own each part of the request looks like it should do nothing.
Say a partner’s contract has ended and their gallery has to go. You have no pictures to send — you want the gallery cleared, not refreshed. A batch with an empty items list reaches nothing, so on its own it does nothing at all. Naming the taxonomy is what points the batch at that gallery:
items
{ "policy": "BATCH_POLICY_REPLACE_ALL_FOR_ALL_LOCALES", "items": [], "extraTaxonomyIds": [ {taxonomyId} ] }
REPLACE_ALL_FOR_ALL_LOCALES is the one policy that knows what to clear without being shown any pictures — “all pictures of this gallery” needs no items to mean something. Pointed at a gallery by extraTaxonomyIds, it deactivates everything in it, in every locale.
The other two policies work the opposite way: they infer what to clear from what was applied, so in a gallery that received nothing they clear nothing. If you would rather be explicit about which markets you are clearing, use one of them and name the locales instead:
{ "policy": "BATCH_POLICY_CREATE_OR_REPLACE", "items": [], "extraTaxonomyIds": [ {taxonomyId} ], "removeLanguageIds": [ 0, 1, 4 ] }
Here nothing is inferred at all: the policy clears nothing, and exactly the three locales you listed are cleared — INT, English and German. Locales you did not name keep their pictures. This is the safer form, and the one to prefer when you are clearing part of a gallery rather than all of it.
Either way the pictures are deactivated, not deleted (section 11), so a partner gallery emptied by mistake can be switched back on.
⚠️The same combination is what makes REPLACE_ALL_FOR_ALL_LOCALES dangerous. Under that policy, every taxonomy in extraTaxonomyIds is emptied — including ones you listed for a different reason. Do not list taxonomies “for completeness” under it. List a taxonomy there when you mean to act on that gallery, and nothing else.
The other use is ordinary: one submission carrying items for several partner galleries. There you do not need extraTaxonomyIds at all — each item names its own taxonomyId, and that is what puts its gallery in scope. Reach for the field only when a gallery has to be acted on without items, which in practice means emptying it.
removeLanguageIds
removeLanguageIds lists locales whose existing pictures should be deactivated. Use it when a market should lose its pictures in the same call that refreshes the others — send the new German items and "removeLanguageIds": [1], and English is cleared while German is replaced.
"removeLanguageIds": [1]
It differs from the policy in one way that matters: the policy only deactivates in proportion to what it managed to apply, while removeLanguageIds is an instruction that is carried out even when nothing was applied at all. An empty batch with removeLanguageIds clears those locales.
Like the policy, it only reaches the galleries the batch reaches — the ones its items name, plus extraTaxonomyIds.
deduplicateBetweenLocales
After the items are applied, deactivate every locale picture whose file is also present among the visible international pictures of its gallery.
Use it when your feed sends the same picture per locale — many do, because their source system has no notion of an international asset. Without this flag a partner in Germany receives the same photograph twice: once as the international picture and once as the German one.
POST …/batch-sync
batch does not make you wait. It answers straight away with a job:
batch
{ "job": { "jobId": "{jobId}", "status": "STATUS_ACCEPTED" } }
Poll it at GET /product-gallery/v1/jobs/{jobId}. The job echoes your whole submission — product, policy, items, removeLanguageIds, extraTaxonomyIds — so a reader does not need the original request to make sense of it. A finished job stays readable for a retention period and is then removed, so read the verdicts when the job finishes rather than months later.
Results appear all at once, with the terminal status. While the job is STATUS_ACCEPTED or STATUS_PROCESSING, no item has a verdict; when it turns STATUS_COMPLETED or STATUS_FAILED, every item has one. There are no partial results and no counters, so poll for the terminal status rather than for progress.
STATUS_ACCEPTED
STATUS_PROCESSING
STATUS_COMPLETED
STATUS_FAILED
Your pictures are fetched in parallel but applied in the order you submitted them, so the final gallery does not depend on which download finished first. Jobs for the same product run one after another.
Each item ends with a status and a finer numeric code:
code
STATUS_CREATED
201
STATUS_UPDATED
200
304
A job that completed with failed items is not a failed job: those verdicts belong to the items. A STATUS_FAILED job carries a failureReason instead — PERMISSION_DENIED (yours to fix, and re-running changes nothing), ABANDONED (nothing is wrong with the submission; resend it) or INTERNAL (ours).
failureReason
PERMISSION_DENIED
ABANDONED
INTERNAL
And note what cannot happen: if your submission is malformed, it is refused whole with 400 and no job is created. A job id always means the submission was structurally sound.
Everything above works the same way for a partner gallery — you change one number.
{ "productId": {productId}, "taxonomyId": {taxonomyId}, "languageId": 0, "imageUrl": "https://images.example.com/your-product/lifestyle_1.jpg" }
The gallery is created on first write; nothing needs to be set up beforehand.
What it means for that partner: they receive this gallery instead of the Icecat gallery, whole. Galleries are never merged — a partner gets one gallery, from one source, complete. So a partner gallery with three pictures replaces an Icecat gallery of ten for that partner, rather than adding three to it.
What it means for everyone else: nothing. Partners who are not authorized for that taxonomy cannot receive that gallery at all, and keep the Icecat gallery.
Two limits to remember:
PATCH https://api.icecat.biz/product-gallery/v1/images/{imageId}
{ "imageType": "ProductDetailImage", "expiryDate": "2027-01-31" }
Absent fields are left alone — the single-picture PATCH really is a patch, unlike a batch item. For expiryDate the two empty-ish values differ: absent means “do not change”, "" means “clear the expiry”.
PATCH
Order numbers are not changed here — they belong to the order endpoints in section 6.
PATCH https://api.icecat.biz/product-gallery/v1/images
{ "imageIds": [{imageId}, {imageId}], "isActive": false }
This call does one thing: it switches pictures on or off. Up to 2000 of them, all belonging to one product. isActive is required — leaving it out is not the same as sending false, it is a request that names no change, and it is refused. Two more refusals arrive before anything runs: repeating an ID, and mixing pictures of different products.
The answer reports every picture you submitted, changed or not:
STATUS_CHANGED
STATUS_NO_CHANGE
STATUS_SKIPPED_NOT_FOUND
STATUS_SKIPPED_NO_PERMISSION
“No change” is reported rather than left out on purpose: you cannot work it out from the outside. A list of IDs that are all unknown is a normal answer with every entry skipped, not an error.
Your picture is fetched by Icecat’s servers, not by a browser, and not from your office network. The platform deliberately behaves like a browser, because supplier servers expect one:
The most common failure is not a broken link — it is a host that serves the picture to a person and refuses our servers. If a URL opens in your browser but Icecat reports that the supplier refused it, the fix is on the hosting side: allow our fetches, or publish the files on a host that does not challenge automated clients. Sending the same URL again will not help; the refusal is a verdict, not a hiccup.
Addresses that resolve inside Icecat’s own network are refused at every step of a redirect chain. This is a security boundary and cannot be configured away.
As a brand account, you cannot delete. This is the vocabulary you have instead:
isActive: false
isActive: true
A deactivated picture is not distributed to anyone, but it is still there: you and Icecat editors can see it and switch it back on. Nothing you do through this API destroys a picture.
Every error, on every operation, has the same body — and it is not the Brand API’s error shape:
{ "code": 3, "message": "invalid image: image dimensions out of range: 199x200 (allowed 200..10000 per side)", "details": [] }
The HTTP status is the class, and it tells you what to do:
metadata.allowedLanguageIds
404
409
429
500
503
message is written to be shown to a person. Do not parse it — its wording can be improved without notice. Branch on the HTTP status and, where you need finer branching, on the numeric code.
message
Not exhaustive, and the wording can change — but these are the ones integrations actually hit, and knowing the cause saves the guessing.
Adding or changing a picture
download failed: HTTP 403 from …
download failed: HTTP 404 from …
download failed: empty response body from …
image download failed
unsupported image format (accepted: jpeg, png, tiff, bmp, webp)
invalid image: image dimensions out of range: 200x143 (allowed 200..10000 per side)
file exceeds the size cap: … bytes (cap …)
unknown image type "…"
image type "…" is not allowed for the product's category …
isPrivate is only allowed on the Icecat gallery (taxonomyId=0)
unknown taxonomyId …
expiryDate "…" is not a valid YYYY-MM-DD date
Locales
languageIds: INT (0) cannot be mixed with specific languages
languageIds are required
placements: an image added to locale N cannot place itself in order M
placements name order N twice
placements
Orders
picture N is not in this sequence
picture N appears more than once
orderNumber N is assigned more than once
orderNumber N is outside [1, 65535]
Batches and bulk changes
imageIds: N ids exceed the bulk ceiling of 2000
imageIds: id N appears more than once
image ids belong to N products; a bulk action addresses one product
Permissions and credentials
Missing credentials
Invalid API token
Auth service unavailable
Your user group is not allowed to perform this action.
service overloaded, retry later
A push you can’t repeat is a push that fails at 3 a.m. and wakes somebody up. Three facts make repeating safe:
REPLACE_ALL…
CREATE_OR_REPLACE
POST /images
What to do when a job or a call fails halfway:
FAILURE_REASON_ABANDONED
FAILURE_REASON_PERMISSION_DENIED
Stated plainly, so you do not build against them:
One product, one submission, three markets. English and French get the international pictures in the standard order; Germany gets an extra German-language packshot first, and the rest after it.
request headers:
Api-Token: your_api_token Content-Type: application/json
body — 4 is the locale id for German, 0 is INT:
4
{ "policy": "BATCH_POLICY_REPLACE_ALL_FOR_ALL_LOCALES", "deduplicateBetweenLocales": true, "items": [ { "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/front.jpg", "imageType": "ProductImage", "fileName": "your_product_front", "placements": [ { "languageId": 0, "orderNumber": 1 } ] }, { "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/back.jpg", "imageType": "ProductImageRear", "fileName": "your_product_back", "placements": [ { "languageId": 0, "orderNumber": 2 } ] }, { "taxonomyId": 0, "languageId": 0, "imageUrl": "https://images.example.com/your-product/detail.jpg", "imageType": "ProductDetailImage", "fileName": "your_product_detail", "placements": [ { "languageId": 0, "orderNumber": 3 } ] }, { "taxonomyId": 0, "languageId": 4, "imageUrl": "https://images.example.com/your-product/de_packshot.jpg", "imageType": "ProductPackagingImage", "fileName": "your_product_de", "placements": [ { "languageId": 4, "orderNumber": 1 } ] } ] }
Every item names its type and its file name, because a batch item is the whole state of that picture — see section 7.
What this does:
Then poll the job, and read the gallery back to confirm:
GET https://api.icecat.biz/product-gallery/v1/jobs/{jobId} GET https://api.icecat.biz/product-gallery/v1/products/{productId}/galleries?view=FULL GET https://api.icecat.biz/product-gallery/v1/products/{productId}/galleries/0?languageId=4
The last call answers with Germany’s effective order — the one a German partner will receive, with isInherited telling you whether it is Germany’s own or the base order showing through.
isInherited
Every bound this API applies, in one place. They are contract limits, the same for every account, and they are checked before anything is written — a request over one of them is refused with 400 and changes nothing.
One picture
Order
One call
Your account
If one of these blocks an integration that is otherwise reasonable, tell us rather than working around it.
Read further: Icecat, API, e-commerce, ecommerce, Icecat