Pular para o conteúdo
Documentação API Reference FAQ Changelog Status
Otimizações API Magento 2 Adobe Commerce

Endpoints de API para integrações que tenham mais volume de alteração de preços e estoques (sem MSI)

PDF

Endpoints recomendados para ERPs integrarem preço e estoque em lojas Magento 2 com estoque único, usando batch, delta e source `default`.

S Sidenei Steinbach 22/05/2026 Atualizado 22/05/2026 13 min de leitura

Orientações de Integração — API Magento 2

Leitura e atualização de Preços e Estoque (lojas com estoque único / single stock)

Documento de referência para integrações ERP ↔ Magento 2 em lojas que operam com estoque único (sem multi-source de fato), mas que tipicamente rodam em Magento 2.3+ — versão em que o módulo Magento_Inventory (MSI) vem habilitado por padrão com um source default que representa o estoque legacy.

Cobre tanto os endpoints customizados de leitura dos módulos MageShop_PriceApi e MageShop_StockApi quanto os endpoints nativos de escrita do Magento.


1. Contexto

Em integrações ERP ↔ Magento, é comum observar volume excessivo de requisições nos endpoints de atualização de estoque e preços. Quando isso ocorre, a aplicação sofre com:

  • Concorrência elevada no PHP-FPM e contenção em locks de tabela.
  • Reindexação acionada repetidamente.
  • Aumento de timeouts/5xx e degradação para o cliente final da loja.

Este documento orienta o time de integração sobre:

  1. Como ler preços e estoque em massa de forma eficiente (módulos MageShop).
  2. Como escrever preços e estoque com o menor custo possível (Magento core).
  3. Boas práticas de paralelismo, delta e retry.

ℹ️ As orientações assumem loja com estoque único (sem multi-source configurado). Para escrita de estoque, recomenda-se o endpoint nativo de MSI fixando source_code = "default" — isso dá acesso a batch real sem precisar configurar nada adicional, já que default é o source criado automaticamente pelo Magento 2.3+. Um caminho alternativo via endpoint legacy é mantido como fallback para ambientes onde o módulo MSI esteja completamente desabilitado.


2. Convenções e Autenticação

2.1. Convenções

  • Base URL: https://<dominio>/rest/<store_code>store_code é o código da loja (ex.: default). Operações globais podem ser chamadas em /rest/V1/....
  • Verbos: GET para leitura (módulos MageShop), POST / PUT para escrita (Magento nativo).
  • Content-Type: application/json em request e response, UTF-8.
  • Numéricos retornados como string — comportamento padrão do Magento. O cliente deve converter (parseFloat / Decimal) conforme necessário.

2.2. Autenticação

ACLs exigidas:

  • Magento_Catalog::products para preços e leitura de estoque.
  • Magento_InventoryApi::source_items para escrita de estoque via inventory/source-items.

Token de admin (uso interno/scripts)

