GET · POST · OPTIONS · single + multi items

API de Cotação
de Frete

Consulte opções de entrega em tempo real usando a Shopify Ajax API com resolução flexível de variantes. A API aceita variantId, handle + options, productUrl + options, sku e também carrinho com múltiplas linhas, sem integração direta com a API da Frenet.

Versão 3.0
Escopo Brasil
Formato JSON / UTF-8
Resolução variantId · handle · productUrl · sku
GET POST OPTIONS
01

Visão geral

A Quote Shipping API permite consultar opções de frete para um item isolado ou para um carrinho com múltiplas variantes. O fluxo é completamente server-side: cada request cria uma sessão isolada de carrinho, resolve o CEP, adiciona os itens na Shopify, consulta as tarifas e limpa o carrinho antes do encerramento da execução.

Entrada recomendada
productUrl + options + cep
País suportado
Brasil
Linhas por request
Até 20
Quantidade por linha
Até 50
O endpoint agora suporta múltiplas variantes, deduplicação de linhas repetidas, resolução por URL do produto e SKU global quando a Storefront API estiver configurada.
02

Arquitetura do fluxo

1
Recepção e validação Cliente envia cep e uma ou mais linhas de carrinho. A API aceita variantId, handle, productUrl, sku, options, option1, option2 e option3.
2
Normalização do carrinho Converte a entrada em items[], aplica validações, soma variantes repetidas e limita a requisição a no máximo 20 linhas e 50 unidades por linha.
3
Resolução da variante Resolve a variante por prioridade: variantIdhandle/productUrl + skuhandle/productUrl + optionshandle/productUrl em produto univariado → sku global via Storefront API.
4
Resolução de CEP Resolve a UF via ViaCEP com timeout de 3,5s. Em caso de falha, tenta BrasilAPI como fallback.
5
Sessão isolada na Shopify Abre uma sessão isolada de carrinho usando cookies em memória. O carrinho é limpo no início e também no encerramento da requisição.
6
Adição ao carrinho Adiciona todas as linhas normalizadas via /cart/add.js em uma única sessão de carrinho.
7
Consulta assíncrona de frete Chama /cart/prepare_shipping_rates.json e faz polling em /cart/async_shipping_rates.json por até 10 tentativas de 1s.
8
Fallback síncrono e resposta Se o fluxo assíncrono não retornar tarifas, faz fallback para /cart/shipping_rates.json. Depois normaliza os fretes, aplica excludePickup se necessário e devolve o JSON padronizado.
03

Modos de resolução de variantes

A API aceita várias formas de identificar a variante. Para operação, o formato mais amigável é productUrl + options. O formato por variantId continua disponível para integrações técnicas e retrocompatibilidade.

Modo Entrada mínima resolved_by Quando usar
Direto por variante variantId variantId Integrações técnicas ou quando o ID já é conhecido
Handle + SKU handle + sku handle+sku SKU conhecido dentro de um produto específico
Product URL + SKU productUrl + sku productUrl+sku Mesmo cenário acima, mas usando URL do produto
Handle + options handle + options ou option1/2/3 handle+options Quando cor/tamanho são conhecidos
Product URL + options productUrl + options ou option1/2/3 productUrl+options Modo recomendado para o time de atendimento
Handle puro handle handle Só funciona quando o produto tem uma única variante utilizável
Product URL pura productUrl productUrl Mesmo comportamento do handle puro
SKU global sku sku Exige SHOPIFY_STOREFRONT_ACCESS_TOKEN e SKU único na loja
Recomendação operacional: usar productUrl + options ou handle + option1/option2. Isso elimina a necessidade de descobrir variantId manualmente.
04

Dependências

Dependência Uso Obrigatória Observação
Shopify Ajax API Sessão de carrinho e cálculo de frete Sim Usa /cart/add.js, prepare_shipping_rates e async_shipping_rates
Shopify Product .js Resolução de variantes por handle Quando usar handle/productUrl Usa /products/{handle}.js
ViaCEP Resolução principal do CEP Não Primeira tentativa
BrasilAPI Fallback do CEP Não Usada quando ViaCEP falha
Shopify Storefront API Busca global por SKU Opcional Só necessária para sku sem handle/productUrl
Vercel / Serverless Hospedagem da função Sim Não faz parte do contrato HTTP
05

Configuração do ambiente

Variável Obrigatória Exemplo Descrição
SHOPIFY_STORE_URL Sim https://www.usereise.com.br URL base da loja Shopify
API_TOKEN Opcional reise_frete_2026_9xK7mP4Q Se preenchida, exige Bearer Token em todas as chamadas
SHOPIFY_STOREFRONT_ACCESS_TOKEN Opcional shpat_ou_storefront_token Habilita busca global por sku
SHOPIFY_STOREFRONT_API_TOKEN Opcional alias do token Alias aceito no código para o mesmo token de Storefront
SHOPIFY_STOREFRONT_API_VERSION Opcional 2025-10 Versão da Storefront API. Default: 2025-10
Sem SHOPIFY_STORE_URL, a API responde 500. Sem Storefront token, a API continua funcionando, mas o modo de resolução por sku global fica indisponível.
06

Base URL

Produção
https:// shipping.cecilia.digital/api/quote-shipping
Preview / Homologação
https:// <deploy-preview>/api/quote-shipping
Local
http:// localhost:3000/api/quote-shipping
07

Autenticação

HTTP Header
Authorization: Bearer <API_TOKEN>
08

Endpoint principal

Método Rota Auth Descrição
GET /api/quote-shipping Bearer opcional Consulta simples via query string. Recomendado só para requests de uma linha.
POST /api/quote-shipping Bearer opcional Consulta por JSON, incluindo múltiplos itens.
OPTIONS /api/quote-shipping Não Preflight CORS
09

Parâmetros

O endpoint aceita dois formatos principais: payload simples e payload com items[]. Quando items[] é enviado, a cotação passa a considerar o carrinho inteiro.

Campos de topo

Campo Tipo Obrigatório Default Descrição
cep / zip string Sim CEP brasileiro. A API remove não numéricos e exige 8 dígitos.
excludePickup boolean / string Não false Quando true, remove opções de retirada/pickup do retorno.
items / lines array Não Lista de linhas do carrinho. Quando omitida, a API tenta resolver a linha a partir do próprio payload de topo.
quantity / qty integer Não 1 Quantidade da linha quando o payload simples é usado.

Campos aceitos em cada item

Campo Obrigatório Aliases Descrição
variantId Não variant_id, id Identificação direta da variante.
handle Não productHandle Handle do produto Shopify.
productUrl Não product_url, url URL da página do produto. A API extrai o handle automaticamente.
sku Não SKU da variante. Pode ser usado junto com handle/productUrl ou de forma global.
options Não selectedOptions Objeto, array ou JSON string com as opções nomeadas da variante.
option1, option2, option3 Não Formas posicionais de informar as opções da variante.
cor, color, tamanho, size Não Atalhos para opções nomeadas comuns.
quantity Não qty Quantidade da linha. Deve ser inteiro entre 1 e 50.
Se items[] contiver variantes repetidas, a API soma as quantidades e devolve deduplicated=true dentro de cart.
10

Exemplos de request

bash
curl -X POST "https://shipping.cecilia.digital/api/quote-shipping" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "cep": "14409541",
    "excludePickup": true,
    "productUrl": "https://www.usereise.com.br/products/tenis-casual-nobuck-marrom-911",
    "options": {
      "Cor": "Marrom",
      "Tamanho de calçado": "41"
    },
    "quantity": 1
  }'
javascript
const response = await fetch("https://shipping.cecilia.digital/api/quote-shipping", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <API_TOKEN>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    cep: "14409541",
    excludePickup: true,
    items: [
      {
        productUrl: "https://www.usereise.com.br/products/tenis-casual-nobuck-marrom-911",
        option1: "Marrom",
        option2: "41",
        quantity: 1
      },
      {
        variantId: 46526582096120,
        quantity: 1
      }
    ]
  })
});

const data = await response.json();
console.log(data);
powershell
$headers = @{
  Authorization = "Bearer <API_TOKEN>"
}

$endpoint = "https://shipping.cecilia.digital/api/quote-shipping"

$body = @{
  cep = "14409541"
  excludePickup = $true
  handle = "tenis-casual-nobuck-marrom-911"
  option1 = "Marrom"
  option2 = "41"
  quantity = 1
} | ConvertTo-Json -Depth 10

$response = Invoke-RestMethod -Method POST -Uri $endpoint -Headers $headers -ContentType "application/json" -Body $body

$response.cart.resolved_items | Select-Object variantId, resolved_by, product_handle, variant_title, sku | Format-Table -AutoSize
$response.rates | Select-Object name, price_brl, delivery_days, delivery_date, source | Format-Table -AutoSize
http
POST /api/quote-shipping HTTP/1.1
Host: shipping.cecilia.digital
Authorization: Bearer <API_TOKEN>
Content-Type: application/json

{
  "cep": "14409541",
  "excludePickup": true,
  "items": [
    {
      "productUrl": "https://www.usereise.com.br/products/tenis-casual-nobuck-marrom-911",
      "option1": "Marrom",
      "option2": "41",
      "quantity": 1
    },
    {
      "sku": "911-M-41",
      "handle": "tenis-casual-nobuck-marrom-911",
      "quantity": 1
    }
  ]
}
GET — uso simples
GET /api/quote-shipping?handle=tenis-casual-nobuck-marrom-911&option1=Marrom&option2=41&cep=14409541&quantity=1&excludePickup=true
Para múltiplas linhas, prefira sempre POST. O método GET é mais indicado para requests simples de uma linha.
11

Resposta de sucesso

200 OK application/json
{
  "success": true,
  "input": {
    "store_url": "https://www.usereise.com.br",
    "cep": "14409541",
    "uf": "SP",
    "province": "Sao Paulo",
    "cep_provider": "ViaCEP",
    "excludePickup": true,
    "mode": "single_legacy",
    "resolution_methods": ["handle+options"],
    "lines_count": 1,
    "item_count": 1,
    "max_lines_per_request": 20,
    "max_quantity_per_line": 50,
    "variantId": 46522942390520,
    "quantity": 1
  },
  "cart": {
    "items": [
      {
        "variantId": 46522942390520,
        "quantity": 1
      }
    ],
    "deduplicated": false,
    "resolved_items": [
      {
        "variantId": 46522942390520,
        "quantity": 1,
        "resolved_by": "handle+options",
        "product_handle": "tenis-casual-nobuck-marrom-911",
        "product_title": "Tênis Casual Nobuck Marrom 911",
        "variant_title": "Marrom / 41",
        "sku": "911-M-41"
      }
    ]
  },
  "warnings": [],
  "rates": [
    {
      "name": "Gollog",
      "code": "GOL_15",
      "price": "14.28",
      "price_brl": "R$ 14,28",
      "source": "Frenet",
      "delivery_date": "2026-04-10",
      "delivery_days": 3,
      "delivery_range": ["2026-04-10", "2026-04-10"],
      "currency": "BRL"
    }
  ]
}
Campo Tipo Contexto Descrição
successbooleanraizIndica sucesso da operação
inputobjectraizParâmetros efetivamente utilizados e metadados de execução
cartobjectraizCarrinho efetivamente cotado após deduplicação
warningsarrayraizAvisos não bloqueantes da resolução
ratesarrayraizLista de opções de frete
cart.itemsarraycartLinhas finais usadas na cotação
cart.deduplicatedbooleancartIndica se houve consolidação de variantes repetidas
cart.resolved_itemsarraycartExplica como cada linha foi resolvida
input.modestringinputModo de entrada: single_legacy ou multi_items
input.resolution_methodsarrayinputMétodos de resolução aplicados às linhas
name, price_brl, delivery_daysstring / numberrates[]Campos mais indicados para exibição ao usuário final
12

Respostas de erro

HTTP Mensagem típica Quando ocorre
400 cep is required and must have 8 digits CEP ausente ou inválido
400 items must be an array Campo items inválido
400 items exceeds the maximum allowed lines per request (20) Mais de 20 linhas no carrinho
400 items[0].quantity must be an integer >= 1 Quantidade inválida
400 No variant matched the provided options for handle "..." Opções não encontraram nenhuma variante
400 More than one variant matched the provided options... Opções insuficientes para desambiguar
400 handle "..." requires options because the product has multiple variants Handle puro em produto multivariado
400 Global sku lookup requires SHOPIFY_STOREFRONT_ACCESS_TOKEN... SKU global sem token da Storefront API
401 Unauthorized Token ausente ou incorreto
404 Could not load product for handle: ... Handle inexistente ou produto indisponível
405 Method not allowed Método não suportado
500 Missing SHOPIFY_STORE_URL environment variable Variável de ambiente obrigatória ausente
13

Limites operacionais

Timeouts
  • ViaCEP3,5s
  • BrasilAPI3,5s
  • Shopify request10s
  • Product lookup10s
Polling assíncrono
  • Tentativas10
  • Intervalo1s por tentativa
  • Fallbackshipping_rates.json
Validação
  • CEP8 dígitos exatos
  • Linhas por request20
  • Quantidade por linha50
Não implementado
  • Rate limit server-side
  • Cache nativo
  • CORS restrito❌ (aberto *)
14

Segurança

Ative API_TOKEN em todos os ambientes públicos. Não exponha o token em front-end público e avalie restringir CORS quando a API sair do ambiente controlado.
15

Troubleshooting

Sintoma Possível causa Ação recomendada
401 Unauthorized Token ausente ou incorreto Revisar API_TOKEN e header Authorization
400 CEP CEP inválido Revisar entrada e remover formatação incorreta
404 handle Handle inexistente ou URL de produto inválida Conferir o slug do produto e se a URL contém /products/{handle}
400 options Opções insuficientes ou incorretas Usar option1/option2 ou passar os nomes exatos das opções do produto
400 sku SKU global sem token ou SKU duplicado Adicionar Storefront token ou informar handle/productUrl junto do SKU
No shipping rates Sem opções de frete para o CEP ou regras logísticas específicas Testar outro CEP, revisar pickup e conferir regras logísticas da loja
Resposta lenta Latência do polling assíncrono ou dos serviços de CEP Monitorar tempo de resposta e considerar cache para combinações recorrentes
source = Frenet Origem das tarifas configurada na Shopify Comportamento esperado
16

Checklist de homologação

Para exibir ao usuário final, prefira name + price_brl + delivery_days. Para depuração e auditoria, use cart.resolved_items e input.resolution_methods.