Icecat

Manual for Brand Partners: Icecat Gallery API

Version: 1.0
Updated 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.


1. Before you start

The address

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/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.

How to identify yourself

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.

A refused credential and a missing one are told apart deliberately:

You getIt means
401 Missing credentialsthe request carried neither header
401 Invalid API tokenthe token was not accepted
503 Auth service unavailablewe could not check the credential — retry

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.

The ids you will be substituting

Everything on this page uses placeholders in curly braces. This is what each one is and where it comes from:

PlaceholderWhat it isWhere you get it
{productId}your product in Icecatthe answer you got when you created the product
{taxonomyId}which gallery of that product: 0 is the Icecat gallery; any other value is one partner’s gallery0, or the id Icecat gave you for that partner
{languageId}a locale: 0 is INT, above 0 is one languageGet your locales, in Manual for Brand Partners: Icecat Push-API (API-IN)
{imageId}one picture, in one locale, of one gallerythe answer when you added it, or any gallery read
{sourceId}the physical file behind a picture. It identifies the file, not a product — the same file can stand behind pictures on several of your productsimage.source.sourceId in any read
{jobId}one batch you sent with POST …/batch, so you can check back on itthe answer to that call

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.

How images reach us

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.

What you build this with

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.

What you may not do

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.

Every operation, in one table

What you want to doCallExplained in
see every gallery of a productGET /product-gallery/v1/products/{productId}/galleries?view=FULL§3
see one galleryGET /product-gallery/v1/products/{productId}/galleries/{taxonomyId}§3
see one pictureGET /product-gallery/v1/images/{imageId}§3
add a picturePOST /product-gallery/v1/images§3
change a picturePATCH /product-gallery/v1/images/{imageId}§9
reuse the same picture in another localePOST /product-gallery/v1/images/{imageId}/copies§4
activate or deactivate many pictures at oncePATCH /product-gallery/v1/images§9
read an orderGET /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}§6
write an orderPATCH /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}§6
let a locale fall back to the base orderDELETE /product-gallery/v1/products/{productId}/galleries/{taxonomyId}/image-orders/{languageId}§6
push a whole gallery in one batch — the answer comes back at once and carries a job id, the work runs after itPOST /product-gallery/v1/products/{productId}/batch§7
push a whole gallery in one batch and wait — the answer comes when every picture has been dealt with, and carries the result of eachPOST /product-gallery/v1/products/{productId}/batch-sync§7
check a batch by its job id: whether it has finished, and what happened to each picture in itGET /product-gallery/v1/jobs/{jobId}§7

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.


2. The model

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):

taxonomyIdWhich gallery
0the Icecat gallery — your standard images, the ones your channel partners see
any othera partner gallery — images for one particular partner. Taxonomies are named after partners, so the taxonomy id is how you address that partner’s gallery

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.

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.


3. Your first picture

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"
}

productIdtaxonomyId 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.

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.

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.

Two things in metadata are worth reading every time:

  • allowedLanguageIds — the locales this account may edit. A locale outside this list will be refused, so check it once at the start of an integration instead of discovering it per request.
  • canDeletefalse for a brand account. It is the API telling you which half of the vocabulary is yours.

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.


4. International and locale pictures

A picture belongs to exactly one locale, and 0 (INT) is not “no locale” — it is “every locale”.

You wantWhat to send
one picture for all marketsone image, languageId: 0
a picture only Germany receivesone image, languageId: 4
the same picture in three marketsthree images, one per locale

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).

Reusing one picture in several locales

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:

  • The list is not a final state. Locales that are not in it keep whatever they already have; nothing is removed here. To take a locale away, deactivate that locale’s picture.
  • INT cannot be mixed with specific locales in one call. [0] alone is valid, [0, 1] is refused.

5. What a picture carries

FieldMeaningRules
imageUrlwhere Icecat fetches the filehttp, https, ftp or ftps, up to 1999 characters. ftps means explicit TLS on the FTP port, not implicit TLS on 990
languageIdthe locale, 0 = INTrequired, no default
taxonomyIdwhich gallery, 0 = Icecatrequired, no default
imageTypewhat the picture showssee below; absent means ProductImage
fileNameyour own name for itletters, digits, _ and -, no dot, up to 35 characters
isActivedistributed or notabsent means active
isPrivaterestricts the picture to the partners authorized for your brand — see belowabsent means public; allowed only in the Icecat gallery (taxonomyId: 0)
expiryDatethe day the picture stops being publishedYYYY-MM-DD, must be a future day; "" means no expiry

