Importar produtos
Envia uma planilha de produtos (ou uma URL) para criar e atualizar o catálogo, com pré-visualização sem gravar e modelo de planilha.
Use esta página para carregar produtos em lote: a carga inicial de uma loja nova, a atualização diária de preço e estoque vinda de um ERP, ou a migração de outra plataforma. A importação é um upsert pelo SKU: um SKU que não existe cria o produto, um SKU que já existe atualiza.
O caminho seguro tem três passos. Baixe o modelo, rode a pré-visualização (não grava produtos e mostra o que aconteceria, linha a linha) e só então importe. Para controle fino de colunas, valores padrão e transformações, use o assistente em Importação avançada.
/api/v1/admin/import/products/templateAdminBaixa o modelo de importação de produtos
Privilégio manage_catalog. Devolve o mesmo CSV de /export/products/template: products_template, separador ;, com o BOM do UTF-8 e duas linhas de exemplo.
/api/v1/admin/import/products/previewAdminSimula a importação sem gravar produtos
Privilégio manage_catalog. Roda toda a validação da importação e responde o que seria criado, atualizado ou recusado, sem gravar nenhum produto. A chamada registra um job de preparação (o arquivo fica guardado temporariamente), mas nada entra no catálogo. Envie multipart/form-data.
Corpo da requisição (multipart/form-data)
filefileobrigatório.csv, .txt, .xml, .xlsx ou .xlsm.field_mappingsstring{"product_code":"sku","price":"price"}. Se omitido, a plataforma mapeia as colunas pelo nome.strategystringpadrão: create_updatecreate_updateupsertupdate_onlycreate_onlycreate_update e upsert criam e atualizam; update_only só atualiza SKUs que já existem; create_only só cria.default_vendor_iduuidvendor_id./api/v1/admin/import/productsAdminImporta produtos de um arquivo ou de uma URL
Privilégio manage_catalog. Aceita dois formatos de corpo, escolhidos pelo Content-Type:
multipart/form-datacom o arquivo no campofile;application/jsoncom{"url": "https://..."}: a plataforma baixa o arquivo (até 100 MB, sóhttpehttps, endereços internos são bloqueados) e importa em segundo plano.
Com arquivo pequeno e opções padrão, a importação é síncrona: grava na hora e responde 200 com o resumo. É pequeno o arquivo com até 256 linhas de dados e até 1 MiB. Acima disso, ou se você mandar qualquer opção diferente do padrão (strategy, update_existing=false, skip_errors=false, vendor_id), a importação vira um job: responde 202 com job_id e você acompanha em Jobs de importação.
Corpo da requisição (multipart/form-data)
filefileobrigatório.csv, .txt, .xml, .xlsx ou .xlsm.strategystringpadrão: import_allimport_allupdate_existingcreate_newimport_all cria e atualiza; update_existing só atualiza; create_new só cria. Outro valor volta 400.update_existingbooleanpadrão: truefalse, linhas cujo SKU já existe são puladas.skip_errorsbooleanpadrão: truetrue, uma linha com erro é registrada e o resto continua. Quando false, a importação para na primeira linha com erro (as linhas anteriores já foram gravadas).vendor_iduuidvendor_id.Corpo da requisição (application/json)
urlstringobrigatórioColunas da planilha
Os nomes não diferenciam maiúsculas de minúsculas e a ordem não importa.
Colunas
product_codestringsku). Até 128 caracteres.namestringobrigatóriopricestringobrigatório49,90, 49.90 ou R$ 49,90. Negativo é recusado.list_pricestringstatusstringAHDquantityintegerstock_quantity. 1,00 é lido como 1; um valor que não é número inteiro recusa a linha.weightnumbercategory_idstringvendor_idstringvendor_id do formulário).slugstringurl_key. Vazio gera a partir do nome.descriptionstringshort_descriptionstringshort_desc.Exemplo de requisição
# 1) pré-visualizar
curl "$BASE_URL/api/v1/admin/import/products/preview" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@produtos.csv" -F "strategy=upsert"
# 2) importar de verdade
curl "$BASE_URL/api/v1/admin/import/products" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@produtos.csv"
# 3) importar de uma URL (sempre assíncrono)
curl "$BASE_URL/api/v1/admin/import/products" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"url":"https://erp.example.com/exports/produtos.csv"}'Resposta
{
"total": 2,
"created": 1,
"imported": 1,
"updated": 1,
"skipped": 0,
"errors": [],
"warnings": []
}{
"job_id": "7aa94252-0df6-4293-8404-c0ffc2a96fbc",
"status": "processing",
"status_url": "/admin/import/jobs/7aa94252-0df6-4293-8404-c0ffc2a96fbc",
"total_rows": 12840
}{
"job_id": "f74f38fb-7010-4c92-99ee-296a11f8d315",
"total_rows": 2,
"would_create": 1,
"would_update": 0,
"would_skip": 1,
"errors_by_row": [
{
"row": 3,
"field": "vendor_id,name",
"message": "produto novo exige vendor_id, name — selecione o \"Vendedor padrão\" ou mapeie uma coluna para vendor_id"
}
],
"preview_rows": [
{
"row_number": 2,
"action": "create",
"data": { "name": "Camiseta Docs Teste", "price": "49,90", "product_code": "DOCS-API-001", "quantity": "10", "status": "A", "vendor_id": "94bd7e9a-fdad-4d01-8fc2-ba9d677d8b36" }
},
{
"row_number": 3,
"action": "error",
"data": { "name": "", "price": "19,90", "product_code": "DOCS-API-002", "quantity": "5", "status": "A", "vendor_id": "" },
"error": "produto novo exige vendor_id, name — selecione o \"Vendedor padrão\" ou mapeie uma coluna para vendor_id"
}
],
"valid": false
}Campos da resposta (200 síncrono)
totalintegercreatedintegerimported repete o mesmo número (nome antigo).updatedintegerskippedintegererrorsarrayrow (número da linha na planilha, o cabeçalho é a linha 1), field (pode faltar) e message. Sempre presente, mesmo vazio. Avisos como "status não reconhecido" também aparecem aqui.warningsarrayCampos da resposta (202)
job_iduuidstatusstringprocessing na criação.status_urlstring/api/v1. Monte a URL como /api/v1 + status_url.total_rowsintegerCampos da resposta (pré-visualização)
job_iduuidtotal_rowsintegerwould_createintegerwould_updateintegerwould_skipintegererrors_by_rowarrayrow, field, message). null quando não há erro.preview_rowsarrayaction é create, update, skip ou error; data mostra os valores já ligados aos campos; error só aparece quando action é error.validbooleantrue quando não há nenhum erro.Erros
| Status | Quando | Corpo de exemplo |
|---|---|---|
| 400 | Campo file ausente | {"error":"missing file field in form","detail":"http: no such file"} |
| 400 | Arquivo vazio | {"error":"csv_empty","detail":"uploaded file is empty"} |
| 400 | Falta a coluna name ou price | {"error":"missing_required_column","detail":"missing required column: price"} |
| 400 | Linha em branco no meio da planilha | {"error":"csv_empty_row","detail":"..."} |
| 400 | strategy fora da lista | {"error":"invalid strategy","detail":"strategy must be import_all, update_existing or create_new"} |
| 400 | JSON sem url, ou URL recusada | {"error":"url is required"} |
| 400 | Planilha Excel corrompida | {"error":"invalid spreadsheet","detail":"..."} |
| 401 | Token ausente ou expirado | {"error":"invalid or expired token"} |
| 403 | Falta manage_catalog | {"error":"insufficient privileges","required":"manage_catalog"} |
| 415 | Extensão não aceita | {"error":"unsupported file type","detail":"only CSV, XLSX and XML files are accepted (got \".pdf\")"} |
| 500 | Falha ao importar | {"error":"import products failed","detail":"..."} |
As mensagens por linha (produto novo exige vendor_id...) saem em português do servidor.