TL;DR (要点)
Amazon Selling Partner API (SP-API) は、注文、在庫、出品、フルフィルメントに関する販売者データにプログラムでアクセスできる REST ベースの API です。OAuth 2.0 認証と IAM ロールを使用し、AWS SigV4 署名が必要で、エンドポイントによって異なるレート制限(1 秒あたり 0.1 から 100 リクエスト)が適用されます。このガイドでは、アカウント設定、認証、コアエンドポイント、ウェブフックサブスクリプション、および本番環境へのデプロイ戦略について説明します。
はじめに
Amazon は、世界中の 200 以上のマーケットプレイスで 3 億 5,000 万以上の商品を扱っています。Eコマースツール、在庫管理システム、分析プラットフォームを構築する開発者にとって、Amazon SP-API との連携は必須です。
現実として、Amazon の運用を管理する販売者は、注文、在庫、出品のデータの手動入力に毎週 20〜30 時間を費やしています。堅固な SP-API 連携により、複数のマーケットプレイスにわたる注文の同期、在庫の更新、出品の管理が自動化されます。
このガイドでは、Amazon SP-API 連携の全プロセスを説明します。IAM ロールの設定、OAuth 2.0 認証、AWS SigV4 リクエスト署名、注文と在庫の管理、通知サブスクリプション、エラーのトラブルシューティングについて学びます。最終的には、本番環境対応の Amazon 連携を構築できるようになります。
Amazon SP-API とは?
Amazon Selling Partner API (SP-API) は、セラーセントラルデータへのプログラムアクセスを提供する REST ベースの API です。セキュリティ、パフォーマンス、機能が向上し、従来の Marketplace Web Service (MWS) に代わるものです。
主な機能
SP-API が扱うもの:
- 注文の取得とステータスの更新
- 複数のマーケットプレイスにわたる在庫管理
- 出品の作成、更新、削除
- FBA (フルフィルメント by Amazon) の出荷管理
- 商品価格設定と競合分析
- レポートと分析の生成
- A+ コンテンツ管理
- ブランド分析と広告データ
SP-API と MWS の比較
| 機能 | SP-API | MWS (レガシー) |
|---|---|---|
| アーキテクチャ | RESTful JSON | XML ベース |
| 認証 | OAuth 2.0 + IAM | MWS 認証トークン |
| セキュリティ | AWS SigV4 署名 | シンプルなトークン |
| レート制限 | エンドポイントごとに動的 | 固定クォータ |
| マーケットプレイス | 統合されたエンドポイント | 地域固有 |
| ステータス | 現行 | 非推奨 (2025年12月) |
MWS 連携はすぐに SP-API に移行してください。Amazon は 2025 年 12 月に MWS の完全廃止を発表しました。
API アーキテクチャの概要
Amazon は、一元化された認証を持つ地域別 API 構造を使用しています。
https://sellingpartnerapi-na.amazon.com (北米)
https://sellingpartnerapi-eu.amazon.com (ヨーロッパ)
https://sellingpartnerapi-fe.amazon.com (極東)
すべてのリクエストには以下が必要です:
- AWS SigV4 署名
- OAuth フローからのアクセストークン
- 適切な IAM ロール権限
- 追跡用のリクエスト ID
サポートされているマーケットプレイス
| 地域 | マーケットプレイス | API エンドポイント |
|---|---|---|
| 北米 | US, CA, MX | sellingpartnerapi-na.amazon.com |
| ヨーロッパ | UK, DE, FR, IT, ES, NL, SE, PL, TR, EG, IN, AE, SA | sellingpartnerapi-eu.amazon.com |
| 極東 | JP, AU, SG, BR | sellingpartnerapi-fe.amazon.com |
はじめに: アカウントと IAM の設定
ステップ 1: Amazon 開発者アカウントを作成する
SP-API にアクセスするには、適切なアカウントアクセスが必要です。
- Amazon 開発者セントラルにアクセスします
- Amazon アカウントでサインインします(セラーセントラルへのアクセスが必要です)
- ダッシュボードでSelling Partner APIに移動します
- 開発者契約に同意します
ステップ 2: アプリケーションを登録する
セラーセントラルでアプリケーションプロファイルを作成します。
- セラーセントラルにログインします
- アプリとサービス > アプリを開発に移動します
- 新しいアプリを追加をクリックします
- アプリケーションの詳細を入力します:
- アプリケーション名: 明確で説明的な名前
- アプリケーションタイプ: 「自社開発」または「サードパーティ」を選択します
- ユースケース: 連携の目的を説明します
- リダイレクト URI: OAuth コールバック用の HTTPS URL
提出後、以下を受け取ります:
- アプリケーション ID: あなたの公開アプリケーション識別子
- クライアント ID: OAuth 認証 URL で使用されます
- クライアントシークレット: あなたのプライベート API シークレット(絶対に公開しないでください)
セキュリティ上の注意: 認証情報は環境変数に保存し、コードには決して含めないでください:
# .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"
ステップ 3: SP-API 用の IAM ロールを作成する
SP-API には、特定の権限を持つ IAM ロールが必要です。
- AWS IAM コンソールにログインします
- ロール > ロールを作成に移動します
- 信頼されたエンティティとして別の AWS アカウントを選択します
- お住まいの地域の Amazon のアカウント ID を入力します:
- 北米:
906394416454 - ヨーロッパ:
336853085554 - 極東:
774466381866
ステップ 4: IAM ポリシーを設定する
このポリシーを IAM ロールにアタッチします:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:*:*:*/prod/*/sellingpartnerapi/*"
]
}
]
}
ロールに SellingPartnerApiRole のような説明的な名前を付け、ARN をメモしておいてください。
ステップ 5: IAM ロールをアプリケーションにリンクする
IAM ロールを SP-API アプリケーションに接続します。
- セラーセントラル > アプリを開発に戻ります
- アプリケーションを選択します
- 編集 > IAM ロール ARNをクリックします
- IAM ロール ARN を入力します
- 変更を保存します
Amazon は数分以内に IAM ロールを検証します。「リンク済み」ステータスが表示されたら準備完了です。
OAuth 2.0 認証フロー
SP-API OAuth の理解
Amazon は OAuth 2.0 を認証に使用しています。完全なフローは次のとおりです:
1. 販売者がアプリケーションで「承認」をクリックする
2. あなたのアプリが Amazon 認証 URL にリダイレクトする
3. 販売者がログインし、権限を付与する
4. Amazon が認証コードとともにリダイレクトバックする
5. あなたのアプリがコードを LWA (Login with Amazon) トークンと交換する
6. あなたのアプリが LWA トークンを SP-API アクセストークンと交換する
7. あなたのアプリがアクセストークンを使用して API 呼び出しを行う (SigV4 署名済み)
8. アクセストークンの有効期限が切れたらリフレッシュトークンを使用する (1 時間)
ステップ 6: 認証 URL を生成する
OAuth 認証 URL を作成します。
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, // CSRF 保護のためのランダムな文字列
scope: 'sellingpartnerapi::notifications'
});
return `${baseUrl}?${params.toString()}`;
};
// 使用方法
const authUrl = generateAuthUrl(
process.env.AMAZON_CLIENT_ID,
'https://your-app.com/callback',
crypto.randomBytes(16).toString('hex')
);
console.log(`Redirect user to: ${authUrl}`);
必要な OAuth スコープ
アプリケーションに必要な権限のみをリクエストしてください。
| スコープ | 説明 | ユースケース |
|---|---|---|
sellingpartnerapi::notifications |
通知を受信する | Webhook サブスクリプション |
sellingpartnerapi::migration |
MWS から移行する | レガシー連携 |
ほとんどの API アクセスは OAuth スコープではなく、IAM ポリシーによって制御されます。
ステップ 7: コードを LWA トークンと交換する
OAuth コールバックを処理し、認証コードを交換します。
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, // 通常は 3600 秒 (1 時間)
token_type: data.token_type
};
};
// コールバックルートを処理する
app.get('/callback', async (req, res) => {
const { spapi_oauth_code, state } = req.query;
// 送信した状態と一致するか検証する (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');
// 販売者に関連付けられたトークンをデータベースに保存する
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');
}
});
ステップ 8: LWA トークンを SP-API 認証情報と交換する
LWA アクセストークンを使用して一時的な AWS 認証情報を取得します。
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();
// STS を介して AWS 認証情報と交換する
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;
};
ステップ 9: トークンのリフレッシュを実装する
アクセストークンは 1 時間で期限が切れます。自動リフレッシュを実装します。
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, // 常に新しいリフレッシュトークンを保存する
expires_in: data.expires_in
};
};
// 有効なトークンを保証するミドルウェア
const ensureValidToken = async (sellerId) => {
const seller = await db.sellers.findById(sellerId);
// トークンが 5 分以内に期限切れになるか確認する
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;
};
AWS SigV4 リクエスト署名
SigV4 の理解
すべての SP-API リクエストには、AWS Signature Version 4 (SigV4) 署名が必要です。これにより、リクエストの認証と整合性が保証されます。
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();
// ステップ 1: 標準化されたリクエストを作成する
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');
// ステップ 2: 署名対象文字列を作成する
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');
// ステップ 3: 署名を計算する
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');
// ステップ 4: 認証ヘッダーを追加する
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);
}
}
// 使用方法
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
});
SigV4 に AWS SDK を使用する
AWS SDK を使用して署名を簡素化します。
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();
};
Orders API
注文の取得
フィルタリングオプションを使用して注文を取得します。
const getOrders = async (accessToken, options = {}) => {
const params = new URLSearchParams({
createdAfter: options.createdAfter, // ISO 8601 形式
createdBefore: options.createdBefore,
orderStatuses: options.orderStatuses?.join(',') || '',
marketplaceIds: options.marketplaceIds?.join(',') || ['ATVPDKIKX0DER'], // 米国
maxResultsPerPage: options.maxResultsPerPage || 100
});
// 空のパラメータを削除する
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);
};
// 使用例
const orders = await getOrders(accessToken, {
createdAfter: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // 過去 24 時間
orderStatuses: ['Unshipped', 'PartiallyShipped'],
marketplaceIds: ['ATVPDKIKX0DER'] // 米国マーケットプレイス
});
注文応答の構造
{
"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 = 販売者
"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=="
}
}
注文品目の取得
注文の詳細な品目を取得します。
const getOrderItems = async (accessToken, orderId) => {
const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders/${orderId}/orderItems`;
return makeSpApiRequest('GET', endpoint, accessToken);
};
// 使用方法
const orderItems = await getOrderItems(accessToken, '112-1234567-1234567');
// 期待される応答
{
"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."
}
}
]
}
}
出荷ステータスの更新
追跡情報とともに注文を出荷済みとしてマークします。
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, // 例: '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);
};
// 使用方法
await confirmShipment(accessToken, '112-1234567-1234567', {
carrierCode: 'USPS',
trackingNumber: '9400111899223456789012',
items: [
{ orderItemId: '12345678901234', quantity: 2 }
]
});
一般的な運送業者コード
| 運送業者 | 運送業者コード |
|---|---|
| USPS | USPS |
| FedEx | FEDEX |
| UPS | UPS |
| DHL | DHL |
| カナダポスト | CANADA_POST |
| ロイヤルメール | ROYAL_MAIL |
| オーストラリアポスト | AUSTRALIA_POST |
| Amazon ロジスティクス | AMZN_UK |
Inventory API
在庫サマリーの取得
複数のマーケットプレイスで在庫レベルを取得します。
const getInventorySummaries = async (accessToken, options = {}) => {
const params = new URLSearchParams({
granularityType: options.granularityType || 'Marketplace',
granularityId: options.granularityId || 'ATVPDKIKX0DER', // 米国
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);
};
// 使用方法
const inventory = await getInventorySummaries(accessToken, {
granularityId: 'ATVPDKIKX0DER',
sellerSkus: ['MYSKU-001', 'MYSKU-002']
});
在庫応答の構造
{
"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"
}
]
}
}
在庫の更新
注: SP-API には、直接的な在庫更新エンドポイントは提供されていません。在庫は以下の方法で管理されます。
- FBA 出荷 - Amazon 倉庫に在庫を送る
- MFN 注文 - 注文が出荷されると在庫が自動的に減少する
- 出品の更新 - Listings API を介して数量を調整する
FBA の場合、出荷計画を作成します。
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);
};
Listings API
出品の取得
フィルタリングを使用して商品出品を取得します。
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);
};
// 使用方法
const listings = await getListings(accessToken, {
identifiers: ['B08N5WRWNW', 'B09JQKJXYZ'],
itemTypes: ['ASIN']
});
出品応答の構造
{
"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
}
出品の作成または更新
バッチ操作には `submitListingsSubmission` を使用します。
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);
};
出品の削除
出品を削除または非アクティブ化します。
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);
};
Reports API
レポートスケジュールの作成
レポートの生成を自動化します。
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);
};
// 一般的なレポートタイプ
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'
};
// 使用方法
const report = await createReport(accessToken, REPORT_TYPES.ORDERS, {
marketplaceIds: ['ATVPDKIKX0DER'],
startTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // 過去 7 日間
endTime: new Date()
});
レポートドキュメントの取得
生成されたレポートをダウンロードします。
const getReportDocument = async (accessToken, reportId) => {
const endpoint = `https://sellingpartnerapi-na.amazon.com/reports/2021-06-30/reports/${reportId}/document`;
return makeSpApiRequest('GET', endpoint, accessToken);
};
// レポートのダウンロードと解析
const downloadReport = async (accessToken, reportId) => {
const documentInfo = await getReportDocument(accessToken, reportId);
const response = await fetch(documentInfo.payload.url);
const content = await response.text();
// レポートは通常、タブ区切りまたは JSON です
if (documentInfo.payload.compressionAlgorithm === 'GZIP') {
const decompressed = await decompressGzip(content);
return decompressed;
}
return content;
};
Notifications API
サブスクリプションの作成
リアルタイムイベント用のウェブフックを設定します。
const createSubscription = async (accessToken, subscriptionData) => {
const endpoint = 'https://sellingpartnerapi-na.amazon.com/notifications/v1/subscriptions';
const payload = {
payload: {
destination: {
resource: subscriptionData.destinationArn, // SNS トピック ARN
name: subscriptionData.name
},
modelVersion: '1.0',
eventFilter: {
eventCode: subscriptionData.eventCode,
marketplaceIds: subscriptionData.marketplaceIds
}
}
};
return makeSpApiRequest('POST', endpoint, accessToken, payload);
};
// 利用可能なイベントタイプ
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'
};
// 使用方法
await createSubscription(accessToken, {
destinationArn: 'arn:aws:sns:us-east-1:123456789012:sp-api-notifications',
name: 'OrderStatusNotifications',
eventCode: EVENT_CODES.ORDER_STATUS_CHANGE,
marketplaceIds: ['ATVPDKIKX0DER']
});
SNS 送信先の設定
Amazon は 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 });
};
// SNS トピックポリシーは Amazon SES が発行することを許可する必要があります
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'
}
]
};
通知の処理
通知を受信するための SNS エンドポイントを設定します。
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;
// 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());
// さまざまなメッセージタイプを処理する
switch (message.Type) {
case 'SubscriptionConfirmation':
// サブスクリプションを自動確認する
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;
}
}
レート制限とクォータ
レート制限の理解
SP-API はエンドポイントごとに動的なレート制限を適用します。
| エンドポイントカテゴリ | レート制限 | バースト制限 |
|---|---|---|
| 注文 | 10 リクエスト/秒 | 20 |
| 注文品目 | 5 リクエスト/秒 | 10 |
| 在庫 | 2 リクエスト/秒 | 5 |
| 出品 | 10 リクエスト/秒 | 20 |
| レポート | 0.5 リクエスト/秒 | 1 |
| 通知 | 1 リクエスト/秒 | 2 |
| FBA 入庫 | 2 リクエスト/秒 | 5 |
現在の制限については、応答の x-amzn-RateLimit-Limit ヘッダーを確認してください。
レート制限処理の実装
再試行に指数バックオフを使用します。
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);
// レート制限ヘッダーを確認する
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) {
// Retry-After ヘッダーを抽出するか、指数バックオフを使用する
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) {
// サービスが利用不可 - 指数バックオフ
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;
}
}
}
};
リクエストキューイングの実装
制限内で動作するためにキューを実装します。
class RateLimitedQueue {
constructor(rateLimit, burstLimit = null) {
this.rateLimit = rateLimit; // 1 秒あたりのリクエスト数
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;
}
}
// 使用方法 - Orders API キュー (10 リクエスト/秒)
const ordersQueue = new RateLimitedQueue(10, 20);
const orders = await ordersQueue.add(() => getOrders(accessToken, options));
セキュリティのベストプラクティス
認証情報の管理
ソースコードに認証情報をハードコードしないでください。環境変数またはシークレットマネージャーを使用してください。
// 悪い例 - これをしてはいけません
const AWS_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE';
const AWS_SECRET = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';
// 良い例 - 環境変数を使用する
const AWS_ACCESS_KEY = process.env.AWS_ACCESS_KEY_ID;
const AWS_SECRET = process.env.AWS_SECRET_ACCESS_KEY;
// より良い例 - AWS Secrets Manager などを使用する
const { SecretsManagerClient } = require('@aws-sdk/client-secrets-manager');
const secretsClient = new SecretsManagerClient({ region: 'us-east-1' });
const getCredentials = async () => {
const response = await secretsClient.send({
Name: 'prod/sp-api/credentials'
});
return JSON.parse(response.SecretString);
};
トークンストレージの要件
SP-API では、トークンストレージに特定のセキュリティ対策が必要です。
- 保存時の暗号化: 保存されたトークンには AES-256 暗号化を使用します
- 転送時の暗号化: 常に HTTPS/TLS 1.2+ を使用します
- アクセス制御: 特定のサービスアカウントへのトークンアクセスを制限します
- 監査ロギング: すべてのトークンアクセスおよびリフレッシュイベントをログに記録します
- 自動ローテーション: 有効期限が切れる前にトークンをリフレッシュします
const crypto = require('crypto');
class TokenStore {
constructor(encryptionKey) {
this.algorithm = 'aes-256-gcm';
this.key = crypto.createHash('sha256').update(encryptionKey).digest('hex').slice(0, 32);
}
encrypt(token) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, Buffer.from(this.key), iv);
let encrypted = cipher.update(token, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag().toString('hex');
return {
iv: iv.toString('hex'),
encryptedData: encrypted,
authTag
};
}
decrypt(encryptedData) {
const decipher = crypto.createDecipheriv(
this.algorithm,
Buffer.from(this.key),
Buffer.from(encryptedData.iv, 'hex')
);
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
let decrypted = decipher.update(encryptedData.encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
IAM 最小権限
アプリケーションに必要な権限のみを付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SPAPIOrdersAccess",
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:*:*:*/prod/*/sellingpartnerapi/orders/*"
},
{
"Sid": "SPAPIInventoryAccess",
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:*:*:*/prod/*/sellingpartnerapi/fba/inventory/*"
}
]
}
本番環境で * ワイルドカードを使用することは避けてください。権限を特定のエンドポイントに限定してください。
リクエスト署名のセキュリティ
SigV4 の実装を常に検証してください。
- すべてのリクエストに HTTPS を使用する
- 必要なすべてのヘッダーを署名に含める
- タイムスタンプが AWS サーバーの 5 分以内であることを確認する
- AWS 認証情報を定期的にローテーションする
- 可能な場合は、長期的な認証情報ではなく IAM ロールを使用する
// リクエストのタイムスタンプを検証する
const validateTimestamp = (amzDate) => {
const now = new Date();
const requestTime = new Date(amzDate);
const diff = Math.abs(now - requestTime);
// AWS は 5 分以上古いリクエストを拒否します
if (diff > 5 * 60 * 1000) {
throw new Error('Request timestamp too old. Sync your server clock.');
}
};
Apidog を使用した SP-API 連携のテスト
SP-API 連携をテストする理由
Amazon SP-API 連携には、複雑な認証フロー、複数のエンドポイント、厳格なレート制限が伴います。テストは以下の目的で役立ちます:
- OAuth 2.0 認証フローを検証する
- SigV4 署名の問題をデバッグする
- レート制限のエラー処理をテストする
- 開発用にレスポンスをモックする
- チームの API ワークフローを文書化する
SP-API テスト用の Apidog の設定
ステップ 1: SP-API OpenAPI 仕様をインポートする
Apidog は OpenAPI 3.0 仕様をサポートしています。Amazon の SP-API 仕様をインポートします。
- Amazon リポジトリから SP-API OpenAPI 仕様をダウンロードします
- Apidog で新しいプロジェクトを作成します
- 仕様ファイルをインポートします
- 認証情報のために環境変数を設定します
ステップ 2: 環境変数を設定する
環境固有の変数を設定します。
ベース URL (サンドボックス): https://sandbox.sellingpartnerapi-na.amazon.com
ベース URL (本番環境): https://sellingpartnerapi-na.amazon.com
LWA アクセストークン: {{lwa_access_token}}
AWS アクセスキー: {{aws_access_key}}
AWS シークレットキー: {{aws_secret_key}}
リージョン: us-east-1
ステップ 3: 事前リクエストスクリプトを作成する
Apidog の事前リクエストスクリプトを使用して SigV4 署名を自動化します。
// SigV4 署名用の Apidog 事前リクエストスクリプト
const crypto = require('crypto');
const accessKey = apidog.variables.get('aws_access_key');
const secretKey = apidog.variables.get('aws_secret_key');
const accessToken = apidog.variables.get('lwa_access_token');
const region = apidog.variables.get('region');
const method = apidog.request.method;
const url = new URL(apidog.request.url);
const body = apidog.request.body;
// SigV4 署名を生成する
const signer = new SigV4Signer(accessKey, secretKey, region);
const signedHeaders = signer.sign(method, url.href, body, {
'x-amz-access-token': accessToken
});
// リクエストのヘッダーを設定する
apidog.request.headers = {
...apidog.request.headers,
...signedHeaders.headers
};
ステップ 4: テストシナリオを作成する
一般的なワークフローのテストシナリオを作成します。
// テスト: 過去 24 時間の注文を取得する
// 1. OAuth コードをトークンと交換する
// 2. GetOrders エンドポイントを呼び出す
// 3. 応答構造を検証する
// 4. 注文 ID を抽出する
// 5. 各注文の GetOrderItems を呼び出す
const ordersResponse = await apidog.send({
method: 'GET',
url: '/orders/v0/orders',
params: {
createdAfter: new Date(Date.now() - 86400000).toISOString(),
marketplaceIds: 'ATVPDKIKX0DER'
}
});
apidog.assert(ordersResponse.status === 200, 'Orders request failed');
apidog.assert(ordersResponse.data.payload.orders.length > 0, 'No orders found');
// 後続のリクエストのために注文 ID を保存する
const orderIds = ordersResponse.data.payload.orders.map(o => o.amazon_order_id);
apidog.variables.set('order_ids', JSON.stringify(orderIds));
SP-API 応答のモック
Apidog のスマートモック機能を使用して、開発中に SP-API 応答をシミュレートします。
// GetOrders のモック応答
{
"payload": {
"orders": [
{
"amazon_order_id": "112-{{randomNumber}}-{{randomNumber}}",
"order_status": "Unshipped",
"purchase_date": "{{now}}",
"order_total": {
"currency_code": "USD",
"amount": "{{randomFloat 10 500}}"
}
}
],
"next_token": null
}
}
これにより、ライブの Amazon エンドポイントにアクセスすることなく、フロントエンド開発を進めることができます。
一般的な問題のデバッグ
問題: SigV4 署名不一致
Apidog のリクエストインスペクターを使用して以下を確認します:
- 必要なすべてのヘッダーが含まれていること
- ヘッダーがアルファベット順にソートされていること
- 標準化されたリクエストが AWS の期待と一致していること
- タイムスタンプが有効な範囲内であること
問題: OAuth トークンエラー
簡単なリクエストでトークンの有効性を確認します。
const tokenCheck = await apidog.send({
method: 'GET',
url: '/orders/v0/orders',
params: { createdAfter: new Date().toISOString() }
});
if (tokenCheck.status === 401) {
console.log('Token expired - refresh required');
// トークンリフレッシュフローをトリガーする
}
CI/CD でのテスト自動化
Apidog テストを CI/CD パイプラインに統合します。
# GitHub Actions の例
name: SP-API Integration Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Apidog Tests
uses: apidog/test-action@v1
with:
project-id: ${{ secrets.APIDOG_PROJECT_ID }}
api-key: ${{ secrets.APIDOG_API_KEY }}
environment: sandbox
- name: Notify on Failure
if: failure()
run: |
echo "SP-API tests failed - check Apidog dashboard"
マーケットプレイス ID リファレンス
一般的なマーケットプレイスについては、このリファレンスを参考にしてください。
| 国 | マーケットプレイス ID |
|---|---|
| 米国 | ATVPDKIKX0DER |
| カナダ | A2EUQ1WTGCTBG2 |
| メキシコ | A1AM78C64UM0Y8 |
| 英国 | A1F83G8C2ARO7P |
| ドイツ | A1PA6795UKMFR9 |
| フランス | A13V1IB3VIYZZH |
| イタリア | APJ6JRA9NG5V4 |
| スペイン | A1RKKUPIHCS9HS |
| 日本 | A1VC38T7YXB528 |
| オーストラリア | A39IBJ37TRP1C6 |
| インド | A21TJRUUN4KGV |
| ブラジル | A2Q3Y263D00KWC |
一般的な問題のトラブルシューティング
問題: 403 Unauthorized エラー
症状: 「Unauthorized」または「Access Denied」エラーが発生する。
診断:
const error = await response.json();
console.error('Auth error:', error);
// 確認: InvalidSignature, AccessDenied, ExpiredToken
解決策:
- AWS 認証情報が正しいこと、およびアクティブであることを確認する
- IAM ロール ARN がアプリケーションにリンクされていることを確認する
- OAuth トークンの有効期限が切れていないことを確認する
- SigV4 署名に正しいリージョンが含まれていることを確認する
x-amz-access-tokenヘッダーが存在することを確認する
問題: 429 Rate Limit Exceeded
症状: HTTP 429 応答が頻繁に返される。
解決策:
- レート制限付きリクエストキューイングを実装する
- 再試行に指数バックオフを使用する
- 可能な場合はリクエストをバッチ処理する (ページネーションには next_token を使用する)
x-amzn-RateLimit-Limitヘッダーを積極的に監視する- 大量の使用ケースについては、セラーセントラルを通じてより高い制限をリクエストする
問題: 404 Not Found
症状: 有効なエンドポイントが 404 を返す。
解決策:
- 正しいリージョンエンドポイントを使用していることを確認する
- マーケットプレイス ID がそのリージョンで有効であることを確認する
- リソース (注文、出品) が存在することを確認する
- パス内の API バージョンが正しいことを確認する (例:
/v0/、/2021-08-01/)
問題: 400 Bad Request
症状: リクエストが検証エラーで失敗する。
一般的な原因:
- 日付形式が不正: ISO 8601 形式 (
2026-03-19T10:30:00Z) を使用する - 必須パラメータの欠落: 必須フィールドについては API ドキュメントを確認する
- マーケットプレイス ID が不正: 正しい ID を使用する (例: 米国向け
ATVPDKIKX0DER) - 不正な JSON: リクエストボディの構文を検証する
解決策:
// 送信する前に日付を検証する
const validateIsoDate = (dateString) => {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
throw new Error('Invalid ISO 8601 date format');
}
return dateString;
};
問題: レポートが INIT_STATE のままになる
症状: レポートが DONE ステータスに達しない。
解決策:
- さらに待つ - 一部のレポートは 15~30 分かかる場合があります
- レポートタイプがあなたのアカウントで利用可能であることを確認する
- 日付範囲が大きすぎないか確認する (より小さい範囲で試す)
- レポートステータスを継続的にではなく 30 秒ごとにポーリングする
- 一部のレポートタイプには特定の権限が必要です
問題: 通知が届かない
症状: サブスクリプションが作成されたが、通知が受信されない。
診断:
- API を介してサブスクリプションステータスを確認する
- SNS トピックポリシーが Amazon SES を許可していることを確認する
- エンドポイント URL が一般にアクセス可能であることを確認する
- CloudWatch ログで配信試行を確認する
解決策:
- SNS トピックに正しいリソースポリシーがあることを確認する
- 有効な SSL 証明書を持つ HTTPS エンドポイントを検証する
- すべての通知に対して 30 秒以内に 200 OK を返す
SubscriptionConfirmationメッセージを自動確認する
本番環境デプロイチェックリスト
本番稼働する前に:
- [ ] 本番環境のセラーセントラルでアプリケーションを登録する
- [ ] 最小権限の権限で本番環境の IAM ロールを設定する
- [ ] すべてのリダイレクト URI を本番環境の URL に更新する
- [ ] 安全なトークンストレージ (暗号化されたデータベース) を実装する
- [ ] 自動トークンリフレッシュロジックを追加する
- [ ] レート制限とリクエストキューイングを設定する
- [ ] 通知用の SNS 送信先を設定する
- [ ] 包括的なエラー処理を実装する
- [ ] リクエスト ID を含むすべての API 呼び出しのログを追加する
- [ ] レート制限の使用状況を監視する設定を行う
- [ ] 一般的な問題のランブックを作成する
- [ ] 複数のマーケットプレイス ID でテストする
- [ ] 販売者オンボーディングのための OAuth フローを文書化する
- [ ] 指数バックオフによる再試行ロジックを実装する
- [ ] 認証失敗のアラートを設定する
監視とアラート
これらのメトリクスを追跡します。
const metrics = {
apiCalls: {
total: 0,
successful: 0,
failed: 0,
rateLimited: 0
},
rateLimitUsage: {
orders: { current: 0, limit: 10 },
inventory: { current: 0, limit: 2 },
listings: { current: 0, limit: 10 }
},
oauthTokens: {
active: 0,
expiring_soon: 0,
refresh_failures: 0
},
notifications: {
received: 0,
processed: 0,
failed: 0
},
reports: {
pending: 0,
completed: 0,
failed: 0
}
};
// 失敗率が高い場合にアラート
const failureRate = metrics.apiCalls.failed / metrics.apiCalls.total;
if (failureRate > 0.05) { // 失敗率が 5% を超える場合
sendAlert('SP-API failure rate above 5%');
}
// レート制限が近づいている場合にアラート
for (const [endpoint, usage] of Object.entries(metrics.rateLimitUsage)) {
if (usage.current / usage.limit > 0.8) {
sendAlert(`${endpoint} rate limit at 80% capacity`);
}
}
実世界のユースケース
複数マーケットプレイスの在庫同期
グローバルなエレクトロニクス販売者は、SP-API を使用して 12 のマーケットプレイス間で在庫を同期しています。
- 課題: 手動での在庫更新により、EU マーケットプレイスでの過剰販売が発生
- 解決策: SP-API ウェブフックとレート制限付きキューを使用した中央在庫システム
- 結果: 過剰販売の事例がゼロになり、毎週 25 時間の時間を節約
実装フロー:
InventoryLevelsイベントで SNS 通知がトリガーされる- 中央システムが新しい数量を計算する
- レート制限付き API 呼び出しが各マーケットプレイスを更新する
- 確認は監査データベースにログとして記録される
自動注文フルフィルメント
サードパーティロジスティクスプロバイダーが注文処理を自動化します。
- 課題: 毎日 200 件以上の注文があり、システム間で手動でのデータ入力が必要
- 解決策: SP-API と倉庫管理システムの連携
- 結果: 注文が発行されてから 2 分以内に倉庫へ自動ルーティング
主な連携ポイント:
OrderStatusChangeイベントをウェブフックがリッスンする- 注文の詳細が REST API を介して WMS に送信される
- 追跡番号が SP-API を介して返され、更新される
- 顧客は自動出荷通知を受け取る
アナリティクスダッシュボード
販売者アナリティクスツールは、500 以上もの販売者アカウントのデータを集約します。
- 課題: 販売者はマーケットプレイス間で統合されたレポートが不足していた
- 解決策: トークン管理を伴う OAuth ベースの複数販売者データ集約
- 結果: 売上、在庫、広告指標を含むリアルタイムダッシュボード
SP-API を介して収集されたデータ:
- すべてのマーケットプレイスにわたる注文と注文品目
- FBA 在庫レベルと入庫出荷
- 出品パフォーマンスと価格データ
- ブランド分析と検索語句レポート
結論
Amazon SP-API は、レガシー MWS に比べてセキュリティとパフォーマンスが向上し、セラーセントラルの機能への包括的なアクセスを提供します。主なポイント:
- OAuth 2.0 と IAM ロールには、慎重な認証情報管理と自動トークンリフレッシュが必要
- AWS SigV4 署名はすべてのリクエストに必須であり、SDK または実績のあるライブラリを使用する
- レート制限はエンドポイントによって異なり (0.5 から 100 リクエスト/秒)、事前の監視が必要
- SNS を介した通知により、リアルタイムの注文および在庫同期が可能
- 適切なエラー処理と再試行ロジックは、本番環境の信頼性に不可欠
- Apidog は、SP-API 連携の API テストとチームコラボレーションを効率化する
FAQ セクション
Amazon SP-API とは?
Amazon Selling Partner API (SP-API) は、注文、在庫、出品、レポートを含むセラーセントラルデータへのプログラムアクセスを提供する REST ベースの API です。OAuth 2.0 と AWS SigV4 署名によるセキュリティの向上により、古い MWS システムに代わるものです。
Amazon SP-API 認証情報はどのように取得しますか?
セラーセントラルの「アプリとサービス」>「アプリを開発」でアプリケーションを登録します。クライアント ID、クライアントシークレット、およびアプリケーション ID を受け取ります。また、AWS で IAM ロールを作成し、それをアプリケーションにリンクする必要があります。
Amazon SP-API は無料で利用できますか?
はい、SP-API へのアクセスは登録済みの Amazon 販売者にとって無料です。ただし、エンドポイントによって異なるレート制限(1 秒あたり 0.5 から 100 リクエスト)が適用されます。特定の大量使用ケースでより高い制限が必要な場合は、Amazon からの承認が必要です。
SP-API はどのような認証を使用しますか?
SP-API は、認可に OAuth 2.0 を使用し、アクセス制御に AWS IAM ロールを組み合わせています。すべての API リクエストには、OAuth フローを通じて取得した一時的な認証情報による AWS Signature Version 4 (SigV4) 署名が必要です。
SP-API レート制限はどのように処理しますか?
エンドポイントごとの制限内に収まるように、リクエストキューイングを実装します。使用状況を追跡するために x-amzn-RateLimit-Limit ヘッダーを監視します。HTTP 429 (Too Many Requests) 応答を受信した場合は、指数バックオフを使用します。エンドポイントによって異なる制限があります。
ライブ販売者アカウントなしで SP-API をテストできますか?
はい。Amazon は SP-API 開発用のサンドボックス環境を提供しています。ライブ販売者データに影響を与えることなく連携をテストするために、サンドボックスモードでアプリケーションを登録してください。ただし、サンドボックスではすべてのエンドポイントが利用できるわけではありません。
SP-API でウェブフックはどのように機能しますか?
SP-API は通知に Amazon SNS を使用します。特定のイベントタイプ(注文、在庫など)のサブスクリプションを作成し、SNS トピックを設定し、通知を受信するための HTTPS エンドポイントを実装します。サブスクリプション確認メッセージは自動的に確認されます。
OAuth トークンの有効期限が切れるとどうなりますか?
LWA アクセストークンは 1 時間で期限が切れます。有効期限が切れる前にリフレッシュトークンを使用して新しいアクセストークンを取得してください。API 呼び出し中の認証失敗を防ぐために、ミドルウェアで自動トークンリフレッシュを実装してください。
MWS から SP-API に移行するにはどうすればよいですか?
MWS の廃止は 2025 年 12 月に予定されています。移行には、認証を MWS トークンから OAuth 2.0 に更新すること、SigV4 署名を実装すること、エンドポイント URL を更新すること、およびリクエスト/応答の解析を XML から JSON に変更することが必要です。
なぜ 403 Unauthorized エラーが発生するのですか?
一般的な原因としては、OAuth トークンの有効期限切れ、不正確な IAM ロール設定、無効な SigV4 署名、x-amz-access-token ヘッダーの欠落、または IAM ロールがアプリケーションにリンクされていないことなどが挙げられます。特定のエラーコードについては、エラー応答の詳細を確認してください。