Image types

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.

The product itself

ValueWhat it is
ProductImagethe product on a white or transparent background. The default when you send no type, and the type of the main image
ProductImageFront-Centerstraight-on, from the front
ProductImageFront-Leftangled, front and left side together
ProductImageFront-Rightangled, front and right side together
ProductImageLeftthe left side
ProductImageRightthe right side
ProductImageRearthe back
ProductImageTopfrom above
ProductImageBottomfrom below
ProductDetailImagea close-up of one feature or detail

The packaging

ValueWhat it is
ProductPackagingImagethe packaging — the general pack-shot
PackshotFront-Centerthe front of the packaging, straight on
PackshotFront-Leftthe packaging, angled from the front left
PackshotFront-Rightthe packaging, angled from the front right
PackshotRearthe back of the packaging, where the detailed information usually is
PackshotBottomthe underside of the packaging

Everything else

ValueWhat it is
LifestyleImagethe product in use, in a real setting
ProductImageAnnotateda picture with callouts naming features. Usually local, because the callouts are written in a language
PackageContentseverything the box contains
Schema/Charta diagram, schema or chart. Note the slash — it is part of the value
Screenshotan interface, for software and digital products
ProductAwardthe product with an award or badge it has won
Logothe product’s logo. For products, there is nothing to photograph — a service, a warranty, a license — the logo is the picture
Otheranything relevant that none of the above describes

Video games only

ValueWhat it is
PackshotFullFlatthe flattened packaging, front and back cover in one image
ProductImagePEGIthe PEGI age rating (European Union)
ProductImageUSKthe USK age rating (Germany)
ProductImageESRBthe ESRB age rating (North America)

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.

The file itself

FormatsJPEG, PNG, TIFF, BMP, WebP
Animated WebPrefused — only still images
Sizeup to 50 MB
Dimensions200 to 10 000 pixels on each side

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.

Private pictures — isPrivate

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.

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:

  • It is a brand-level list, not a per-product one. The authorization that determines this runs at the brand level. It is not the per-product selection you make when a product is set to limited access — a picture does not have its own partner list.
  • It works only in the Icecat gallery (taxonomyId: 0). In a partner gallery, the flag is refused, and it would mean nothing there anyway: that gallery already goes to one partner.
  • It is not a way to take a picture down. A private picture is still published, to a smaller audience. To stop it being distributed at all, deactivate it — section 11.

Expiry dates are calendar days in Amsterdam

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.

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.


6. Order

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.

One gallery, three sequences

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:

PictureBase order (languageId 0)English (languageId 1)German (languageId 4)
front.jpg (INT)112
back.jpg (INT)223
detail.jpg (INT)554
de_packshot.jpg (German only)91

Reading the columns:

  • the base order covers every picture of the gallery, whatever locale it lives in — the German packshot included;
  • English has no order of its own. It follows the base order, over the pictures English receives, which is why its column repeats the base numbers and the German picture is not there at all;
  • German has its own order, and it puts the packshot first.

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.

Two orders, side by side

A gallery keeps:

  • the base order (languageId: 0) — over every picture of the gallery, whatever locales those pictures live in. A picture present only in French is in the base order just the same.
  • a locale’s own order (languageId > 0) — over the pictures that locale receives: the international ones plus its own.

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.

So one picture can hold several numbers at once: one in the base order, and one in each locale that has been arranged deliberately.

Numbers are 1-based and sparse

  • The first picture in an order is 1, never 0.
  • Gaps are normal. Moving one picture does not renumber its neighbors, so an order of 1, 2, 5, 9 is a healthy order and there is nothing to repair.
  • An order number is at most 65535.

Orders name pictures, not locale entries

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.

Placing a picture when you add it

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:

The picture isIt may place itself in
a locale picture (languageId > 0)the base order and its own locale — so at most two entries
an INT picture (languageId: 0)the base order and any locales it likes

Three more rules, and they are short:

  • an order you do not name is left alone, and the picture simply lands at the end of it;
  • naming a locale that had no order of its own gives that locale an order, starting from the one it was following;
  • a locale can appear only once in the list — naming it twice is refused — and the list holds at most 120 entries, far more than any gallery needs.

Rearranging what is already there

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.

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.

Two more refusals, each a 400 before anything is written:

  • the same sourceId twice in one request;
  • the same orderNumber twice in one request — two pictures cannot ask for the same place.

Letting a locale fall back again

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.


7. Pushing a whole gallery in one call

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.

A batch item replaces the picture’s state, it does not patch it

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.

Choose the policy deliberately

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.

PolicyWhat it does to what is already thereWhen you want it
BATCH_POLICY_CREATE_OR_REPLACEnothing is deactivated; new pictures are added, matching ones updatedyou are adding to a gallery somebody else also contributes to
BATCH_POLICY_REPLACE_ALLthe pictures of the group’s locale are deactivated first, then your items are appliedyou own one market’s pictures and are republishing that market
BATCH_POLICY_REPLACE_ALL_FOR_ALL_LOCALESall pictures of the group’s gallery are deactivated first, then your items are appliedyour system owns the whole gallery and Icecat should mirror it

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.

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.

Which galleries a batch acts on, and extraTaxonomyIds

Before the policy does anything, the batch works out which galleries it is acting on. There are exactly two ways in:

  1. an item that was applied in that gallery — a refused item does not count;
  2. the gallery’s taxonomy listed in extraTaxonomyIds.

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.

Emptying a partner’s gallery

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:

{
  "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.

Pushing to several partners at once

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.

Dropping a locale: 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.

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.

Removing duplicates between locales: 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.

Two ways to send a batch

POST …/batchPOST …/batch-sync
Answersat once, with a job id to check back onwhen the work is done, with the results
Good forreal submissions of any sizesmall batches, and testing

batch does not make you wait. It answers straight away with a job:

{ "job": { "jobId": "{jobId}", "status": "STATUS_ACCEPTED" } }

Poll it at GET /product-gallery/v1/jobs/{jobId}. The job echoes your whole submission — product, policy, items, removeLanguageIdsextraTaxonomyIds — 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.

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.

Reading the verdicts

Each item ends with a status and a finer numeric code:

StatuscodeMeaning
STATUS_CREATED201a new picture was created
STATUS_UPDATED200an existing picture was updated
STATUS_UPDATED304it already held exactly this state; nothing was written
STATUS_FAILED400the item cannot be applied — a dead URL, a file that is not an image, an unknown type or locale, a duplicate inside the submission
STATUS_FAILED403you may not edit this item’s locale

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).

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.


8. Giving one partner their own gallery

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:

  • isPrivate is not available in a partner gallery. Private pictures exist only in the Icecat gallery (taxonomyId: 0).
  • Everything else — locales, orders, the base order, batches, policies — behaves identically. Each gallery keeps its own orders.

9. Changing what is already there

One picture at a time

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”.

Order numbers are not changed here — they belong to the order endpoints in section 6.

Many pictures at once

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:

OutcomeMeaning
STATUS_CHANGEDapplied
STATUS_NO_CHANGEit was already in that state
STATUS_SKIPPED_NOT_FOUNDno such picture
STATUS_SKIPPED_NO_PERMISSIONyou may not edit it — most often a locale outside your allowed set

“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.


10. How Icecat fetches your URL

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:

  • it follows redirects, up to twenty, including to another domain, carrying the cookies of that chain;
  • it identifies itself as a browser, and sends the request headers a browser sends;
  • it accepts broken, self-signed and ancient certificates — a long-standing decision, because they are ordinary on vendor servers;
  • it repairs addresses that are not strictly valid — spaces, parentheses, non-Latin file names and domain names — the way a browser silently does;
  • it retries once on a transient network failure. A refusal by your server, a “not found” or a file over the limit is a verdict and is not retried;
  • it tolerates slow and abandons silent: a transfer that keeps delivering is left alone for several minutes; one that stops delivering for a few seconds is dropped;
  • it limits how many files it pulls from one host at a time, so a batch of sixty pictures does not arrive as sixty connections.

If your files are behind protection

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.

What is never fetched

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.


11. Hiding a picture instead of deleting it

As a brand account, you cannot delete. This is the vocabulary you have instead:

You wantDo this
stop distributing a picture to channel partnersPATCH it with isActive: false
stop many at oncebulk PATCH with isActive: false
put one backPATCH it with isActive: true
stop a picture on a known dateset expiryDate
clear a whole locale in one submissiona batch with removeLanguageIds

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.


12. When something goes wrong

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:

StatusWhat it meansWhat to do
400the request itself is wrong — a missing field, an impossible value, a file that is not acceptablefix and resend; retrying unchanged will fail identically
401the credential is missing (Missing credentials) or not accepted (Invalid API token)check the Api-Token header — see section 1
403you may not do this — the product, the locale, or the operation is not yoursdo not retry; check metadata.allowedLanguageIds, and whether the operation is one brands have at all
404the product, picture or job does not existcheck the id
409the change conflicts with the current stateread the current state and decide
429the service is at capacity right nowback off and retry
500our side failedretry with a backoff
503the service was saturated and could not take your file, or your credential could not be checkedretry — nothing is wrong with the request, and writing the picture off would discard a good one

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.

Messages you will meet, and what they mean

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

MessageWhat happenedWhat to do
download failed: HTTP 403 from …the host serving your file refused Icecat’s serversfix it on the hosting side — section 10. Resending will not help
download failed: HTTP 404 from …the address leads to nothingcheck the URL
download failed: empty response body from …the host answered, with no filecheck the URL, then the host
image download failedthe fetch failed for a reason without a status — DNS, TLS, a refused connectioncheck the host is reachable from the public internet
unsupported image format (accepted: jpeg, png, tiff, bmp, webp)the file is not one of the accepted formats, or it is an animated WebPconvert it. The format is read from the file, not from the extension
invalid image: image dimensions out of range: 200x143 (allowed 200..10000 per side)one side is outside the rangeresize
file exceeds the size cap: … bytes (cap …)over 50 MBcompress or resize
unknown image type "…"the type is not in the Icecat dictionarycheck the spelling against section 5 — hyphens and capitals are part of the value
image type "…" is not allowed for the product's category …the type exists but not for this categorysee section 5: four types belong to video games only
isPrivate is only allowed on the Icecat gallery (taxonomyId=0)isPrivate was sent on a partner gallerydrop the flag; it would mean nothing there
unknown taxonomyId …no such gallery exists to addresscheck the id Icecat gave you for that partner
expiryDate "…" is not a valid YYYY-MM-DD datethe format is wrong, or the day is today or earlieruse a future day; to stop a picture now, deactivate it

Locales

MessageWhat happenedWhat to do
languageIds: INT (0) cannot be mixed with specific languagesa reuse call named 0 alongside real localessend [0] on its own, or the locales on their own
languageIds are requiredthe reuse call named no localename at least one
placements: an image added to locale N cannot place itself in order Ma locale picture tried to write into another locale’s ordera locale picture may name only the base order and its own locale
placements name order N twicethe same locale appears twice in placementsone entry per locale

Orders

MessageWhat happenedWhat to do
picture N is not in this sequencethat sourceId is not among the pictures visible in the order you are writingit is a file ID, not a product ID — check it belongs to this gallery and this locale
picture N appears more than oncethe same sourceId twice in one requestone entry per picture
orderNumber N is assigned more than oncetwo pictures asked for the same placegive them different numbers
orderNumber N is outside [1, 65535]the number is out of range, or 0the first place is 1, never 0

Batches and bulk changes

MessageWhat happenedWhat to do
imageIds: N ids exceed the bulk ceiling of 2000too many pictures in one callsplit the call
imageIds: id N appears more than oncea repeated IDde-duplicate your list
image ids belong to N products; a bulk action addresses one productthe list spans productsone call per product

Permissions and credentials

MessageWhat happenedWhat to do
Missing credentialsneither Api-Token nor Access-Key was sentsend the token — section 1
Invalid API tokenthe token was not acceptedcheck it in your profile. The reason is not disclosed, and retrying will not change it
Auth service unavailablewe could not check the credentialretry with a backoff
Your user group is not allowed to perform this action.the operation is not one brand accounts have — cropping, or the source endpointssee section 14
service overloaded, retry laterthe service was saturatedretry. Nothing is wrong with your request

13. Retrying safely

A push you can’t repeat is a push that fails at 3 a.m. and wakes somebody up. Three facts make repeating safe:

  1. The same file costs nothing twice. Sending a file Icecat already stores reuses the stored one instead of storing it again.
  2. A batch under REPLACE_ALL… is idempotent in effect. Applying the same submission twice leaves the same gallery, because the policy deactivates first and then applies your items. This is why you should prefer a batch over a sequence of single calls for anything automated.
  3. CREATE_OR_REPLACE and single POST /images are not. They add. Repeating them adds again, and you will end up with two entries for one picture in one locale.

What to do when a job or a call fails halfway:

  • Structural failure (400) — nothing was applied; no job exists. Fix and resend.
  • Failed items inside a completed job — the rest was applied, and nothing was deactivated because of the failed items. Resend only the failed items, or the whole batch under a replacing policy.
  • STATUS_FAILED with FAILURE_REASON_ABANDONED — resend; nothing about the submission is wrong.
  • STATUS_FAILED with FAILURE_REASON_PERMISSION_DENIED — do not resend until the access question is settled.
  • 503 — retry with a backoff. This is the one ingest failure that is ours, not yours.

14. What this API does not let a brand do

Stated plainly, so you do not build against them:

  • Delete anything. No picture, no gallery. Deactivate instead — section 11.
  • Crop. Cropping and the source endpoints behind it are for Icecat editors only. A crop changes the shared physical file, which every picture referencing it would follow — including pictures on other products — so it is not a brand-side operation.
  • Mark a picture private outside the Icecat gallery. isPrivate works only at taxonomyId: 0.
  • Edit a locale outside your own set. metadata.allowedLanguageIds is the list; anything else is refused, and in a bulk call is reported as STATUS_SKIPPED_NO_PERMISSION.

15. A complete example

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.

POST https://api.icecat.biz/product-gallery/v1/products/{productId}/batch

request headers:

Api-Token: your_api_token
Content-Type: application/json

body — 4 is the locale id for German, 0 is INT:

{
  "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:

  • every existing picture of the Icecat gallery is deactivated first — REPLACE_ALL_FOR_ALL_LOCALES covers all locales of that gallery;
  • three international pictures are added and placed 1, 2, 3 in the base order;
  • one German picture is added and placed first for Germany; Germany now has an order of its own, and the three international pictures follow it in the sequence the base order gives them;
  • English, French and every other locale are untouched by the German item and follow the base order;
  • any locale picture whose file is identical to a visible international one is deactivated, so the same photograph is never distributed twice in one locale.

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.


16. Limits

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

imageUrl length1999 characters
fileNameup to 35 characters; letters, digits, _ and -, no dot
imageType1–64 characters, and must be a value from section 5
File size50 MB
Dimensions200 to 10 000 pixels on each side
FormatsJPEG, PNG, TIFF, BMP, WebP — animated WebP refused

Order

Order number1 to 65535. The first place is 1, never 0
placements when adding a pictureup to 120 entries, each locale at most once
A locale picture may place itself inthe base order and its own locale — at most two orders

One call

Batch items2000 per submission
extraTaxonomyIds500 per submission, each ID once
removeLanguageIdsthe locales you name; unknown ones are skipped, not refused
Activate / deactivate in bulk2000 picture IDs, all of one product, no ID repeated

Your account

Locales you may writewhatever your account is assigned — metadata.allowedLanguageIds on every read
Products you may writethose of the brands assigned to your account

If one of these blocks an integration that is otherwise reasonable, tell us rather than working around it.

icecat

Icecat is a global leader in product content syndication, helping brands, manufacturers, distributors, and retailers deliver enriched and consistent product information across multiple platforms. Trusted by 40,000+ e-commerce brands, Icecat helps turn browsers into buyers.

Recent Posts

Similarweb Report: AI Is Changing How Consumers Discover and Buy Products

AI is becoming a regular part of online shopping, but consumers are not abandoning traditional…

1 hour ago

Icecat Studio Sprint 104 Release Notes: Verified Publishing and Full Accessibility Coverage

Sprint 104 is about making what we already have more dependable. Publication status is now…

3 days ago

Meet Icecat at E-SHOW Madrid 2026

Icecat will be present at E-SHOW Madrid 2026, taking place on November 4 and 5…

3 days ago

Meta Muse Brings AI Agents Closer to Becoming Online Shoppers

Meta has introduced Muse, a personal AI agent designed to do more than answer questions.…

4 days ago

Bol Pulls Further Ahead of Amazon in the Dutch E-commerce Market

Bol has strengthened its position as the largest online retailer in the Netherlands, widening its…

5 days ago

German Online Stores Lose Google Visibility as AI Changes Product Discovery

German online stores are losing visibility in Google search as AI-generated answers take a larger…

6 days ago