Como Integrar a API SP da Amazon: Tutorial Passo a Passo

Ashley Innocent

Ashley Innocent

20 março 2026

Como Integrar a API SP da Amazon: Tutorial Passo a Passo

RESUMO

A Amazon Selling Partner API (SP-API) é uma API REST que permite acesso programático a dados de vendedores para pedidos, estoque, listagens e fulfillment. Ela utiliza autenticação OAuth 2.0 com funções IAM, exige a assinatura AWS SigV4 e impõe limites de taxa que variam por endpoint (0,1 a 100 requisições por segundo). Este guia abrange a configuração da conta, autenticação, endpoints principais, assinaturas de webhook e estratégias de implantação em produção.

Introdução

A Amazon processa mais de 350 milhões de produtos em mais de 200 marketplaces em todo o mundo. Para desenvolvedores que criam ferramentas de e-commerce, sistemas de gerenciamento de estoque ou plataformas de análise, a integração com a Amazon SP-API não é opcional. É essencial.

Aqui está a realidade: vendedores que gerenciam operações na Amazon perdem de 20 a 30 horas semanais com entrada manual de dados em pedidos, estoque e listagens. Uma integração SP-API robusta automatiza a sincronização de pedidos, atualizações de estoque e gerenciamento de listagens em vários marketplaces.

Este guia detalha o processo completo de integração da Amazon SP-API. Você aprenderá a configurar funções IAM, autorização OAuth 2.0, assinatura de requisições AWS SigV4, gerenciamento de pedidos e estoque, assinaturas de notificação e solução de problemas. Ao final, você terá uma integração Amazon pronta para produção.

💡
Apidog simplifica o teste de integração de API. Teste seus endpoints SP-API, valide fluxos OAuth, inspecione assinaturas de requisição e depure problemas de autenticação em um único ambiente de trabalho. Importe especificações de API, simule respostas e compartilhe cenários de teste com sua equipe.
botão

O Que É a Amazon SP-API?

Amazon Selling Partner API (SP-API) é uma API REST que fornece acesso programático a dados do Seller Central. Ela substituiu o antigo Marketplace Web Service (MWS) com segurança, desempenho e funcionalidade aprimorados.

Principais Funcionalidades

A SP-API gerencia:

Comparativo SP-API vs MWS

Funcionalidade SP-API MWS (Legado)
Arquitetura JSON RESTful Baseado em XML
Autenticação OAuth 2.0 + IAM Token de Autenticação MWS
Segurança Assinatura AWS SigV4 Tokens simples
Limites de Taxa Dinâmico por endpoint Cotas fixas
Marketplaces Endpoints unificados Específicos da região
Status Atual Obsoleto (Dez 2025)

Migre quaisquer integrações MWS para a SP-API imediatamente. A Amazon anunciou a desativação completa do MWS para dezembro de 2025.

Visão Geral da Arquitetura da API

A Amazon utiliza uma estrutura de API regional com autorização centralizada:

https://sellingpartnerapi-na.amazon.com (América do Norte)
https://sellingpartnerapi-eu.amazon.com (Europa)
https://sellingpartnerapi-fe.amazon.com (Extremo Oriente)

Todas as requisições exigem:

  1. Assinatura AWS SigV4
  2. Token de acesso do fluxo OAuth
  3. Permissões adequadas de função IAM
  4. ID de requisição para rastreamento

Marketplaces Suportados

Região Marketplaces Endpoint da API
América do Norte EUA, CA, MX sellingpartnerapi-na.amazon.com
Europa Reino Unido, Alemanha, França, Itália, Espanha, Holanda, Suécia, Polônia, Turquia, Egito, Índia, Emirados Árabes Unidos, Arábia Saudita sellingpartnerapi-eu.amazon.com
Extremo Oriente Japão, Austrália, Cingapura, Brasil sellingpartnerapi-fe.amazon.com

Primeiros Passos: Configuração da Conta e IAM

Passo 1: Crie Sua Conta de Desenvolvedor Amazon

Antes de acessar a SP-API, você precisa de acesso adequado à conta:

  1. Visite o Amazon Developer Central
  2. Faça login com sua conta Amazon (deve ter acesso ao Seller Central)
  3. Navegue até Selling Partner API no painel
  4. Aceite o Contrato de Desenvolvedor

