Amazon SP API 連携方法:ステップバイステップチュートリアル

Ashley Innocent

Ashley Innocent

20 3月 2026

Amazon SP API 連携方法:ステップバイステップチュートリアル

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 連携を構築できるようになります。

💡
Apidog は API 連携のテストを簡素化します。SP-API エンドポイントのテスト、OAuth フローの検証、リクエスト署名の検査、認証の問題のデバッグを 1 つのワークスペースで行うことができます。API 仕様のインポート、モックレスポンスの作成、テストシナリオのチームとの共有も可能です。
button

Amazon SP-API とは?

Amazon Selling Partner API (SP-API) は、セラーセントラルデータへのプログラムアクセスを提供する REST ベースの API です。セキュリティ、パフォーマンス、機能が向上し、従来の Marketplace Web Service (MWS) に代わるものです。

主な機能

SP-API が扱うもの:

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 (極東)

すべてのリクエストには以下が必要です:

  1. AWS SigV4 署名
  2. OAuth フローからのアクセストークン
  3. 適切な IAM ロール権限
  4. 追跡用のリクエスト 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 にアクセスするには、適切なアカウントアクセスが必要です。

  1. Amazon 開発者セントラルにアクセスします
  2. Amazon アカウントでサインインします(セラーセントラルへのアクセスが必要です)
  3. ダッシュボードでSelling Partner APIに移動します
  4. 開発者契約に同意します

ステップ 2: アプリケーションを登録する

セラーセントラルでアプリケーションプロファイルを作成します。

  1. セラーセントラルにログインします
  2. アプリとサービス > アプリを開発に移動します
  3. 新しいアプリを追加をクリックします
  4. アプリケーションの詳細を入力します:

提出後、以下を受け取ります:

セキュリティ上の注意: 認証情報は環境変数に保存し、コードには決して含めないでください:

# .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 ロールが必要です。

  1. AWS IAM コンソールにログインします
  2. ロール > ロールを作成に移動します
  3. 信頼されたエンティティとして別の AWS アカウントを選択します
  4. お住まいの地域の Amazon のアカウント ID を入力します:

ステップ 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 アプリケーションに接続します。

  1. セラーセントラル > アプリを開発に戻ります
  2. アプリケーションを選択します
  3. 編集 > IAM ロール ARNをクリックします
  4. IAM ロール ARN を入力します
  5. 変更を保存します

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 には、直接的な在庫更新エンドポイントは提供されていません。在庫は以下の方法で管理されます。

  1. FBA 出荷 - Amazon 倉庫に在庫を送る
  2. MFN 注文 - 注文が出荷されると在庫が自動的に減少する
  3. 出品の更新 - 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 では、トークンストレージに特定のセキュリティ対策が必要です。

  1. 保存時の暗号化: 保存されたトークンには AES-256 暗号化を使用します
  2. 転送時の暗号化: 常に HTTPS/TLS 1.2+ を使用します
  3. アクセス制御: 特定のサービスアカウントへのトークンアクセスを制限します
  4. 監査ロギング: すべてのトークンアクセスおよびリフレッシュイベントをログに記録します
  5. 自動ローテーション: 有効期限が切れる前にトークンをリフレッシュします
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 の実装を常に検証してください。

  1. すべてのリクエストに HTTPS を使用する
  2. 必要なすべてのヘッダーを署名に含める
  3. タイムスタンプが AWS サーバーの 5 分以内であることを確認する
  4. AWS 認証情報を定期的にローテーションする
  5. 可能な場合は、長期的な認証情報ではなく 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 連携には、複雑な認証フロー、複数のエンドポイント、厳格なレート制限が伴います。テストは以下の目的で役立ちます:

SP-API テスト用の Apidog の設定

ステップ 1: SP-API OpenAPI 仕様をインポートする

Apidog は OpenAPI 3.0 仕様をサポートしています。Amazon の SP-API 仕様をインポートします。

  1. Amazon リポジトリから SP-API OpenAPI 仕様をダウンロードします
  2. Apidog で新しいプロジェクトを作成します
  3. 仕様ファイルをインポートします
  4. 認証情報のために環境変数を設定します