TOKEN=$(curl -s -X POST https://loja.com/rest/V1/integration/admin/token \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"senha"}' | tr -d '"')

curl -H "Authorization: Bearer $TOKEN" https://loja.com/rest/V1/product-prices

Token de integration (recomendado para ERPs/terceiros)

  1. Admin → System → Extensions → Integrations → Add New Integration.
  2. Em API → Resource Access selecione Catalog → Inventory → Products.
  3. Salve, ative e copie o Access Token gerado.
curl -H "Authorization: Bearer <access_token>" https://loja.com/rest/V1/product-prices

Tokens de integration não expiram automaticamente — revogue pelo admin quando deixar de usar.


3. Leitura para conferência (módulos MageShop)

Os módulos MageShop_PriceApi e MageShop_StockApi expõem endpoints customizados de extração em massa. São otimizados para conferência e reconciliação de catálogo, sem o custo do endpoint genérico /V1/products.

3.1. Paginação

Paginação é opt-in. Se page_size não for enviado, a resposta traz todos os registros (legado).

Parâmetro Tipo Default Máximo
page_size int 10.000
current_page int 1
  • Valores acima do máximo são clampeados silenciosamente.
  • A resposta é sempre um array JSON; não há total_count. Para detectar fim das páginas, itere até receber [].
  • Page size recomendado: 1000 para os dois endpoints abaixo.

3.2. GET /V1/product-prices (MageShop_PriceApi)

Lista preços base (price) de produtos simples, expandidos por store_id.

Parâmetros (query string)

Nome Tipo Descrição
website_id int Restringe ao website informado.
sku_like string Filtro LIKE SQL no SKU. Use % como wildcard (URL-encode → %25).
page_size int Ativa paginação. Máx. 10.000.
current_page int Página (1-based). Só usado com page_size.

Exemplo

curl -H "Authorization: Bearer $TOKEN" \
  'https://loja.com/rest/V1/product-prices?website_id=1&sku_like=ABC-%25&page_size=1000&current_page=1'

Resposta

[
  { "sku": "ABC-001", "store_id": "1", "attribute_code": "price", "price": "199.9000" },
  { "sku": "ABC-001", "store_id": "2", "attribute_code": "price", "price": "189.9000" }
]

Notas

  • price aplica COALESCE(override_da_loja, default) — se a loja tem preço sobrescrito, vem o da loja; caso contrário, o global.
  • Não inclui special_price, tier_price ou cost. Para consultar promoções, ver seção 5.3.
  • Ordenação: sku ASC, attribute_code ASC, store_id ASC.

3.3. GET /V1/product-stocks/legacy (MageShop_StockApi)

Lista estoque, lendo de cataloginventory_stock_status. Em lojas com estoque único, este endpoint é suficiente para conferência — não há sources[] aninhado para apresentar.

Parâmetros

Nome Tipo Descrição
website_id int Restringe ao website.
sku_like string Filtro LIKE no SKU.
page_size int Ativa paginação. Máx. 10.000.
current_page int Página (1-based).

Exemplo

curl -H "Authorization: Bearer $TOKEN" \
  'https://loja.com/rest/V1/product-stocks/legacy?website_id=1&page_size=1000'

Resposta

[
  {
    "sku":         "ABC-001",
    "store_id":    "1",
    "website_id":  "1",
    "stock_id":    "1",
    "salable_qty": "12.0000",
    "is_salable":  "1"
  }
]

Notas

  • stock_id é sempre 1 neste modelo.
  • is_salable: 1 (disponível) ou 0 (esgotado).
  • salable_qty vem como string — converter no consumidor.

4. Escrita — Estoque

4.1. Endpoint principal — POST /V1/inventory/source-items

Em Magento 2.3+, o módulo Magento_Inventory vem habilitado por padrão com um source chamado default que representa o estoque único da loja. Isso permite usar o endpoint nativo de inventory com batch real, mesmo em lojas que não operam com múltiplos depósitos — basta fixar source_code: "default" em todas as entradas.

POST /rest/V1/inventory/source-items
Content-Type: application/json
Authorization: Bearer {token}

ACL: Magento_InventoryApi::source_items

Payload

{
  "sourceItems": [
    { "sku": "ABC-001", "source_code": "default", "quantity": 10, "status": 1 },
    { "sku": "ABC-002", "source_code": "default", "quantity": 0,  "status": 0 },
    { "sku": "ABC-003", "source_code": "default", "quantity": 5,  "status": 1 }
  ]
}
Campo Tipo Obrigatório Descrição
sku string sim SKU do produto.
source_code string sim Fixar em "default" — único source existente em lojas com estoque único.
quantity decimal sim Quantidade disponível.
status int sim 1 = em estoque, 0 = esgotado.

Em lojas com estoque único, sempre use source_code: "default". Usar qualquer outro código retornará erro por item (Source "%1" does not exist.).

Resposta

  • Sucesso total: []
  • Falha parcial:
[
  { "message": "Source \"%1\" does not exist.", "parameters": ["deposito_xx"] }
]

4.2. Recomendações

💡 O endpoint aceita batch real. A otimização vem de consolidar SKUs por chamada + delta + paralelismo controlado.

  1. Lotes consolidados — 100 a 200 SKUs por requisição

    • Em estoque único, cada SKU consome apenas uma posição do array (não há múltiplos sources).
    • Sweet spot: 100–200 itens. Lotes muito grandes (>500) começam a atingir timeouts de proxy/PHP.
  2. Enviar apenas o delta

    • Atualizar somente SKUs cuja quantidade ou status mudou desde a última sincronização.
    • Manter no ERP um snapshot da última carga enviada e comparar antes de disparar.
    • Isoladamente, esta é a otimização de maior impacto: tipicamente reduz o volume em 80–95%.
  3. Paralelismo controlado

    • Máximo 1 a 2 requisições simultâneas ao endpoint.
    • O Magento serializa internamente operações sobre as mesmas linhas — paralelismo maior gera contenção em locks.
  4. Intervalo entre lotes

    • 200–500 ms entre chamadas sequenciais para evitar acúmulo na fila do PHP-FPM.
  5. Retry com backoff exponencial

    • Em 5xx/timeout, não retentar imediatamente.
    • Backoff sugerido: 1s → 2s → 4s → 8s, máximo 3–4 tentativas.
  6. Janela de sincronização

    • Preferir sincronização incremental contínua (delta a cada N minutos) em vez de cargas massivas em horários cheios.
    • Cargas full (reconciliação diária) → agendar fora dos horários de pico.

4.3. Notas operacionais

  • salable_qty é assíncronosalable_qty consultado via /V1/product-stocks/legacy só reflete a mudança após o indexer rodar. Geralmente isso é automático em segundos. Para forçar: bin/magento indexer:reindex inventory.
  • quantity = 0 não remove o source item — apenas zera a quantidade. Para marcar como indisponível, use status = 0.
  • Sempre inspecione a resposta — HTTP 200 pode conter falhas parciais por item (seção 6.3).

4.4. Fallback — endpoint legacy PUT /V1/products/{sku}/stockItems/{itemId}

Use apenas se o módulo Magento_Inventory estiver completamente desabilitado no ambiente — cenário raro em Magento 2.3+, mas possível em instalações customizadas.

Sinal para acionar este caminho: chamadas a /V1/inventory/source-items retornarem HTTP 400 com:

{ "message": "MSI is not enabled on this instance." }

Endpoint e payload

PUT /rest/V1/products/{sku}/stockItems/{itemId}
{
  "stockItem": {
    "qty": 25,
    "is_in_stock": true
  }
}

⚠️ Este endpoint não aceita batch — cada requisição atualiza um único SKU. As otimizações neste fallback são focadas em reduzir o volume total de requisições (delta + paralelismo controlado).

Obtenção do itemId (stock_item_id)

itemId é o stock_item_id, atribuído pelo Magento na criação do produto. Não é igual ao ID do produto.

GET /rest/V1/stockItems/{sku}
  • Cachear o mapeamento sku → stock_item_id no lado do ERP.
  • Refazer a busca apenas em caso de erro 404 ou quando um SKU novo for sincronizado pela primeira vez.

Recomendações neste fallback

  1. Delta apenas (mesmo princípio do caminho principal).
  2. Paralelismo controlado — máximo 2 a 4 requisições simultâneas (pode ser maior que no batch porque cada requisição toca um único SKU).
  3. Retry com backoff exponencial — 1s → 2s → 4s → 8s.
  4. Janela de sincronização — incremental contínua, evitar cargas massivas em horários de pico.

5. Escrita — Preços

5.1. Preço base — POST /V1/products/base-prices

⚠️ Não usar PUT /V1/products/{sku} apenas para atualizar preço. Esse endpoint processa reindexação, EAV, mídia e categorias — gera carga desnecessária.

Use o endpoint dedicado, que aceita lote real:

POST /rest/V1/products/base-prices
Content-Type: application/json
Authorization: Bearer {token}

Payload

{
  "prices": [
    { "price": 27.34, "store_id": 0, "sku": "123" },
    { "price": 49.90, "store_id": 0, "sku": "124" },
    { "price": 99.00, "store_id": 0, "sku": "125" }
  ]
}
Campo Tipo Obrigatório Descrição
sku string sim SKU do produto.
price decimal sim Novo preço base.
store_id int sim 0 = global (default), >0 = override por loja.

Sobre store_id em lojas com uma única store view

Use sempre store_id = 0 — representa o escopo global e aplica o preço a todas as views. Não é necessário replicar a linha para store_id 1, 2 etc.; isso apenas multiplica o tamanho do payload.

Boas práticas

  • Lotes: 100 a 200 SKUs por requisição.
  • Delta apenas: enviar somente os SKUs cujo preço mudou.
  • Paralelismo: máximo 1 a 2 requisições simultâneas.
  • Intervalo entre lotes: 200–500 ms entre chamadas sequenciais.
  • Retry com backoff exponencial em 5xx/timeout.
  • Uma entrada por par sku + store_id no mesmo payload — múltiplas linhas para a mesma chave geram comportamento "última ganha" não garantido.

5.2. Preço promocional — POST /V1/products/special-price

Cria ou atualiza special_price com janela de vigência opcional.

Payload

{
  "prices": [
    {
      "sku":        "411",
      "price":      13,
      "store_id":   0,
      "price_from": "2025-11-19 00:00:00",
      "price_to":   ""
    }
  ]
}
Campo Tipo Obrigatório Descrição
sku string sim SKU.
price decimal sim Valor da promoção.
store_id int sim 0 = global.
price_from string recomendado Início (YYYY-MM-DD HH:MM:SS).
price_to string opcional Fim. "" ou omitido = promoção sem data fim.

Notas

  • O special_price só é exibido se price_from <= now <= price_to (quando price_to está definido).
  • O Magento não valida que special_price < price base; a vitrine simplesmente exibe o menor.
  • Evite enviar null para price_to na criação — alguns clientes serializam como string "null". Use "" ou omita.

5.3. Consultar preços promocionais — POST /V1/products/special-price-information

Apesar do verbo POST, é leitura — o POST é exigido porque o payload tem array.

{ "skus": ["411"] }

Resposta

[
  {
    "price":      13,
    "store_id":   0,
    "sku":        "411",
    "price_from": "2025-11-19 00:00:00",
    "price_to":   null
  }
]
  • Array vazio [] = nenhum SKU consultado tem promoção configurada.
  • Um SKU pode aparecer múltiplas vezes (uma por store_id com promoção distinta).

5.4. Remover preços promocionais — POST /V1/products/special-price-delete

A correspondência é feita por sku + store_id + price + price_from + price_totodos precisam bater com o registro existente, caso contrário a remoção é silenciosamente ignorada.

Payload

{
  "prices": [
    {
      "sku":        "411",
      "price":      13,
      "store_id":   0,
      "price_from": "2025-11-19 00:00:00",
      "price_to":   null
    }
  ]
}

💡 Fluxo recomendado: chamar special-price-information primeiro para obter os campos exatos, depois passar o objeto retornado como payload de special-price-delete. É idempotente — não erra se o registro não existir.


6. Erros e códigos HTTP

6.1. Códigos

Código Quando Corpo (resumido)
200 Sucesso (mesmo se array vazio). [] ou [ {...}, ... ]
401 Token ausente ou inválido. { "message": "Consumer is not authorized..." }
403 Token sem a ACL exigida. { "message": "..." }
400 Payload inválido ou pré-requisito não atendido (ex.: MSI desabilitado). { "message": "..." }
500 Erro inesperado. { "message": "..." }

6.2. Erro global — MSI desabilitado

Indica que source-items não pode ser usado neste ambiente. Acionar o fallback descrito em 4.4.

{ "message": "MSI is not enabled on this instance." }

6.3. Erros parciais (endpoints de escrita em lote)

Endpoints de escrita (source-items, base-prices, special-price etc.) retornam HTTP 200 mesmo quando alguns itens falham. O resultado é um array por item:

[
  {
    "message": "Invalid attribute value. Row ID: SKU = %1, Store ID: %2",
    "parameters": ["411", "99"]
  },
  {
    "message": "Source \"%1\" does not exist.",
    "parameters": ["deposito_xx"]
  }
]
  • Array vazio [] → todos os itens foram aceitos.
  • Array com objetos → cada objeto descreve um item que falhou.
  • Sempre inspecione o tamanho do array nos endpoints de escrita — HTTP 200 não significa sucesso total.

7. Receitas práticas

7.1. Extração completa paginada (Python)

import requests

TOKEN = "..."
BASE  = "https://loja.com/rest/V1/product-prices"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}

