Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [7.1.0] - 2026-09-03

### Added

- `timeout` parameter in `AmazonCreatorsApi` and `AsyncAmazonCreatorsApi` to set the request timeout in seconds, or `None` to wait indefinitely

### Changed

- `AmazonCreatorsApi` API requests now time out after 30 seconds instead of waiting indefinitely, matching the timeout already used by `AsyncAmazonCreatorsApi`. Pass `timeout=None` to restore the previous behavior
- `AsyncAmazonCreatorsApi` now applies `timeout` to the OAuth2 token refresh as well, which previously always used the 5 second default from `httpx`

## [7.0.0] - 2026-09-03

### Removed
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ amazon = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, throttling=4) # M
amazon = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, throttling=0) # No wait time between requests
```

### Timeout

Timeout value represents the number of seconds to wait for a response before failing, being the default value 30 seconds. Use `None` to wait indefinitely.

```python
amazon = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, timeout=10) # Fails after 10 seconds
amazon = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, timeout=0.5) # Fails after half a second
```

It applies to every API request. In `AmazonCreatorsApi` the OAuth2 token refresh is handled by the bundled SDK and is not covered by this value, while `AsyncAmazonCreatorsApi` applies it to the token refresh as well.

### Async Support

For async/await applications, use the async version of the API with `httpx`:
Expand Down
19 changes: 14 additions & 5 deletions amazon_creatorsapi/aio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@

from typing_extensions import Self

from amazon_creatorsapi.core.constants import DEFAULT_THROTTLING
from amazon_creatorsapi.core.constants import DEFAULT_THROTTLING, DEFAULT_TIMEOUT
from amazon_creatorsapi.core.error_handling import handle_api_error
from amazon_creatorsapi.core.parsers import get_asin, get_items_ids
from amazon_creatorsapi.core.resources import get_all_resources
from amazon_creatorsapi.core.validation import validate_and_get_marketplace
from amazon_creatorsapi.core.validation import (
validate_and_get_marketplace,
validate_timeout,
)
from amazon_creatorsapi.errors import ItemsNotFoundError

try:
Expand Down Expand Up @@ -118,9 +121,12 @@ class AsyncAmazonCreatorsApi:
country: Country code (e.g., "ES", "US"). Used to determine marketplace.
marketplace: Marketplace URL (e.g., "www.amazon.es"). Overrides country.
throttling: Wait time in seconds between API calls. Defaults to 1 second.
timeout: Request timeout in seconds, or None to wait indefinitely.
Defaults to 30 seconds.

Raises:
InvalidArgumentError: If neither country nor marketplace is provided.
InvalidArgumentError: If neither country nor marketplace is provided,
or if timeout is not greater than zero.
ValueError: If version is not supported (valid versions: 2.1, 2.2, 2.3,
3.1, 3.2, 3.3).

Expand All @@ -135,6 +141,7 @@ def __init__(
country: CountryCode | None = None,
marketplace: str | None = None,
throttling: float = DEFAULT_THROTTLING,
timeout: float | None = DEFAULT_TIMEOUT,
) -> None:
"""Initialize the async Amazon Creators API client."""
# Validate version early to fail fast (before token manager initialization)
Expand All @@ -147,6 +154,7 @@ def __init__(
self._throttle_lock: asyncio.Lock | None = None
self.tag = tag
self.throttling = float(throttling)
self.timeout = validate_timeout(timeout)

# Determine marketplace from country or direct value
self.marketplace = validate_and_get_marketplace(country, marketplace)
Expand All @@ -157,6 +165,7 @@ def __init__(
credential_id=credential_id,
credential_secret=credential_secret,
version=version,
timeout=self.timeout,
)
self._owns_client = False

Expand All @@ -177,7 +186,7 @@ def _validate_version(self, version: str) -> None:

async def __aenter__(self) -> Self:
"""Enter async context manager, creating a persistent HTTP client."""
self._http_client = AsyncHttpClient(host=API_HOST)
self._http_client = AsyncHttpClient(host=API_HOST, timeout=self.timeout)
await self._http_client.__aenter__()
self._owns_client = True
return self
Expand Down Expand Up @@ -596,7 +605,7 @@ async def _make_request(
if self._http_client is not None:
response = await self._http_client.post(endpoint, headers, body)
else:
async with AsyncHttpClient(host=API_HOST) as client:
async with AsyncHttpClient(host=API_HOST, timeout=self.timeout) as client:
response = await client.post(endpoint, headers, body)

# Handle errors
Expand Down
7 changes: 6 additions & 1 deletion amazon_creatorsapi/aio/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import asyncio
import time

from amazon_creatorsapi.core.constants import DEFAULT_TIMEOUT
from amazon_creatorsapi.errors import AuthenticationError

try:
Expand Down Expand Up @@ -55,6 +56,8 @@ class AsyncOAuth2TokenManager:
credential_secret: OAuth2 credential secret.
version: API version (determines auth endpoint).
auth_endpoint: Optional custom auth endpoint URL.
timeout: Token request timeout in seconds, or None to wait
indefinitely. Defaults to 30 seconds.

"""

Expand All @@ -64,12 +67,14 @@ def __init__(
credential_secret: str,
version: str,
auth_endpoint: str | None = None,
timeout: float | None = DEFAULT_TIMEOUT,
) -> None:
"""Initialize the async OAuth2 token manager."""
self._credential_id = credential_id
self._credential_secret = credential_secret
self._version = version
self._auth_endpoint = self._determine_auth_endpoint(version, auth_endpoint)
self._timeout = timeout

self._access_token: str | None = None
self._expires_at: float | None = None
Expand Down Expand Up @@ -186,7 +191,7 @@ async def refresh_token(self) -> str:
}

try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(timeout=self._timeout) as client:
if self.is_lwa():
response = await client.post(
self._auth_endpoint,
Expand Down
8 changes: 5 additions & 3 deletions amazon_creatorsapi/aio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from typing_extensions import Self

from amazon_creatorsapi.core.constants import DEFAULT_TIMEOUT

if TYPE_CHECKING:
from types import TracebackType

Expand All @@ -26,7 +28,6 @@


DEFAULT_HOST = "https://creatorsapi.amazon"
DEFAULT_TIMEOUT = 30.0
VERSION = version("python-amazon-paapi")
USER_AGENT = f"python-amazon-paapi/{VERSION} (async)"

Expand Down Expand Up @@ -64,14 +65,15 @@ class AsyncHttpClient:

Args:
host: Base URL for API requests. Defaults to Amazon Creators API.
timeout: Request timeout in seconds. Defaults to 30.
timeout: Request timeout in seconds, or None to wait indefinitely.
Defaults to 30.

"""

def __init__(
self,
host: str = DEFAULT_HOST,
timeout: float = DEFAULT_TIMEOUT,
timeout: float | None = DEFAULT_TIMEOUT,
) -> None:
"""Initialize the async HTTP client."""
self._host = host
Expand Down
30 changes: 25 additions & 5 deletions amazon_creatorsapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import time
from typing import TYPE_CHECKING, NoReturn

from amazon_creatorsapi.core.constants import DEFAULT_THROTTLING
from amazon_creatorsapi.core.constants import DEFAULT_THROTTLING, DEFAULT_TIMEOUT
from amazon_creatorsapi.core.error_handling import handle_api_error
from amazon_creatorsapi.core.parsers import get_asin, get_items_ids
from amazon_creatorsapi.core.resources import get_all_resources
from amazon_creatorsapi.core.validation import validate_and_get_marketplace
from amazon_creatorsapi.core.validation import (
validate_and_get_marketplace,
validate_timeout,
)
from amazon_creatorsapi.errors import ItemsNotFoundError
from creatorsapi_python_sdk.api.default_api import DefaultApi
from creatorsapi_python_sdk.api_client import ApiClient
Expand Down Expand Up @@ -68,9 +71,12 @@ class AmazonCreatorsApi:
country: Country code (e.g., "ES", "US"). Used to determine marketplace.
marketplace: Marketplace URL (e.g., "www.amazon.es"). Overrides country.
throttling: Wait time in seconds between API calls. Defaults to 1 second.
timeout: Request timeout in seconds, or None to wait indefinitely.
Defaults to 30 seconds.

Raises:
InvalidArgumentError: If neither country nor marketplace is provided.
InvalidArgumentError: If neither country nor marketplace is provided,
or if timeout is not greater than zero.

Example:
>>> api = AmazonCreatorsApi(
Expand All @@ -93,6 +99,7 @@ def __init__(
country: CountryCode | None = None,
marketplace: str | None = None,
throttling: float = DEFAULT_THROTTLING,
timeout: float | None = DEFAULT_TIMEOUT,
) -> None:
"""Initialize the Amazon Creators API client."""
self._credential_id = credential_id
Expand All @@ -101,6 +108,7 @@ def __init__(
self._last_query_time = time.time() - throttling
self.tag = tag
self.throttling = float(throttling)
self.timeout = validate_timeout(timeout)

# Determine marketplace from country or direct value
self.marketplace = validate_and_get_marketplace(country, marketplace)
Expand Down Expand Up @@ -158,6 +166,7 @@ def get_items(
response = self._api.get_items(
x_marketplace=self.marketplace,
get_items_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand Down Expand Up @@ -258,6 +267,7 @@ def search_items(
response = self._api.search_items(
x_marketplace=self.marketplace,
search_items_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand Down Expand Up @@ -318,6 +328,7 @@ def get_variations(
response = self._api.get_variations(
x_marketplace=self.marketplace,
get_variations_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand Down Expand Up @@ -364,6 +375,7 @@ def get_browse_nodes(
response = self._api.get_browse_nodes(
x_marketplace=self.marketplace,
get_browse_nodes_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand Down Expand Up @@ -392,7 +404,10 @@ def list_feeds(self) -> list[Feed]:
self._throttle()

try:
response = self._api.list_feeds(x_marketplace=self.marketplace)
response = self._api.list_feeds(
x_marketplace=self.marketplace,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)

Expand Down Expand Up @@ -421,6 +436,7 @@ def get_feed(self, feed_name: str, feed_type: FeedType | None = None) -> str:
response = self._api.get_feed(
x_marketplace=self.marketplace,
get_feed_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand All @@ -442,7 +458,10 @@ def list_reports(self) -> list[ReportMetadata]:
self._throttle()

try:
response = self._api.list_reports(x_marketplace=self.marketplace)
response = self._api.list_reports(
x_marketplace=self.marketplace,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)

Expand Down Expand Up @@ -471,6 +490,7 @@ def get_report(self, filename: str, report_type: ReportType | None = None) -> st
response = self._api.get_report(
x_marketplace=self.marketplace,
get_report_request_content=request,
_request_timeout=self.timeout,
)
except ApiException as exc:
self._handle_api_exception(exc)
Expand Down
1 change: 1 addition & 0 deletions amazon_creatorsapi/core/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Constants for the Amazon Creators API."""

DEFAULT_THROTTLING = 1
DEFAULT_TIMEOUT = 30.0

# HTTP status codes
HTTP_NOT_FOUND = 404
Expand Down
21 changes: 21 additions & 0 deletions amazon_creatorsapi/core/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,24 @@ def validate_and_get_marketplace(
return MARKETPLACES[country]
msg = "Either 'country' or 'marketplace' must be provided"
raise InvalidArgumentError(msg)


def validate_timeout(timeout: float | None) -> float | None:
"""Validate the request timeout value.

Args:
timeout: Request timeout in seconds, or None to wait indefinitely.

Returns:
The timeout as a float, or None when disabled.

Raises:
InvalidArgumentError: If the timeout is not greater than zero.

"""
if timeout is None:
return None
if timeout <= 0:
msg = "Timeout must be greater than zero, or None to wait indefinitely"
raise InvalidArgumentError(msg)
return float(timeout)
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
author = "Sergio Abad"

# The full version, including alpha/beta/rc tags
release = "7.0.0"
release = "7.1.0"


# -- General configuration ---------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions docs/pages/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@ api = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, throttling=4) # Make
api = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, throttling=0) # No wait time between requests
```

## Timeout

Timeout value represents the number of seconds to wait for a response before failing, being the default value 30 seconds. Use `None` to wait indefinitely.

```python
api = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, timeout=10) # Fails after 10 seconds
api = AmazonCreatorsApi(ID, SECRET, VERSION, TAG, COUNTRY, timeout=0.5) # Fails after half a second
```

It applies to every API request. In `AmazonCreatorsApi` the OAuth2 token refresh is handled by the bundled SDK and is not covered by this value, while `AsyncAmazonCreatorsApi` applies it to the token refresh as well.

## Async Support

For async/await applications, install with async support:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "python-amazon-paapi"
version = "7.0.0"
version = "7.1.0"
description = "Amazon Creators API wrapper for Python"
readme = "README.md"
requires-python = ">=3.9"
Expand Down
Loading
Loading