Amazon for omnisense/omnitrade, through the Creators API - which replaced the Product Advertising API 5 on 2026-05-15 -: a product by its ASIN or its page, a search, and the affiliate link carrying the Associates partner tag. Nothing is bought here: no API lets a site buy for someone.
| Operation | Call |
|---|---|
| affiliate link | none: written from the ASIN and the tag |
| token | OAuth 2 client credentials: POST https://api.amazon.co.uk/auth/o2/token (credential version 3.2, Europe), https://api.amazon.com/auth/o2/token (3.1, North America), https://api.amazon.co.jp/auth/o2/token (3.3, Far East) |
| product | POST https://creatorsapi.amazon/catalog/v1/getItems |
| products (a search) | POST https://creatorsapi.amazon/catalog/v1/searchItems |
There is no sandbox: the API answers an account's real catalogue.
Installation
composer require omnitrade/amazon
Requires PHP 8.2, omnisense/omnitrade (1.x) and symfony/http-client (6.4, 7 or 8). In a
Symfony application with Omnitrade\Bridge\Symfony\OmnitradeBundle, the factory is registered as
soon as the package is installed.
$gateway = (new AmazonGatewayFactory($http))->create(['partner_tag' => 'mysite-21']); // $http: the application's HTTP client; none given, the factory makes its own
Credentials
- The Associates partner tag (
mysite-21), from the Amazon Associates account of the store linked to: enough for the links. - The Creators API credentials, a client id and a client secret (Associates Central → Tools →
Creators API), with their credential version (3.1, 3.2 or 3.3, which names the token
endpoint). Amazon opens the API only to accounts with qualified sales: until then, only
affiliateLink()is offered, and the product can be read from its page withomnitrade/web.
Options
| Option | Type | Default | Required | |
|---|---|---|---|---|
client_id, client_secret |
?string | null |
for the reads | the Creators API credentials; without both, fetchProduct() and fetchProducts() are not supported (supports() is false) |
partner_tag |
?string | null |
for links | the Associates tag (mysite-21): in every request and every link; without it affiliateLink() answers null |
marketplace |
string | www.amazon.fr |
no | the store read; sent in the body and as the x-marketplace header; the host of a link to a bare ASIN |
credential_version |
string | 3.2 |
no | which token endpoint: 3.1 api.amazon.com, 3.2 api.amazon.co.uk, 3.3 api.amazon.co.jp (/auth/o2/token); another value without token_url is an InvalidConfigException when the gateway is built |
token_url |
?string | null |
no | another token endpoint (earlier, v2 credentials use regional ones) |
api_url |
string | https://creatorsapi.amazon/catalog/v1 |
no | |
timeout |
int | 10 |
no | seconds |
No option is required: create([]) builds a gateway that answers every link null.
omnitrade:
gateways:
amazon:
factory: amazon
options:
client_id: '%env(AMAZON_CREATORS_CLIENT_ID)%'
client_secret: '%env(AMAZON_CREATORS_CLIENT_SECRET)%'
partner_tag: '%env(AMAZON_CREATORS_PARTNER_TAG)%' # mysite-21
marketplace: www.amazon.fr
credential_version: '3.2' # 3.1 North America, 3.2 Europe, 3.3 Far East
Capabilities
| Request | Supported |
|---|---|
AffiliateLink |
yes, always |
FetchProduct |
with client_id and client_secret (getItems) |
FetchProducts |
with client_id and client_secret: a search (searchItems), a query required |
payments, FetchOrder, FetchInventory, Notify, connected accounts, subscriptions |
no |
Behaviour
Affiliate links. No call. The ASIN is the reference itself when it is ten capitals and digits,
else the one in an Amazon address's /dp/, /gp/product/, /gp/aw/d/, /exec/obidos/ASIN/ or
/o/ASIN/. The link is https://<host>/dp/<ASIN>?tag=<tag>: the host is the address's when it is
an Amazon store (www.amazon.de stays www.amazon.de), else marketplace; the tag is the
request's when given, else partner_tag. Everything else in the address (its path's title,
ref=, its query) is dropped. null without a tag, or for a reference that names no ASIN (a
shortened amzn.eu link names none).
Token. Asked for on the first read, not when the gateway is built: a JSON body with
grant_type: client_credentials, the client id and secret and scope: creatorsapi::default. It
is kept in the API client until a minute before it runs out (expires_in, an hour when Amazon does
not say).
Product, search. getItems with the ASIN (itemIdType: ASIN), or searchItems with the
query as keywords and the limit as itemCount (1 to 10); each body carries partnerTag,
marketplace and the resources asked for. A product's reference that names no ASIN answers
null without a call; an ASIN Amazon does not have, null. A search without a query is a
ProviderException (query_required); the cursor and updatedSince are not read and the page
has no next. What is sent and how an item becomes a Product:
the Creators API.
Example
<?php // amazon.php: links with the partner tag, then a product read through the Creators API, on answers shaped as its documentation shows them
require __DIR__.'/vendor/autoload.php';
use Omnitrade\Amazon\AmazonGatewayFactory;
use Omnitrade\Request\FetchProduct;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
$item = [
'asin' => 'B0C1234567',
'detailPageURL' => 'https://www.amazon.fr/dp/B0C1234567?tag=mysite-21',
'images' => ['primary' => ['large' => ['url' => 'https://m.media-amazon.com/images/I/1.jpg', 'width' => 500, 'height' => 500]]],
'itemInfo' => [
'title' => ['displayValue' => 'Tour de lit bébé en coton'],
'byLineInfo' => ['brand' => ['displayValue' => 'Jollein']],
'features' => ['displayValues' => ['Coton bio', 'Lavable à 30°']],
'classifications' => ['productGroup' => ['displayValue' => 'Baby Product']],
],
'offersV2' => ['listings' => [['price' => ['money' => ['amount' => 34.99, 'currency' => 'EUR']], 'availability' => ['type' => 'IN_STOCK'], 'merchantInfo' => ['name' => 'Amazon.fr', 'id' => 'A1X6FK5RDHNB96']]]],
];
$sent = [];
$http = new MockHttpClient(static function (string $method, string $url) use ($item, &$sent): MockResponse {
$sent[] = $method.' '.$url;
return new MockResponse(json_encode(match (true) {
str_ends_with($url, '/auth/o2/token') => ['access_token' => 'Atc|not_a_real_token', 'token_type' => 'bearer', 'expires_in' => 3600],
str_ends_with($url, '/getItems') => ['itemsResult' => ['items' => [$item]]],
}));
});
// The tag alone: links, no call.
$links = (new AmazonGatewayFactory($http))->create(['partner_tag' => 'mysite-21']);
var_dump($links->supports(FetchProduct::class));
echo $links->affiliateLink('https://www.amazon.fr/Jollein-Tour-lit/dp/B0C1234567/ref=sr_1_3?keywords=tour'), "\n";
echo $links->affiliateLink('B0C1234567', tag: 'other-21'), "\n";
var_dump($links->affiliateLink('https://amzn.eu/d/abc123'));
// With the Creators API credentials: the product itself.
$amazon = (new AmazonGatewayFactory($http))->create(['partner_tag' => 'mysite-21', 'client_id' => 'not_a_real_id', 'client_secret' => 'not_a_real_secret']);
$product = $amazon->fetchProduct('https://www.amazon.fr/dp/B0C1234567');
printf("%s %s by %s, %s from %s, %s\n", $product->reference, $product->title, $product->brand, $product->price(), $product->variants[0]->offer()->merchant->name, implode(' / ', $product->categories));
echo $product->description, "\n", $product->url, "\n";
echo implode("\n", $sent), "\n";
$ php amazon.php
bool(false)
https://www.amazon.fr/dp/B0C1234567?tag=mysite-21
https://www.amazon.fr/dp/B0C1234567?tag=other-21
NULL
B0C1234567 Tour de lit bébé en coton by Jollein, 34.99 EUR from Amazon.fr, Baby Product
<ul><li>Coton bio</li><li>Lavable à 30°</li></ul>
https://www.amazon.fr/dp/B0C1234567?tag=mysite-21
POST https://api.amazon.co.uk/auth/o2/token
POST https://creatorsapi.amazon/catalog/v1/getItems
(run on 2026-10-25 in omnisense/omnitrade's harness image, PHP 8.4; with HttpClient::create()
and an eligible account's credentials the same lines read the real item.)
The programme's terms ask for a mention near the links ("En tant que Partenaire Amazon, je
réalise un bénéfice sur les achats remplissant les conditions requises" on amazon.fr) and for
prices shown with the time they were read, at most 24 hours old (the README). In the core's Docker
harness: docker compose run --rm omnitrade link amazon B0C1234567, catalogue amazon --query='tour de lit'.
Errors
Every failure is an Omnitrade\Exception\ProviderException whose provider is amazon:
- an HTTP status of 400 or more: the message is
errors[0].message(orMessage),providerCodeitscode, else the HTTP status; - a body that is not JSON:
The Creators API answered HTTP <status> with a body that is not JSON.; a network failure:The Creators API did not answer: ...(The token endpoint did not answer: ...for the token), a copy of the HTTP client's exception asprevious; - no token: Amazon's
error_descriptionorerror(No token (HTTP <status>).when it says nothing),providerCodeitserroror the status; query_requiredfor a search without a query;no_credentialswhen the API is reached without credentials (the gateway does not offer the reads then, so only an action given by hand gets there).
An unknown credential_version without token_url is an InvalidConfigException when the
gateway is built, even for a gateway that only writes links. Payments, FetchOrder,
FetchInventory and Notify raise RequestNotSupportedException before any call, and so do the
reads without credentials.
No failure carries the Creators API's client secret and the token: a network failure's message has
them masked as *** (the key parameters of a URL too), its previous is a copy of the HTTP client's
exception (the same class and code, without its request), and no trace of the chain keeps its
arguments, where the request's options stood (No secret in a
failure).
Verified, and not
Tested offline, 16 tests on answers shaped as the Creators API documentation shows them:
Tests/AmazonGatewayTest.php (a product by its page, the token kept, the token endpoint of
version 3.2, the headers and the body; a search; without credentials only the link; ASINs in
addresses) and Tests/AmazonContractTest.php, the family's conformance suite with credentials
given (nothing to fix).
Not verified: no call was made with real credentials (the API is opened to accounts with
qualified sales). The field names of externalIds and availability.type are assumed from
PA-API 5's lowerCamelCase equivalents (the Creators API).
Responsibility
An independent, unofficial client, not affiliated with, endorsed by or sponsored by Amazon (Amazon.com, Inc. and its affiliates). Whoever runs it complies with the Amazon Associates Program's terms (Associates Program Operating Agreement) and obtains what they require: see the README's Responsibility section.
Propose a change docs/index.md
The Creators API
What each read sends, and how an item becomes a Product. Installation, credentials and options:
at the head of this manual.
Requests
| Request | Call | |
|---|---|---|
AffiliateLink |
none | https://<store>/dp/<ASIN>?tag=<tag>: the ASIN from the id or from /dp/, /gp/product/, /gp/aw/d/, /exec/obidos/ASIN/ in an Amazon address; the store of the address when it has one, else marketplace. Null without a tag or an ASIN (a shortened amzn.eu link names none). |
FetchProduct |
POST {api_url}/getItems |
itemIds: [ASIN], itemIdType: ASIN; null when Amazon has no such item |
FetchProducts |
POST {api_url}/searchItems |
keywords = the query (required), itemCount = the limit (1 to 10). No cursor, no updatedSince: the page has no next. |
Each call carries Authorization: Bearer <token>, x-marketplace, and in
its JSON body partnerTag, marketplace and resources (lowerCamelCase):
images.primary.large, images.variants.large, itemInfo.title,
itemInfo.byLineInfo, itemInfo.features, itemInfo.externalIds,
itemInfo.classifications, offersV2.listings.price,
offersV2.listings.availability, offersV2.listings.merchantInfo,
parentASIN.
The token is an OAuth 2.0 client-credentials one (grant_type,
client_id, client_secret, scope: creatorsapi::default, as JSON), kept
in memory until a minute before it runs out (an hour).
What is mapped
| Product | From |
|---|---|
reference, handle, the variant's sku |
asin |
title |
itemInfo.title.displayValue |
brand |
itemInfo.byLineInfo.brand.displayValue (else manufacturer) |
description |
itemInfo.features.displayValues, as an HTML list (<ul><li>, each escaped) |
url |
detailPageURL (it carries the tag) |
media |
images.primary.large, images.variants[].large (url, width, height) |
| offers | offersV2.listings[]: price.money.amount / .currency, availability.type, merchantInfo.name |
categories |
itemInfo.classifications.productGroup.displayValue |
the variant's barcode |
itemInfo.externalIds.eaNs.displayValues[0] (else eans) |
merchant |
the store itself: Amazon.fr for www.amazon.fr, its URL https://www.amazon.fr |
Each item is one product with one variant (its reference the ASIN, its image the first picture),
status active, no stock, no options. Each listing with a price is an offer: the price from
money.amount (a decimal) and money.currency, available unless availability.type says out
of stock or unavailable, url the detail page, merchant the listing's merchantInfo (name,
id) or the store, taxIncluded true, no struck-through price. A listing without a price is left
out; an item without a title takes its ASIN as title. The images' alt is the title. The
description is HTML, as the contract has it: the features as a list, each one escaped.
Not supported, and not verified
Purchases, stock, variations, browse nodes, paging a search, the "offers"
of other sellers beyond the listings returned: not offered - Amazon's
documentation read on 2026-10-04 (get-started/using-curl, the migration
guide from PA-API, api-reference/operations/search-items) shows the token
call, getItems and searchItems only in the detail used here. The tests
run on answers shaped as that documentation shows them; no call was made
with real credentials (the API is opened to accounts with qualified sales).
The field names of externalIds and availability.type are assumed from
PA-API 5's lowerCamelCase equivalents: check them on a first real answer
(Product::$raw keeps it whole).