page = 1
out  = []
while True:
    r = requests.get(BASE, headers=HEADERS,
                     params={"page_size": 1000, "current_page": page})
    r.raise_for_status()
    chunk = r.json()
    if not chunk:
        break
    out.extend(chunk)
    page += 1

print(f"Total: {len(out)} registros")

7.2. Atualização em massa de estoque (Python)

Lê CSV com sku,quantity,status e envia em lotes de 200 com source_code fixo em default.

import requests, csv

TOKEN = "..."
URL   = "https://loja.com/rest/default/V1/inventory/source-items"
HDRS  = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

# Carrega CSV: sku,quantity,status
items = []
with open("estoque.csv") as f:
    for row in csv.DictReader(f):
        items.append({
            "sku":         row["sku"],
            "source_code": "default",
            "quantity":    float(row["quantity"]),
            "status":      int(row["status"]),
        })

# Envia em lotes de 200
for i in range(0, len(items), 200):
    batch = items[i:i+200]
    r = requests.post(URL, headers=HDRS, json={"sourceItems": batch}, timeout=120)
    r.raise_for_status()
    errors = r.json()
    if errors:
        print(f"Lote {i}: {len(errors)} erros")
        for e in errors:
            print(" -", e["message"], e.get("parameters"))

7.3. Sync incremental por prefixo de SKU (Bash)