Passo 2: Registre Seu Aplicativo

Crie um perfil de aplicativo no Seller Central:

  1. Faça login no Seller Central
  2. Navegue até Aplicativos e Serviços > Desenvolver Aplicativos
  3. Clique em Adicionar Novo Aplicativo
  4. Preencha os detalhes do aplicativo:

Após a submissão, você receberá:

Nota de segurança: Armazene as credenciais em variáveis de ambiente, nunca no código:

# .env file
AMAZON_APPLICATION_ID="amzn1.application.xxxxx"
AMAZON_CLIENT_ID="amzn1.account.xxxxx"
AMAZON_CLIENT_SECRET="your_client_secret_here"
AMAZON_SELLER_ID="your_seller_id_here"
AWS_ACCESS_KEY_ID="your_aws_access_key"
AWS_SECRET_ACCESS_KEY="your_aws_secret_key"
AWS_REGION="us-east-1"

Passo 3: Crie uma Função IAM para a SP-API

A SP-API exige uma função IAM com permissões específicas:

  1. Faça login no Console AWS IAM
  2. Navegue até Funções > Criar Função
  3. Selecione Outra conta AWS como entidade confiável
  4. Insira o ID da conta da Amazon para sua região:

Passo 4: Configure a Política IAM

Anexe esta política à sua função IAM:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "execute-api:Invoke"
      ],
      "Resource": [
        "arn:aws:execute-api:*:*:*/prod/*/sellingpartnerapi/*"
      ]
    }
  ]
}

Nomeie sua função com algo descritivo, como SellingPartnerApiRole, e anote o ARN.

Passo 5: Vincule a Função IAM ao Aplicativo

Conecte sua função IAM ao aplicativo SP-API:

  1. Retorne ao Seller Central > Desenvolver Aplicativos
  2. Selecione seu aplicativo
  3. Clique em Editar > ARN da Função IAM
  4. Insira o ARN da sua função IAM
  5. Salve as alterações

A Amazon valida a função IAM em minutos. Você verá um status “Vinculado” quando estiver pronto.

Fluxo de Autenticação OAuth 2.0

Entendendo o OAuth da SP-API

A Amazon usa OAuth 2.0 para autorização. Aqui está o fluxo completo:

1. O vendedor clica em "Autorizar" no seu aplicativo
2. Seu aplicativo redireciona para a URL de autorização da Amazon
3. O vendedor faz login e concede permissões
4. A Amazon redireciona de volta com o código de autorização
5. Seu aplicativo troca o código por um token LWA (Login with Amazon)
6. Seu aplicativo troca o token LWA por um token de acesso SP-API
7. Seu aplicativo usa o token de acesso para chamadas de API (assinadas com SigV4)
8. Atualize o token quando o token de acesso expirar (1 hora)

Passo 6: Gerar URL de Autorização

Crie a URL de autorização OAuth:

const generateAuthUrl = (clientId, redirectUri, state) => {
  const baseUrl = 'https://www.amazon.com/sp/apps/oauth/authorize';
  const params = new URLSearchParams({
    application_id: process.env.AMAZON_APPLICATION_ID,
    client_id: clientId,
    redirect_uri: redirectUri,
    state: state, // String aleatória para proteção CSRF
    scope: 'sellingpartnerapi::notifications'
  });

  return `${baseUrl}?${params.toString()}`;
};

// Uso
const authUrl = generateAuthUrl(
  process.env.AMAZON_CLIENT_ID,
  'https://your-app.com/callback',
  crypto.randomBytes(16).toString('hex')
);

console.log(`Redirecionar usuário para: ${authUrl}`);

Escopos OAuth Necessários

Solicite apenas as permissões que seu aplicativo precisa:

Escopo Descrição Caso de Uso
sellingpartnerapi::notifications Receber notificações Assinaturas de Webhook
sellingpartnerapi::migration Migrar do MWS Integrações legadas

A maioria dos acessos à API é controlada por políticas IAM, não por escopos OAuth.

Passo 7: Trocar Código por Token LWA

Lide com o callback OAuth e troque o código de autorização:

const exchangeCodeForLwaToken = async (code, redirectUri) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      redirect_uri: redirectUri,
      code: code
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`LWA Token Error: ${error.error_description}`);
  }

  const data = await response.json();

  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in, // Tipicamente 3600 segundos (1 hora)
    token_type: data.token_type
  };
};

// Lidar com a rota de callback
app.get('/callback', async (req, res) => {
  const { spapi_oauth_code, state } = req.query;

  // Verifique se o estado corresponde ao que você enviou (proteção CSRF)
  if (state !== req.session.oauthState) {
    return res.status(400).send('Invalid state parameter');
  }

  try {
    const tokens = await exchangeCodeForLwaToken(spapi_oauth_code, 'https://your-app.com/callback');

    // Armazenar tokens no banco de dados associados ao vendedor
    await db.sellers.update(req.session.sellerId, {
      amazon_lwa_access_token: tokens.access_token,
      amazon_lwa_refresh_token: tokens.refresh_token,
      amazon_token_expires: Date.now() + (tokens.expires_in * 1000)
    });

    res.redirect('/dashboard');
  } catch (error) {
    console.error('Token exchange failed:', error);
    res.status(500).send('Authentication failed');
  }
});

Passo 8: Trocar Token LWA por Credenciais SP-API

Use o token de acesso LWA para obter credenciais AWS temporárias:

const assumeRole = async (lwaAccessToken) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      scope: 'sellingpartnerapi::notifications'
    })
  });

  const data = await response.json();

  // Exchange for AWS credentials via STS
  const stsResponse = await fetch('https://sts.amazonaws.com/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': `Bearer ${data.access_token}`
    },
    body: new URLSearchParams({
      Action: 'AssumeRole',
      RoleArn: 'arn:aws:iam::YOUR_ACCOUNT:role/SellingPartnerApiRole',
      RoleSessionName: 'sp-api-session',
      Version: '2011-06-15'
    })
  });

  return stsResponse;
};

Passo 9: Implementar Renovação de Token

Tokens de acesso expiram após 1 hora. Implemente a renovação automática:

const refreshLwaToken = async (refreshToken) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      refresh_token: refreshToken
    })
  });

  const data = await response.json();

  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token, // Sempre salve o novo refresh token
    expires_in: data.expires_in
  };
};

// Middleware para garantir token válido
const ensureValidToken = async (sellerId) => {
  const seller = await db.sellers.findById(sellerId);

  // Verifique se o token expira em 5 minutos
  if (seller.amazon_token_expires < Date.now() + 300000) {
    const newTokens = await refreshLwaToken(seller.amazon_lwa_refresh_token);

    await db.sellers.update(sellerId, {
      amazon_lwa_access_token: newTokens.access_token,
      amazon_lwa_refresh_token: newTokens.refresh_token,
      amazon_token_expires: Date.now() + (newTokens.expires_in * 1000)
    });

    return newTokens.access_token;
  }

  return seller.amazon_lwa_access_token;
};

Assinatura de Requisição AWS SigV4

Entendendo o SigV4

Todas as requisições SP-API exigem a assinatura AWS Signature Version 4 (SigV4). Isso garante a autenticidade e integridade da requisição.

Processo de Assinatura SigV4

const crypto = require('crypto');

class SigV4Signer {
  constructor(accessKey, secretKey, region, service = 'execute-api') {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
    this.region = region;
    this.service = service;
  }

  sign(method, url, body = '', headers = {}) {
    const parsedUrl = new URL(url);
    const now = new Date();

    // Passo 1: Criar requisição canônica
    const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, '');
    const dateStamp = amzDate.slice(0, 8);

    headers['host'] = parsedUrl.host;
    headers['x-amz-date'] = amzDate;
    headers['x-amz-access-token'] = this.accessToken;
    headers['content-type'] = 'application/json';

