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.
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:
- Recuperação de pedidos e atualizações de status
- Gerenciamento de estoque em todos os marketplaces
- Criação, atualização e exclusão de listagens
- Gerenciamento de remessas FBA (Fulfillment by Amazon)
- Precificação de produtos e análise competitiva
- Geração de relatórios e análises
- Gerenciamento de Conteúdo A+
- Análise de marca e dados de publicidade
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:
- Assinatura AWS SigV4
- Token de acesso do fluxo OAuth
- Permissões adequadas de função IAM
- 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:
- Visite o Amazon Developer Central
- Faça login com sua conta Amazon (deve ter acesso ao Seller Central)
- Navegue até Selling Partner API no painel
- Aceite o Contrato de Desenvolvedor
Passo 2: Registre Seu Aplicativo
Crie um perfil de aplicativo no Seller Central:
- Faça login no Seller Central
- Navegue até Aplicativos e Serviços > Desenvolver Aplicativos
- Clique em Adicionar Novo Aplicativo
- Preencha os detalhes do aplicativo:
- Nome do Aplicativo: Nome claro e descritivo
- Tipo de Aplicativo: Selecione “Desenvolvido por você” ou “Terceiro”
- Caso de Uso: Descreva o propósito da sua integração
- URI de Redirecionamento: URL HTTPS para callback OAuth
Após a submissão, você receberá:
- ID do Aplicativo: Seu identificador público de aplicativo
- ID do Cliente: Usado na URL de autorização OAuth
- Segredo do Cliente: Seu segredo de API privado (nunca o exponha)
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:
- Faça login no Console AWS IAM
- Navegue até Funções > Criar Função
- Selecione Outra conta AWS como entidade confiável
- Insira o ID da conta da Amazon para sua região:
- América do Norte:
906394416454 - Europa:
336853085554 - Extremo Oriente:
774466381866
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:
- Retorne ao Seller Central > Desenvolver Aplicativos
- Selecione seu aplicativo
- Clique em Editar > ARN da Função IAM
- Insira o ARN da sua função IAM
- 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:
- Remessas FBA - Envie estoque para os depósitos da Amazon
- Pedidos MFN - O estoque diminui automaticamente quando os pedidos são enviados
- 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