for prefix in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9; do
  curl -s -H "Authorization: Bearer $TOKEN" \
    "https://loja.com/rest/V1/product-stocks/legacy?sku_like=${prefix}%25" \
    > "stock_legacy_${prefix}.json"
done

7.4. Atualização de preço base seguindo extração

# 1) Extrai preços atuais
curl -s -H "Authorization: Bearer $TOKEN" \
  'https://loja.com/rest/V1/product-prices?sku_like=ABC-%25' > current.json

# 2) Gera payload com aumento de 10% (jq)
jq '{ prices: [ .[] | { sku, store_id: (.store_id|tonumber), price: ((.price|tonumber) * 1.10) } ] }' \
  current.json > new_prices.json

# 3) Aplica
curl -X POST 'https://loja.com/rest/V1/products/base-prices' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  --data @new_prices.json

7.5. Remover promoção a partir da consulta

# 1) Consulta promoções ativas
INFO=$(curl -s -X POST 'https://loja.com/rest/V1/products/special-price-information' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  --data '{ "skus": ["411"] }')

# 2) Usa o próprio retorno como payload de delete
curl -X POST 'https://loja.com/rest/V1/products/special-price-delete' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  --data "{ \"prices\": $INFO }"

8. Resumo das recomendações

Item Forma ineficiente Forma recomendada
Leitura de preços em massa GET /V1/products (pesado) GET /V1/product-prices (MageShop)
Leitura de estoque em massa GET /V1/products ou /V1/stockItems/{sku} por SKU GET /V1/product-stocks/legacy (MageShop)
Endpoint de escrita de estoque PUT /V1/products/{sku}/stockItems/{itemId} (per-SKU) POST /V1/inventory/source-items com source_code: "default" (lote)
Endpoint de escrita de preço PUT /V1/products/{sku} POST /V1/products/base-prices (lote)
source_code em estoque Fixo em "default"
store_id em preço Repetido em várias views Somente store_id = 0 (loja com store view única)
SKUs enviados Todos, sempre Apenas os que mudaram (delta)
Lotes (estoque e preço) Várias requisições, poucos itens 100 a 200 itens por requisição
Paralelismo (estoque batch) Não controlado Máx. 1–2 requisições simultâneas
Paralelismo (estoque fallback per-SKU) Não controlado Máx. 2–4 requisições simultâneas
Paralelismo (preços) Não controlado Máx. 1–2 requisições simultâneas
Intervalo entre lotes Sem controle 200–500 ms
Retry Imediato Backoff exponencial (1s→2s→4s→8s)

9. Boas práticas e resultado esperado

Leitura

  • Sempre pagine em produção — catálogos com 50k+ SKUs × N stores podem estourar memória do PHP-FPM e timeout de proxy.
  • Use filtros agressivamente (website_id, sku_like) para reduzir o custo SQL no banco.
  • URL-encode wildcards% precisa virar %25 na URL (ABC-%25ABC-%).
  • Evite polling intenso — endpoints são read-only, mas executam SQL pesado.

Escrita

  • Inspecione sempre a resposta — HTTP 200 pode conter falhas parciais (seção 6.3).
  • Uma entrada por chave única no payload: sku + source_code em estoque (source_code sempre "default"), sku + store_id em preços.
  • Reindex após escritas em massa — após carga grande em source-items, o salable_qty só reflete depois do indexer rodar (geralmente automático).
  • Token de integration, não de admin, para clientes externos persistentes.
  • Idempotência — reenviar o mesmo payload não duplica registros. special-price-delete é totalmente idempotente.

Resultado esperado da adoção das recomendações

  • Redução significativa do número total de requisições por minuto (via batch + delta).
  • Menor consumo de CPU e banco de dados na aplicação Magento.
  • Menor risco de timeouts, bloqueios e degradação para o cliente final.
  • Sincronizações de catálogo mais rápidas e previsíveis.
  • Reconciliação ERP ↔ Magento simplificada pelos endpoints de leitura em massa.

Em caso de dúvidas sobre a implementação ou sobre limites adequados de paralelismo para o ambiente, fica à disposição o time de integração.

Copiar para usar com IA

Este artigo foi útil?

Obrigado pelo seu feedback!