ステップ 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 のリクエストインスペクターを使用して以下を確認します:

  1. 必要なすべてのヘッダーが含まれていること
  2. ヘッダーがアルファベット順にソートされていること
  3. 標準化されたリクエストが AWS の期待と一致していること
  4. タイムスタンプが有効な範囲内であること

問題: 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

解決策:

  1. AWS 認証情報が正しいこと、およびアクティブであることを確認する
  2. IAM ロール ARN がアプリケーションにリンクされていることを確認する
  3. OAuth トークンの有効期限が切れていないことを確認する
  4. SigV4 署名に正しいリージョンが含まれていることを確認する
  5. x-amz-access-token ヘッダーが存在することを確認する

問題: 429 Rate Limit Exceeded

症状: HTTP 429 応答が頻繁に返される。

解決策:

  1. レート制限付きリクエストキューイングを実装する
  2. 再試行に指数バックオフを使用する
  3. 可能な場合はリクエストをバッチ処理する (ページネーションには next_token を使用する)
  4. x-amzn-RateLimit-Limit ヘッダーを積極的に監視する
  5. 大量の使用ケースについては、セラーセントラルを通じてより高い制限をリクエストする

問題: 404 Not Found

症状: 有効なエンドポイントが 404 を返す。

解決策:

  1. 正しいリージョンエンドポイントを使用していることを確認する
  2. マーケットプレイス ID がそのリージョンで有効であることを確認する
  3. リソース (注文、出品) が存在することを確認する
  4. パス内の API バージョンが正しいことを確認する (例: /v0//2021-08-01/)

問題: 400 Bad Request

症状: リクエストが検証エラーで失敗する。

一般的な原因:

  1. 日付形式が不正: ISO 8601 形式 (2026-03-19T10:30:00Z) を使用する
  2. 必須パラメータの欠落: 必須フィールドについては API ドキュメントを確認する
  3. マーケットプレイス ID が不正: 正しい ID を使用する (例: 米国向け ATVPDKIKX0DER)
  4. 不正な 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 ステータスに達しない。

解決策:

  1. さらに待つ - 一部のレポートは 15~30 分かかる場合があります
  2. レポートタイプがあなたのアカウントで利用可能であることを確認する
  3. 日付範囲が大きすぎないか確認する (より小さい範囲で試す)
  4. レポートステータスを継続的にではなく 30 秒ごとにポーリングする
  5. 一部のレポートタイプには特定の権限が必要です

問題: 通知が届かない

症状: サブスクリプションが作成されたが、通知が受信されない。

診断:

  1. API を介してサブスクリプションステータスを確認する
  2. SNS トピックポリシーが Amazon SES を許可していることを確認する
  3. エンドポイント URL が一般にアクセス可能であることを確認する
  4. CloudWatch ログで配信試行を確認する

解決策:

  1. SNS トピックに正しいリソースポリシーがあることを確認する
  2. 有効な SSL 証明書を持つ HTTPS エンドポイントを検証する
  3. すべての通知に対して 30 秒以内に 200 OK を返す
  4. SubscriptionConfirmation メッセージを自動確認する

本番環境デプロイチェックリスト

本番稼働する前に:

監視とアラート

これらのメトリクスを追跡します。

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 のマーケットプレイス間で在庫を同期しています。

実装フロー:

  1. InventoryLevels イベントで SNS 通知がトリガーされる
  2. 中央システムが新しい数量を計算する
  3. レート制限付き API 呼び出しが各マーケットプレイスを更新する
  4. 確認は監査データベースにログとして記録される

自動注文フルフィルメント

サードパーティロジスティクスプロバイダーが注文処理を自動化します。

主な連携ポイント:

アナリティクスダッシュボード

販売者アナリティクスツールは、500 以上もの販売者アカウントのデータを集約します。

SP-API を介して収集されたデータ:

結論

Amazon SP-API は、レガシー MWS に比べてセキュリティとパフォーマンスが向上し、セラーセントラルの機能への包括的なアクセスを提供します。主なポイント:

button

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 ロールがアプリケーションにリンクされていないことなどが挙げられます。特定のエラーコードについては、エラー応答の詳細を確認してください。

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる