この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:10:02

タイトル: リクエストにスペースを含める方法
SEOタイトル: Amazon PA-API リクエストにスペースを含める方法完全ガイド

この記事の要点
  • Amazon PA-API(Product Advertising API)で複数語キーワードを投げる時はスペースを URL エンコード
  • クエリ文字列のスペースは %20 または + (PA-API v5 は JSON ボディなので %20 推奨)
  • PHP: urlencode() / rawurlencode() / http_build_query()
  • JS: encodeURIComponent()
  • AWS V4 署名計算は RFC 3986 準拠の rawurlencode(スペース→%20)が前提
  • JSON ボディの Keywords はそのままスペースで入れて OK。署名対象はボディハッシュのみ
  • SDK(PaapiSDK / Amazon SDK for PHP)を使えば URL エンコードや署名は SDK 任せ

PA-API v5 とスペースの扱い

Amazon Product Advertising API は 2020 年以降 v5 に統一されました。v5 ではPOST + JSON ボディでリクエストを送るので、複数語キーワード(例: harry potter)はJSON 文字列の中にそのままスペースで書くのが基本です。

一方で v4 以前は GET + クエリ文字列だったため、スペースは URL エンコードが必要でした。古いコードと新コードが混在する解説が多いので、まずどちらの API かを確認してください。

v5 (現行) のリクエスト例

// SearchItems の JSON ボディ
POST /paapi5/searchitems HTTP/1.1
Host: webservices.amazon.co.jp
Content-Type: application/json; charset=utf-8
X-Amz-Date: 20260611T100000Z
Authorization: AWS4-HMAC-SHA256 Credential=AKIA...

{
    "Keywords": "harry potter",          // ← スペースそのまま OK
    "SearchIndex": "All",
    "ItemCount": 10,
    "Resources": ["Images.Primary.Small", "ItemInfo.Title"],
    "PartnerTag": "yourtag-22",
    "PartnerType": "Associates",
    "Marketplace": "www.amazon.co.jp"
}

JSON 文字列の中にあるスペースは何もする必要なし。ASCII の半角スペース (0x20) として送信されます。

署名計算(AWS V4)でのスペース

v5 の署名対象はボディ全体のハッシュ + Canonical Headersです。クエリ文字列を使わないので URL エンコードを意識する必要はほぼありません。ただし、Canonical Request 内に URI パス(/paapi5/searchitems)が含まれます。パスにスペースが入ることはまずありませんが、入れる場合は RFC 3986 準拠の rawurlencode でエンコードする必要があります。

<?php
// RFC 3986 準拠のエンコード
$encoded = rawurlencode('harry potter');
echo $encoded;   // harry%20potter

// urlencode() との違い: urlencode は + を使う(application/x-www-form-urlencoded 用)
echo urlencode('harry potter');     // harry+potter
echo rawurlencode('harry potter');  // harry%20potter

// PA-API v4 のような URL 直書きでは rawurlencode のほうが安全

PHP での実装

cURL で直接呼ぶ

<?php
function paapiSearch(string $keywords): array {
    $accessKey = getenv('PAAPI_ACCESS_KEY');
    $secretKey = getenv('PAAPI_SECRET_KEY');
    $partnerTag = 'yourtag-22';
    $host = 'webservices.amazon.co.jp';
    $region = 'us-west-2';
    $service = 'ProductAdvertisingAPI';
    $target = 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems';
    $path = '/paapi5/searchitems';

    $payload = json_encode([
        'Keywords'    => $keywords,            // ★ スペースそのまま
        'SearchIndex' => 'All',
        'ItemCount'   => 10,
        'Resources'   => ['Images.Primary.Small', 'ItemInfo.Title'],
        'PartnerTag'  => $partnerTag,
        'PartnerType' => 'Associates',
        'Marketplace' => 'www.amazon.co.jp',
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    $amzDate   = gmdate('Ymd\THis\Z');
    $dateStamp = gmdate('Ymd');

    // Canonical Request
    $canonicalHeaders =
        "content-encoding:amz-1.0\n" .
        "content-type:application/json; charset=utf-8\n" .
        "host:{$host}\n" .
        "x-amz-date:{$amzDate}\n" .
        "x-amz-target:{$target}\n";
    $signedHeaders = 'content-encoding;content-type;host;x-amz-date;x-amz-target';
    $payloadHash = hash('sha256', $payload);

    $canonicalRequest =
        "POST\n{$path}\n\n{$canonicalHeaders}\n{$signedHeaders}\n{$payloadHash}";

    // String to Sign
    $credentialScope = "{$dateStamp}/{$region}/{$service}/aws4_request";
    $stringToSign =
        "AWS4-HMAC-SHA256\n{$amzDate}\n{$credentialScope}\n" .
        hash('sha256', $canonicalRequest);

    // Signature
    $kDate    = hash_hmac('sha256', $dateStamp, "AWS4{$secretKey}", true);
    $kRegion  = hash_hmac('sha256', $region,    $kDate,  true);
    $kService = hash_hmac('sha256', $service,   $kRegion, true);
    $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
    $signature = hash_hmac('sha256', $stringToSign, $kSigning);

    $authHeader =
        "AWS4-HMAC-SHA256 Credential={$accessKey}/{$credentialScope}, " .
        "SignedHeaders={$signedHeaders}, Signature={$signature}";

    $ch = curl_init("https://{$host}{$path}");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_HTTPHEADER => [
            'Content-Encoding: amz-1.0',
            'Content-Type: application/json; charset=utf-8',
            "Host: {$host}",
            "X-Amz-Date: {$amzDate}",
            "X-Amz-Target: {$target}",
            "Authorization: {$authHeader}",
        ],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $res = curl_exec($ch);
    return json_decode($res, true);
}

// 使用
$result = paapiSearch('harry potter');           // 半角スペース OK
$result = paapiSearch('鬼滅 の刃');                // 日本語 + スペース OK
print_r($result);

公式 PA-API SDK 利用(推奨)

// Amazon 公式 SDK (paapi5-php-sdk) をインストール
// composer require ...

use Amazon\ProductAdvertisingAPI\v1\ApiException;
use Amazon\ProductAdvertisingAPI\v1\api\DefaultApi;
use Amazon\ProductAdvertisingAPI\v1\Configuration;
use Amazon\ProductAdvertisingAPI\v1\com\amazon\paapi5\v1\SearchItemsRequest;

$config = new Configuration();
$config->setAccessKey(getenv('PAAPI_ACCESS_KEY'));
$config->setSecretKey(getenv('PAAPI_SECRET_KEY'));
$config->setHost('webservices.amazon.co.jp');
$config->setRegion('us-west-2');

$api = new DefaultApi(new \GuzzleHttp\Client(), $config);

$req = new SearchItemsRequest();
$req->setPartnerTag('yourtag-22');
$req->setPartnerType('Associates');
$req->setKeywords('harry potter');     // ★ スペースそのまま
$req->setSearchIndex('All');
$req->setItemCount(10);

try {
    $res = $api->searchItems($req);
    foreach ($res->getSearchResult()->getItems() as $item) {
        echo $item->getItemInfo()->getTitle()->getDisplayValue() . "\n";
    }
} catch (ApiException $e) {
    echo $e->getMessage();
}

JS / Node.js

// fetch で直接呼ぶ場合
const body = JSON.stringify({
  Keywords: 'harry potter',     // スペースそのまま
  SearchIndex: 'All',
  ItemCount: 10,
  PartnerTag: 'yourtag-22',
  PartnerType: 'Associates',
  Marketplace: 'www.amazon.co.jp'
});

// URL 構築時にスペースを含めるなら
const url = 'https://example.com/search?q=' + encodeURIComponent('harry potter');
// → ?q=harry%20potter

v4 (旧) でのスペース

古い PA-API v4 は GET メソッドでクエリ文字列に乗せる方式だったので、必ず URL エンコードが必要でした。

GET /onca/xml?Operation=ItemSearch&Keywords=harry%20potter&...
                                          ↑ 必須エンコード

PHP: rawurlencode('harry potter')  → harry%20potter
JS:  encodeURIComponent('harry potter')  → harry%20potter

よくあるトラブル

症状原因対処
SignatureDoesNotMatch署名対象とボディが不一致JSON ボディ完全一致を確認
"+" がそのまま検索されるurlencode() の "+" 仕様rawurlencode() 使用
日本語が文字化けJSON_UNESCAPED_UNICODE 未指定フラグ追加 + UTF-8 で送信
InvalidParameterValueKeywords が空 or 長すぎ1〜250 文字
BadRequestContent-Type ミスapplication/json; charset=utf-8

Lambda からの呼出

サーバレス環境で PA-API を叩く際はSecrets Manager に Access/Secret Key を保管し、IAM ロールで取得するのが定石。Lambda の実行ロール自体には PA-API のアクセス権限不要(PA-API は IAM ではなく専用 Access Key 認証)。

FAQ

Q: +%20 どちらが正しい?
A: HTTP 仕様としては URL のパスは %20、フォームの application/x-www-form-urlencoded は +。JSON ボディ内ならエンコード不要のスペース。

Q: Keywords に複数語入れると AND 検索?
A: PA-API は内部で AND 寄りに解釈します。完全フレーズなら "" で囲む(JSON 内では \" でエスケープ)。

Q: スペースを + で入れたら署名エラー
A: ボディ JSON 内の + はリテラルの「+」として送信されます。スペースの代わりにしないでください。