Skip to content

Import a catalog

This guide imports products from your own system into a marketplace, then keeps them up to date on every run. It uses the Admin API; the same calls exist in the Partners API for a vendor importing their own catalog, with the differences listed at the end.

Collect the identifiers your listings need, once per import:

Need Call Keep
The vendor GET /api/v1/users?group_id=… or GET /api/v1/users/search?email=… id, sent as user_id
Categories GET /api/v1/listings/categories id, sent as category_id
Custom fields GET /api/v1/listings/attributes each attribute’s alias, and its options for select fields
How it sells GET /api/v1/listings/order_types the order type, when the marketplace has several

Map your product categories to Kreezalid category ids in your own configuration, rather than matching titles on every run.

POST /api/v1/listings/bulk takes an array of listings, each with the same fields as Create a listing. Give every listing your product code in external_id: it is how you will find it again.

Terminal window
curl -X POST https://acme.mykreezalid.com/api/v1/listings/bulk \
-u "$KREEZALID_API_KEY:$KREEZALID_API_SECRET" \
-H "Content-Type: application/json" \
-d '[
{
"external_id": "SKU-1001",
"user_id": 4242,
"category_id": 812,
"title": "Linen apron",
"description": "<p>Washed linen, adjustable straps.</p>",
"price": 39.9,
"quantity": 25,
"images": ["https://cdn.example.com/products/sku-1001-front.jpg"],
"attributes": { "material": "Linen" }
},
{
"external_id": "SKU-1002",
"user_id": 4242,
"category_id": 812,
"title": "Cotton tea towel",
"description": "Set of two.",
"price": 14.5,
"quantity": 80
}
]'

The response has one entry per listing, in the order you sent them:

{
"responses": [
{ "id": 645646, "message": "Listing created", "success": true },
{ "id": null, "message": "Error. No listing created. price: …", "success": false }
]
}

A refused listing does not stop the others. Store the id of every created listing next to your product, and log the message of the refused ones. Besides invalid fields, a listing is refused when the vendor has reached the number of listings the marketplace allows them.

Creating twice creates two listings: external_id is not unique. On every run after the first, update the listings you already created and create only the new products.

  1. Find the listing. Use the id you stored. If you did not keep it, look it up by your code:

    Terminal window
    curl "https://acme.mykreezalid.com/api/v1/listings/search?external_id=SKU-1001&user_id=4242" \
    -u "$KREEZALID_API_KEY:$KREEZALID_API_SECRET"

    The match is exact and the answer is a listings array: empty means the product is new.

  2. Send what changed. PATCH /api/v1/listings/{id} with only the fields to change:

    Terminal window
    curl -X PATCH https://acme.mykreezalid.com/api/v1/listings/645646 \
    -u "$KREEZALID_API_KEY:$KREEZALID_API_SECRET" \
    -H "Content-Type: application/json" \
    -d '{"price": 36.9, "quantity": 12}'
    { "success": true, "message": "Listing has been updated" }

    A refused update answers "success": false with Error. No updates were made. and no detail. Check the values against the field rules of Create a listing.

  3. Retire products you no longer sell with "status": "archived" rather than deleting them, so their past orders keep their listing. DELETE /api/v1/listings/{id} removes one for good.

  • images is a list of public URLs. Kreezalid downloads each one during the request and stores its own copy, so the URL only needs to work at import time.
  • On creation, an image that cannot be downloaded is skipped and the listing is created without it.
  • On update, sending images replaces all of the listing’s images with the ones that could be downloaded. When none could, the current images stay. Do not send images on every run if they did not change: each run would download them again.

Sizes, colours and other variants are set on update, not on creation: create the listing, then PATCH it with a variants array.

Terminal window
curl -X PATCH https://acme.mykreezalid.com/api/v1/listings/645646 \
-u "$KREEZALID_API_KEY:$KREEZALID_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"variants": [
{ "external_id": "SKU-1001-S", "sku": "SKU-1001-S", "title": "Small", "price": 36.9, "quantity": 5 },
{ "external_id": "SKU-1001-L", "sku": "SKU-1001-L", "title": "Large", "price": 39.9, "quantity": 7 }
]
}'

The array is the complete list of the listing’s variants:

  • a variant whose external_id, or failing that sku, matches an existing one updates it;
  • a variant that matches none is created;
  • an existing variant missing from the array is deleted.

So always send every variant of the listing, not only those that changed. An empty array changes nothing. Read them back with GET /api/v1/listings/{id}/variants.

  • One request at a time per vendor. Saving a listing updates its vendor’s counters, so parallel writes for the same vendor wait on each other and can fail. Parallelise across vendors, not within one.
  • Make every run resumable. Keep, per product, the Kreezalid id and a hash of what you last sent. Skip unchanged products, and you can stop and restart an import at any point.
  • Read success on every answer, not only the HTTP status. See Errors.
  • Keep your own data with the listing in metafields, for instance pim.product_id, when external_id already holds the vendor’s code.
  • Listen instead of polling to learn about changes made on the marketplace: the listing.update and listing.delete webhooks.

A vendor importing their own catalog uses the same requests under /api/partners/v1, with these differences:

  • user_id is ignored: every listing belongs to the vendor of the credential, and is created approved.
  • There is no search endpoint. Keep the ids returned at creation, or rebuild the mapping by paging through GET /api/partners/v1/listings and reading external_id.
  • The variant synchronisation described above is specific to the Admin API: do not rely on variants in Partners API updates.
  • Listings can be attached to the vendor’s stores with store_id or store_external_id. See Stores.