Skip to content
Catalog

Product import

Upload a product file, follow the operation, and fix the rejected rows.

Upload a CSV with your catalog and get an operation back right away. The file is processed in the background; poll the operation until it finishes and download the report if any rows were rejected.

Requirements

  • An Admin token in ECOMIQ_TOKEN. See Quickstart.
  • The catalog:create and catalog:update permissions to import, and operation:read to follow the operation.

Build the file

Each row is a variant. Rows with the same Código producto make one product.

products.csv
Código producto,Producto,SKU,Precio,Option 1 name,Option 1 value,Option 2 name,Option 2 value,Categoría
POL-001,Polo de algodón,POL-001-AZ-S,79.00,Size,S,Color,Blue,Ropa > Polos
POL-001,Polo de algodón,POL-001-AZ-M,79.00,Size,M,Color,Blue,Ropa > Polos
BOT-001,Botella,BOT-001-500,39.00,Capacity,500 ml,,,Hogar > Botellas
ColumnRequiredWhat it holds
Código productoTo createYour code. It creates the product or updates the one that has it.
ProductoYesProduct name.
SKUYesVariant SKU.
PrecioYesVariant price, with a decimal point. If the column is present, it is not empty.
Option N name, Option N valueNoName and value of a variant option, such as Size and S.
Atributo: <name>NoProduct characteristic, such as Atributo: Material.
CategoríaNoName or path, such as Ropa > Polos. If it does not exist, it is created.
MarcaNoBrand name. If it does not exist, it is created.
EtiquetasNoComma separated.
Imágenes, Imagen varianteNoComma-separated URLs. The variant image must also be in Imágenes.
Peso (kg), Largo (cm), Ancho (cm), Alto (cm)NoPackage size.
ID internoTo update by idAn Ecomiq product id. The row updates that product.

English headers, such as Product code or Price, also work.

Each row needs Código producto or ID interno. If you import as a seller, Categoría and Marca must already exist.

To load stock, add Stock inicial (an integer, 0 or more) and Ubicación de stock, with the inventory:update permission. The location must match exactly one active location; without it, the default one is used. An empty quantity keeps the current stock.

Variant options

Each pair holds the name and the value in the same row, so one file can mix products with Talla and Color and others with Capacidad.

  • The template has three pairs. Add Opción 4 nombre and Opción 4 valor and keep numbering if you need more.
  • A pair with both cells empty changes nothing. When updating, each complete pair changes that option and keeps the rest: sending only Color = Negro keeps the Talla.
  • A row is rejected if a pair has only a name or only a value, or if a name repeats in the row (case-insensitive).
  • All variants of a product use the same option names, in any order.

Template and export

Request the template with POST /v1/catalog/products/exports and { "format": "xlsx", "template": true }. It puts the required columns first, with lists of your categories, brands, and shipping profiles, and help on each column.

Without template, the export has your products with ID interno, ID interno variante, and an ID en {canal} column for each channel with listings. Upload it again to edit; the channel columns are read-only. Both are operations: when they finish, download their resultUrl.

In XLSX, Productos must be the first sheet and the headers the first row with data.

Formats

FormatContent-TypeMaximum
CSVtext/csv50 MiB
TSVtext/tab-separated-values50 MiB
XLSXapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet10 MiB
JSONLapplication/x-ndjson50 MiB

All formats accept up to 100,000 rows. In JSONL, each line is an object with the same column names:

products.jsonl
{"Código producto":"POL-001","Producto":"Polo de algodón","SKU":"POL-001-AZ-S","Precio":79,"Opción 1 nombre":"Talla","Opción 1 valor":"S"}

Upload the file

Send the raw file as the body, with its Content-Type.

curl -X POST https://api.ecomiq.pe/api/v1/catalog/products/imports \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: text/csv" \
  -H "Idempotency-Key: catalog-2026-10-04" \
  --data-binary @products.csv

The API checks the file's headers and responds 202 with the operation. Location points to /v1/operations/7204558912004325401.

202 response
{
  "id": "7204558912004325401",
  "status": "pending",
  "resource": "products",
  "format": "csv",
  "createdOn": "2026-10-04T15:30:00Z"
}

Keep the id to follow the operation; the products are created afterward.

Idempotency-Key is optional. If the response got lost, retry with the same key and the same file: you get the same 202 without a new operation. The key is valid for 24 hours.

Validate without writing

Add ?validateOnly=true to the URL. The operation checks every row and reports the errors, but does not change the catalog.

If the file can't be read, the API responds with an error and creates no operation:

400 response
{
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Import.InvalidFile": [
      "The import source is missing required headers: sku."
    ]
  },
  "code": "Import.InvalidFile"
}
ResponseWhenWhat to do
400 Import.InvalidFileRequired columns are missing or the file cannot be read.Fix the header.
400 Import.ContentTypeThe Content-Type is not a supported format.Send the type that matches the file.
400 Import.SizeLimitExceededThe file exceeds the maximum for its format.Split the file.
409 Operation.ResourceBusyA product import is already running.Wait for it to finish or cancel it.
422You reused the Idempotency-Key with another file or other options.Use a new key.
429You exceeded the upload limit.Wait as long as Retry-After says.

Follow the operation

Check the operation every few seconds while status is pending or running.

curl https://api.ecomiq.pe/api/v1/operations/7204558912004325401 \
  -H "Authorization: Bearer $ECOMIQ_TOKEN"

In this example the file had 4 rows and one was missing Código producto:

200 response
{
  "id": "7204558912004325401",
  "type": "import",
  "resource": "products",
  "format": "csv",
  "status": "completed",
  "fileName": "product-import-errors.csv",
  "resultUrl": null,
  "errorsUrl": "https://…/product-import-errors.csv?X-Amz-Expires=300&X-Amz-Signature=…",
  "error": null,
  "totalRows": 4,
  "processedRows": 4,
  "succeededRows": 3,
  "failedRows": 1,
  "startedOn": "2026-10-04T15:30:02Z",
  "completedOn": "2026-10-04T15:30:05Z",
  "createdOn": "2026-10-04T15:30:00Z"
}
statusWhat it means
pendingQueued.
runningProcessing. processedRows goes up.
completedDone. Valid rows were applied, even if others failed.
failedThe file could not be processed. Read error.
cancelledIt was cancelled. What was already applied stays.

Instead of polling, you can receive the operation.finished webhook, which carries the id and the counters. To cancel, use POST /v1/operations/{id}/cancel.

Review the rejected rows

If failedRows is greater than zero, errorsUrl holds a CSV with each rejected row. It's a signed link that expires after 5 minutes and needs no token; request the operation again to get a new one.

curl -o errors.csv "$ERRORS_URL"
errors.csv
sheet,row,column,code,message
,5,,Products.ImportExternalProductIdRequired,Source row 5 requires product_id or product_code.
ColumnWhat it holds
sheetXLSX sheet. Empty for CSV, TSV, and JSONL.
rowFile row, counting the header as row 1.
columnColumn of the error, when known.
code, messageWhat failed.

Fix those rows and upload them in a new file.

Next steps

  • For up to 100 products in JSON, use POST /v1/catalog/products/batch: it returns 200 with a result per row, without an operation.
  • Variants and prices to adjust prices after importing.
  • Inventory to load stock.

Last updated:

On this page