    const canonicalHeaders = Object.entries(headers)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([k, v]) => `${k.toLowerCase()}:${v.trim()}`)
      .join('\n');

    const signedHeaders = Object.keys(headers)
      .sort()
      .map(k => k.toLowerCase())
      .join(';');

    const payloadHash = crypto.createHash('sha256').update(body).digest('hex');

    const canonicalRequest = [
      method.toUpperCase(),
      parsedUrl.pathname,
      parsedUrl.search.slice(1),
      canonicalHeaders,
      '',
      signedHeaders,
      payloadHash
    ].join('\n');

    // Passo 2: Criar string para assinar
    const algorithm = 'AWS4-HMAC-SHA256';
    const credentialScope = `${dateStamp}/${this.region}/${this.service}/aws4_request`;

    const stringToSign = [
      algorithm,
      amzDate,
      credentialScope,
      crypto.createHash('sha256').update(canonicalRequest).digest('hex')
    ].join('\n');

    // Passo 3: Calcular assinatura
    const kDate = this.hmac(`AWS4${this.secretKey}`, dateStamp);
    const kRegion = this.hmac(kDate, this.region);
    const kService = this.hmac(kRegion, this.service);
    const kSigning = this.hmac(kService, 'aws4_request');
    const signature = this.hmac(kSigning, stringToSign, 'hex');

    // Passo 4: Adicionar cabeçalho de autorização
    const authorization = `${algorithm} Credential=${this.accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;

    return {
      headers: {
        ...headers,
        'Authorization': authorization
      },
      canonicalRequest,
      stringToSign,
      signature
    };
  }

  hmac(key, data, encoding = 'buffer') {
    return crypto.createHmac('sha256', key).update(data).digest(encoding);
  }
}

// Uso
const signer = new SigV4Signer(
  process.env.AWS_ACCESS_KEY_ID,
  process.env.AWS_SECRET_ACCESS_KEY,
  'us-east-1'
);

const signedRequest = signer.sign('GET', 'https://sellingpartnerapi-na.amazon.com/orders/v0/orders', '', {
  'x-amz-access-token': accessToken
});

Usando o SDK da AWS para SigV4

Simplifique a assinatura com o SDK da AWS:

const { SignatureV4 } = require('@aws-sdk/signature-v4');
const { Sha256 } = require('@aws-crypto/sha256-js');

const signer = new SignatureV4({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
  },
  region: 'us-east-1',
  service: 'execute-api',
  sha256: Sha256
});

const makeSpApiRequest = async (method, endpoint, accessToken, body = null) => {
  const url = new URL(endpoint);

  const headers = {
    'host': url.host,
    'content-type': 'application/json',
    'x-amz-access-token': accessToken,
    'x-amz-date': new Date().toISOString().replace(/[:-]|\.\d{3}/g, '')
  };

  const signedRequest = await signer.sign({
    method,
    hostname: url.hostname,
    path: url.pathname,
    query: Object.fromEntries(url.searchParams),
    headers,
    body: body ? JSON.stringify(body) : undefined
  });

  const response = await fetch(endpoint, {
    method,
    headers: signedRequest.headers,
    body: signedRequest.body
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`SP-API Error: ${error.errors?.[0]?.message || response.statusText}`);
  }

  return response.json();
};

API de Pedidos

Recuperando Pedidos

Busque pedidos com opções de filtro:

const getOrders = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    createdAfter: options.createdAfter, // ISO 8601 format
    createdBefore: options.createdBefore,
    orderStatuses: options.orderStatuses?.join(',') || '',
    marketplaceIds: options.marketplaceIds?.join(',') || ['ATVPDKIKX0DER'], // EUA
    maxResultsPerPage: options.maxResultsPerPage || 100
  });

  // Remover parâmetros vazios
  for (const [key, value] of params.entries()) {
    if (!value) params.delete(key);
  }

  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Exemplo de uso
const orders = await getOrders(accessToken, {
  createdAfter: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // Últimas 24 horas
  orderStatuses: ['Unshipped', 'PartiallyShipped'],
  marketplaceIds: ['ATVPDKIKX0DER'] // Marketplace dos EUA
});

Estrutura da Resposta de Pedido

{
  "payload": {
    "orders": [
      {
        "amazon_order_id": "112-1234567-1234567",
        "seller_order_id": "ORDER-001",
        "purchase_date": "2026-03-19T10:30:00Z",
        "last_update_date": "2026-03-19T14:45:00Z",
        "order_status": "Unshipped",
        "fulfillment_channel": "AFN", // AFN = FBA, MFN = Vendedor
        "sales_channel": "Amazon.com",
        "order_channel": "Amazon.com",
        "ship_service_level": "Std US D2D Dom",
        "order_total": {
          "currency_code": "USD",
          "amount": "89.99"
        },
        "number_of_items_shipped": 0,
        "number_of_items_unshipped": 2,
        "payment_execution_detail": [],
        "payment_method": "CreditCard",
        "payment_method_details": ["CreditCard"],
        "marketplace_id": "ATVPDKIKX0DER",
        "shipment_service_level_category": "Standard",
        "easy_ship_shipment_status": null,
        "is_business_order": false,
        "is_prime": true,
        "is_premium_order": false,
        "is_global_express_enabled": false
      }
    ],
    "next_token": "eyJleHBpcmF0aW9uVGltZU9mTmV4dFRva2VuIjoxNzEwOTUwNDAwfQ=="
  }
}

Obtendo Itens do Pedido

Recupere itens de linha detalhados para um pedido:

const getOrderItems = async (accessToken, orderId) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders/${orderId}/orderItems`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Uso
const orderItems = await getOrderItems(accessToken, '112-1234567-1234567');

// Resposta esperada
{
  "payload": {
    "order_items": [
      {
        "asin": "B08N5WRWNW",
        "seller_sku": "MYSKU-001",
        "title": "Wireless Bluetooth Headphones",
        "quantity_ordered": 2,
        "quantity_shipped": 0,
        "product_info": {
          "number_of_items": 2
        },
        "item_price": {
          "currency_code": "USD",
          "amount": "44.99"
        },
        "item_total": {
          "currency_code": "USD",
          "amount": "89.98"
        },
        "tax_collection": {
          "tax_collection_model": "MarketplaceFacilitator",
          "responsible_party": "Amazon Services, Inc."
        }
      }
    ]
  }
}

Atualizando o Status da Remessa

Marque pedidos como enviados com informações de rastreamento:

const confirmShipment = async (accessToken, orderId, shipmentData) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders/${orderId}/shipmentConfirmation`;

  const payload = {
    packageDetails: {
      packageReferenceId: shipmentData.packageReferenceId || '1',
      carrier_code: shipmentData.carrierCode, // ex: 'USPS', 'FEDEX', 'UPS'
      tracking_number: shipmentData.trackingNumber,
      ship_date: shipmentData.shipDate || new Date().toISOString(),
      items: shipmentData.items.map(item => ({
        order_item_id: item.orderItemId,
        quantity: item.quantity
      }))
    }
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Uso
await confirmShipment(accessToken, '112-1234567-1234567', {
  carrierCode: 'USPS',
  trackingNumber: '9400111899223456789012',
  items: [
    { orderItemId: '12345678901234', quantity: 2 }
  ]
});

Códigos de Transportadora Comuns

Transportadora Código da Transportadora
USPS USPS
FedEx FEDEX
UPS UPS
DHL DHL
Correios do Canadá CANADA_POST
Royal Mail ROYAL_MAIL
Correios da Austrália AUSTRALIA_POST
Logística da Amazon AMZN_UK

API de Estoque

Obtendo Resumos de Estoque

Busque níveis de estoque em todos os marketplaces:

const getInventorySummaries = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    granularityType: options.granularityType || 'Marketplace',
    granularityId: options.granularityId || 'ATVPDKIKX0DER', // EUA
    startDateTime: options.startDateTime || '',
    sellerSkus: options.sellerSkus?.join(',') || ''
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/fba/inventory/v1/summaries?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Uso
const inventory = await getInventorySummaries(accessToken, {
  granularityId: 'ATVPDKIKX0DER',
  sellerSkus: ['MYSKU-001', 'MYSKU-002']
});

Estrutura da Resposta de Estoque

{
  "payload": {
    "inventorySummaries": [
      {
        "asin": "B08N5WRWNW",
        "seller_sku": "MYSKU-001",
        "condition": "NewItem",
        "details": {
          "quantity": 150,
          "fulfillable_quantity": 145,
          "inbound_working_quantity": 0,
          "inbound_shipped_quantity": 5,
          "inbound_receiving_quantity": 0,
          "reserved_quantity": 5,
          "unfulfillable_quantity": 0,
          "warehouse_damage_quantity": 0,
          "distributor_damaged_quantity": 0,
          "carrier_damaged_quantity": 0,
          "defective_quantity": 0,
          "customer_damaged_quantity": 0
        },
        "marketplace_id": "ATVPDKIKX0DER"
      }
    ]
  }
}

Atualizando o Estoque

Nota: A SP-API não fornece endpoints diretos de atualização de estoque. O estoque é gerenciado através de:

  1. Remessas FBA - Envie estoque para os depósitos da Amazon
  2. Pedidos MFN - O estoque diminui automaticamente quando os pedidos são enviados
  3. Atualizações de listagem - Ajuste a quantidade através da API de Listagens

Para FBA, crie planos de remessa:

const createInboundShipmentPlan = async (accessToken, shipmentData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/fba/inbound/v0/plans';

  const payload = {
    ShipFromAddress: {
      Name: shipmentData.shipFromName,
      AddressLine1: shipmentData.shipFromAddress,
      City: shipmentData.shipFromCity,
      StateOrProvinceCode: shipmentData.shipFromState,
      CountryCode: shipmentData.shipFromCountry,
      PostalCode: shipmentData.shipFromPostalCode
    },
    LabelPrepPreference: 'SELLER_LABEL',
    InboundPlanItems: shipmentData.items.map(item => ({
      SellerSKU: item.sku,
      ASIN: item.asin,
      Quantity: item.quantity,
      Condition: 'NewItem'
    }))
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

API de Listagens

Obtendo Listagens

Busque listagens de produtos com filtragem:

const getListings = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    marketplaceIds: options.marketplaceIds?.join(',') || ['ATVPDKIKX0DER'],
    itemTypes: options.itemTypes?.join(',') || ['ASIN', 'SKU'],
    identifiers: options.identifiers?.join(',') || '',
    issuesLocale: options.locale || 'en_US'
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Uso
const listings = await getListings(accessToken, {
  identifiers: ['B08N5WRWNW', 'B09JQKJXYZ'],
  itemTypes: ['ASIN']
});

Estrutura da Resposta de Listagem

{
  "identifiers": {
    "marketplaceId": "ATVPDKIKX0DER",
    "sku": "MYSKU-001",
    "asin": "B08N5WRWNW"
  },
  "attributes": {
    "title": "Wireless Bluetooth Headphones",
    "description": "Premium wireless headphones with noise cancellation",
    "brand": "MyBrand",
    "color": "Black",
    "size": "One Size",
    "item_weight": "0.5 pounds",
    "product_dimensions": "7 x 6 x 3 inches"
  },
  "product_type": "LUGGAGE",
  "sales_price": {
    "currency_code": "USD",
    "amount": "89.99"
  },
  "list_price": {
    "currency_code": "USD",
    "amount": "129.99"
  },
  "fulfillment_availability": [
    {
      "fulfillment_channel_code": "AFN",
      "quantity": 150
    }
  ],
  "condition_type": "New",
  "status": "ACTIVE",
  "procurement": null
}

Criando ou Atualizando Listagens

Use submitListingsSubmission para operações em lote:

const submitListingUpdate = async (accessToken, listingData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items/MYSKU-001';

  const payload = {
    productType: 'LUGGAGE',
    patches: [
      {
        op: 'replace',
        path: '/attributes/title',
        value: 'Updated Wireless Bluetooth Headphones - Premium Sound'
      },
      {
        op: 'replace',
        path: '/salesPrice',
        value: {
          currencyCode: 'USD',
          amount: '79.99'
        }
      }
    ]
  };

  return makeSpApiRequest('PATCH', endpoint, accessToken, payload);
};

Excluindo uma Listagem

Remova ou desative uma listagem:

const deleteListing = async (accessToken, sku, marketplaceIds) => {
  const params = new URLSearchParams({
    marketplaceIds: marketplaceIds.join(',')
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items/${sku}?${params.toString()}`;

  return makeSpApiRequest('DELETE', endpoint, accessToken);
};

