|
| 1 | +Architecture |
| 2 | +============ |
| 3 | + |
| 4 | +This page explains how PYTHON-AMAZON-SP-API is structured so you know what the |
| 5 | +library is doing under the hood and how to extend it safely. |
| 6 | + |
| 7 | +High-level components |
| 8 | +--------------------- |
| 9 | + |
| 10 | +The project is organised roughly as: |
| 11 | + |
| 12 | +* ``sp_api.base`` – core building blocks (client, response wrapper, marketplaces, exceptions, credentials) |
| 13 | +* ``sp_api.api`` – one client class per SP-API group (Orders, Reports, Feeds, DataKiosk, ...) |
| 14 | +* ``sp_api.util`` – utilities for retries, throttling and pagination |
| 15 | +* ``docs`` – Sphinx documentation |
| 16 | + |
| 17 | +Most users only touch the classes in ``sp_api.api``, but everything ultimately |
| 18 | +flows through :class:`sp_api.base.Client`. |
| 19 | + |
| 20 | +Client lifecycle |
| 21 | +---------------- |
| 22 | + |
| 23 | +All endpoint clients inherit from :class:`sp_api.base.client.Client`, which |
| 24 | +itself extends :class:`sp_api.base.base_client.BaseClient`. |
| 25 | + |
| 26 | +When you do: |
| 27 | + |
| 28 | +.. code-block:: python |
| 29 | +
|
| 30 | + from sp_api.api import Orders |
| 31 | +
|
| 32 | + client = Orders() |
| 33 | +
|
| 34 | +the following happens internally: |
| 35 | + |
| 36 | +1. Credentials are resolved via :class:`sp_api.base.credential_provider.CredentialProvider` |
| 37 | + (code → env vars → config file). |
| 38 | +2. The marketplace is chosen, either from the ``marketplace`` argument or from |
| 39 | + the ``SP_API_DEFAULT_MARKETPLACE`` environment variable. |
| 40 | +3. An :class:`sp_api.auth.AccessTokenClient` is created for the account. |
| 41 | +4. The client prepares the base endpoint URL, marketplace id and region. |
| 42 | +5. The **request method**, path, params and body are passed to ``Client._request``. |
| 43 | + |
| 44 | +Request flow |
| 45 | +------------ |
| 46 | + |
| 47 | +All concrete endpoint methods eventually call :meth:`sp_api.base.client.Client._request`. |
| 48 | + |
| 49 | +Key steps: |
| 50 | + |
| 51 | +* The HTTP method is taken from the ``method`` parameter (if present in ``params`` or ``data``) |
| 52 | + or defaults to ``GET``. |
| 53 | +* ``_add_marketplaces`` injects marketplace parameters into the request if you did not |
| 54 | + explicitly set them. It handles both ``MarketplaceId`` and ``MarketplaceIds`` forms. |
| 55 | +* Request headers include: |
| 56 | + |
| 57 | + * ``x-amz-access-token`` – from either the account's access token or a restricted data token |
| 58 | + * ``x-amz-date`` – current UTC timestamp |
| 59 | + * ``user-agent`` – a library user agent |
| 60 | + * ``host`` – derived from the marketplace endpoint |
| 61 | + |
| 62 | +* The request is sent using :func:`requests.request`. |
| 63 | + |
| 64 | +Response handling |
| 65 | +----------------- |
| 66 | + |
| 67 | +Responses are normal ``requests`` responses but wrapped into |
| 68 | +:class:`sp_api.base.ApiResponse` via ``Client._check_response``. |
| 69 | + |
| 70 | +* On success, the JSON body is parsed into the ``payload`` attribute. |
| 71 | +* If the top-level JSON is a list, it is either wrapped into ``{'payload': list}`` |
| 72 | + or the first item is used (depending on the operation). |
| 73 | +* If the response contains an ``errors`` key, an appropriate |
| 74 | + :class:`sp_api.base.exceptions.SellingApiException` subclass is raised |
| 75 | + based on the HTTP status. |
| 76 | + |
| 77 | +The :class:`sp_api.base.ApiResponse` object gives you: |
| 78 | + |
| 79 | +* ``payload`` – the original JSON payload |
| 80 | +* ``errors`` – if present |
| 81 | +* ``headers`` – HTTP headers (useful for rate limits) |
| 82 | +* ``nextToken`` / ``pagination`` – when applicable |
| 83 | +* ``__call__`` – shorthand for ``payload`` |
| 84 | +* ``__getattr__`` – convenience accessor to payload keys |
| 85 | + |
| 86 | +See :doc:`responses` for details. |
| 87 | + |
| 88 | +Credentials and marketplaces |
| 89 | +---------------------------- |
| 90 | + |
| 91 | +Credentials are injected by :class:`sp_api.base.credential_provider.CredentialProvider`. |
| 92 | +You generally do not need to instantiate this manually – passing ``credentials=...`` or |
| 93 | +setting env vars / config file is enough. |
| 94 | + |
| 95 | +Marketplaces are defined in :mod:`sp_api.base.marketplaces` and expose: |
| 96 | + |
| 97 | +* The SP-API endpoint URL |
| 98 | +* The marketplaceId |
| 99 | +* The region |
| 100 | + |
| 101 | +Client classes accept a ``marketplace`` keyword which defaults to |
| 102 | +``Marketplaces.US`` if no override is found, or to ``SP_API_DEFAULT_MARKETPLACE`` |
| 103 | +when that environment variable is set. |
| 104 | + |
| 105 | +Grantless operations & RDT |
| 106 | +-------------------------- |
| 107 | + |
| 108 | +Two special cases: |
| 109 | + |
| 110 | +* **Grantless operations**: some SP-API operations do not require seller-level |
| 111 | + authorization but do require a **scope**. Endpoint clients specify a |
| 112 | + ``grantless_scope`` and internally call ``Client._request_grantless_operation``. |
| 113 | +* **Restricted Data Token (RDT)**: when you pass ``restricted_data_token=...`` to a client, |
| 114 | + the token is used instead of the normal access token (see the Quickstart and PII docs). |
| 115 | + |
| 116 | +Retry & pagination utilities |
| 117 | +---------------------------- |
| 118 | + |
| 119 | +The :mod:`sp_api.util` module provides decorators that work with any endpoint |
| 120 | +methods returning an :class:`sp_api.base.ApiResponse`: |
| 121 | + |
| 122 | +* :func:`sp_api.util.load_all_pages` – transforms a function into a generator |
| 123 | + that automatically follows ``NextToken`` (or another token name you configure). |
| 124 | +* :func:`sp_api.util.throttle_retry` – retries on throttling exceptions |
| 125 | + (HTTP 429 / :class:`sp_api.base.SellingApiRequestThrottledException`). |
| 126 | +* :func:`sp_api.util.sp_retry` – more general retry decorator. |
| 127 | + |
| 128 | +These utilities are intentionally decoupled: you can apply them to your own |
| 129 | +wrappers around endpoint calls without changing the endpoint clients themselves. |
| 130 | + |
| 131 | +Extending with new endpoints |
| 132 | +---------------------------- |
| 133 | + |
| 134 | +All endpoint clients in :mod:`sp_api.api` follow the same internal pattern: |
| 135 | + |
| 136 | +* A class deriving from :class:`sp_api.base.client.Client` |
| 137 | +* One method per SP-API operation |
| 138 | +* A decorator (``@sp_endpoint``) capturing the path template and HTTP method |
| 139 | + |
| 140 | +To add support for a missing SP-API model, you can use the ``make_endpoint`` |
| 141 | +helper from the repository root: |
| 142 | + |
| 143 | +.. code-block:: bash |
| 144 | +
|
| 145 | + make_endpoint https://raw.githubusercontent.com/amzn/selling-partner-api-models/main/models/listings-restrictions-api-model/listingsRestrictions_2021-08-01.json |
| 146 | +
|
| 147 | +This will generate a new endpoint client file that you can adjust and, ideally, |
| 148 | +contribute back via a pull request. |
| 149 | + |
| 150 | +When to dive deeper |
| 151 | +------------------- |
| 152 | + |
| 153 | +You may want to look at the internals when: |
| 154 | + |
| 155 | +* Implementing new endpoints or tweaking existing ones |
| 156 | +* Integrating with non-standard authentication flows |
| 157 | +* Debugging low-level issues (signature, region, marketplace routing) |
| 158 | +* Adapting the base client to other REST APIs |
| 159 | + |
| 160 | +For most use cases, staying at the :mod:`sp_api.api` layer plus |
| 161 | +:mod:`sp_api.util` is sufficient. |
0 commit comments