API de Relatórios

Criando Agendamentos de Relatórios

Automatize a geração de relatórios:

const createReport = async (accessToken, reportType, dateRange) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/reports/2021-06-30/reports';

  const payload = {
    reportType: reportType,
    marketplaceIds: dateRange.marketplaceIds || ['ATVPDKIKX0DER'],
    dataStartTime: dateRange.startTime?.toISOString(),
    dataEndTime: dateRange.endTime?.toISOString()
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Tipos de relatório comuns
const REPORT_TYPES = {
  ORDERS: 'GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL',
  ORDER_ITEMS: 'GET_FLAT_FILE_ORDER_ITEMS_DATA_BY_LAST_UPDATE_GENERAL',
  INVENTORY: 'GET_MERCHANT_LISTINGS_ALL_DATA',
  FBA_INVENTORY: 'GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA',
  SETTLEMENT: 'GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE',
  SALES_AND_TRAFFIC: 'GET_SALES_AND_TRAFFIC_REPORT',
  ADVERTISING: 'GET_BRAND_ANALYTICS_SEARCH_TERMS_REPORT'
};

// Uso
const report = await createReport(accessToken, REPORT_TYPES.ORDERS, {
  marketplaceIds: ['ATVPDKIKX0DER'],
  startTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Últimos 7 dias
  endTime: new Date()
});

Obtendo Documento de Relatório

Baixe o relatório gerado:

const getReportDocument = async (accessToken, reportId) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/reports/2021-06-30/reports/${reportId}/document`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Baixar e analisar relatório
const downloadReport = async (accessToken, reportId) => {
  const documentInfo = await getReportDocument(accessToken, reportId);

  const response = await fetch(documentInfo.payload.url);
  const content = await response.text();

  // Os relatórios são tipicamente delimitados por tabulações ou JSON
  if (documentInfo.payload.compressionAlgorithm === 'GZIP') {
    const decompressed = await decompressGzip(content);
    return decompressed;
  }

  return content;
};

API de Notificações

Criando Assinaturas

Configure webhooks para eventos em tempo real:

const createSubscription = async (accessToken, subscriptionData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/notifications/v1/subscriptions';

  const payload = {
    payload: {
      destination: {
        resource: subscriptionData.destinationArn, // ARN do tópico SNS
        name: subscriptionData.name
      },
      modelVersion: '1.0',
      eventFilter: {
        eventCode: subscriptionData.eventCode,
        marketplaceIds: subscriptionData.marketplaceIds
      }
    }
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Tipos de evento disponíveis
const EVENT_CODES = {
  ORDER_STATUS_CHANGE: 'OrderStatusChange',
  ORDER_ITEM_CHANGE: 'OrderItemChange',
  ORDER_CHANGE: 'OrderChange',
  FBA_ORDER_STATUS_CHANGE: 'FBAOrderStatusChange',
  FBA_OUTBOUND_SHIPMENT_STATUS: 'FBAOutboundShipmentStatus',
  INVENTORY_LEVELS: 'InventoryLevels',
  PRICING_HEALTH: 'PricingHealth'
};

// Uso
await createSubscription(accessToken, {
  destinationArn: 'arn:aws:sns:us-east-1:123456789012:sp-api-notifications',
  name: 'OrderStatusNotifications',
  eventCode: EVENT_CODES.ORDER_STATUS_CHANGE,
  marketplaceIds: ['ATVPDKIKX0DER']
});

Configurando Destino SNS

A Amazon envia notificações para tópicos SNS:

const createSnsDestination = async (accessToken, destinationData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/notifications/v1/destinations';

  const payload = {
    resource: destinationData.snsTopicArn,
    name: destinationData.name
  };

  return makeSpApiRequest('POST', endpoint, accessToken, { payload });
};

// A política do tópico SNS deve permitir que o Amazon SES publique
const snsTopicPolicy = {
  Version: '2012-10-17',
  Statement: [
    {
      Effect: 'Allow',
      Principal: {
        Service: 'notifications.amazon.com'
      },
      Action: 'SNS:Publish',
      Resource: 'arn:aws:sns:us-east-1:123456789012:sp-api-notifications'
    }
  ]
};

Processando Notificações

Configure um endpoint SNS para receber notificações:

const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/amazon', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-amz-sns-message-signature'];
  const payload = req.body;

  // Verificar assinatura da mensagem SNS
  const isValid = await verifySnsSignature(payload, signature);

  if (!isValid) {
    console.error('Invalid SNS signature');
    return res.status(401).send('Unauthorized');
  }

  const message = JSON.parse(payload.toString());

  // Lidar com diferentes tipos de mensagem
  switch (message.Type) {
    case 'SubscriptionConfirmation':
      // Auto-confirmar assinatura
      await fetch(message.SubscribeURL);
      break;
    case 'Notification':
      const notification = JSON.parse(message.Message);
      await handleSpApiNotification(notification);
      break;
  }

  res.status(200).send('OK');
});

async function handleSpApiNotification(notification) {
  const { notificationType, payload } = notification;

  switch (notificationType) {
    case 'OrderStatusChange':
      await syncOrderStatus(payload.amazonOrderId);
      break;
    case 'OrderChange':
      await syncOrderDetails(payload.amazonOrderId);
      break;
    case 'InventoryLevels':
      await updateInventoryCache(payload);
      break;
  }
}

Limites de Taxa e Cotas

Entendendo os Limites de Taxa

A SP-API impõe limites de taxa dinâmicos por endpoint:

Categoria de Endpoint Limite de Taxa Limite de Pico
Pedidos 10 requisições/segundo 20
Itens do Pedido 5 requisições/segundo 10
Estoque 2 requisições/segundo 5
Listagens 10 requisições/segundo 20
Relatórios 0,5 requisições/segundo 1
Notificações 1 requisição/segundo 2
FBA Entrada 2 requisições/segundo 5

Verifique o cabeçalho x-amzn-RateLimit-Limit nas respostas para os limites atuais.

Implementando o Tratamento de Limite de Taxa

Use backoff exponencial para retentativas:

const makeRateLimitedRequest = async (method, endpoint, accessToken, body = null, maxRetries = 5) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await makeSpApiRequest(method, endpoint, accessToken, body);

      // Verificar cabeçalhos de limite de taxa
      const rateLimit = response.headers.get('x-amzn-RateLimit-Limit');
      const retryAfter = response.headers.get('Retry-After');

      if (retryAfter) {
        console.warn(`Rate limited. Retry after: ${retryAfter} seconds`);
      }

      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        // Extrair cabeçalho retry-after ou usar backoff exponencial
        const retryAfter = error.headers?.get('Retry-After') || Math.pow(2, attempt);
        console.log(`Rate limited. Retrying in ${retryAfter}s...`);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      } else if (error.message.includes('503') && attempt < maxRetries) {
        // Serviço indisponível - backoff exponencial
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Service unavailable. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};

Implementação de Fila de Requisições

Implemente uma fila para se manter dentro dos limites:

class RateLimitedQueue {
  constructor(rateLimit, burstLimit = null) {
    this.rateLimit = rateLimit; // requisições por segundo
    this.burstLimit = burstLimit || rateLimit * 2;
    this.tokens = this.burstLimit;
    this.lastRefill = Date.now();
    this.queue = [];
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  refillTokens() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    const tokensToAdd = elapsed * this.rateLimit;

    this.tokens = Math.min(this.burstLimit, this.tokens + tokensToAdd);
    this.lastRefill = now;
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      this.refillTokens();

      if (this.tokens < 1) {
        const waitTime = (1 / this.rateLimit) * 1000;
        await new Promise(r => setTimeout(r, waitTime));
        continue;
      }

      this.tokens--;
      const { requestFn, resolve, reject } = this.queue.shift();

      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }
    }

    this.processing = false;
  }
}

// Uso - Fila da API de Pedidos (10 req/s)
const ordersQueue = new RateLimitedQueue(10, 20);
const orders = await ordersQueue.add(() => getOrders(accessToken, options));

Melhores Práticas de Segurança

Gerenciamento de Credenciais

Nunca codifique credenciais diretamente no seu código-fonte. Use variáveis de ambiente ou um gerenciador de segredos:

// Ruim - nunca faça isso
const AWS_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE';
const AWS_SECRET = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';

// Bom - use variáveis de ambiente
const AWS_ACCESS_KEY = process.env

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs