HTTP Layer

Low-level HTTP transport with rate limiting and retry logic.

HttpClient

class etoropy.HttpClient(config, *, rate_limiter=None)[source]

Bases: object

Async HTTP client wrapping httpx.AsyncClient.

Every request automatically includes x-api-key and x-user-key auth headers, passes through the token-bucket rate limiter, and is retried on transient failures (5xx, 429, connection errors) with exponential backoff and jitter.

Pydantic BaseModel request bodies are serialized with model_dump(by_alias=True, exclude_none=True) so PascalCase field aliases are preserved for the eToro API.

Parameters:
async request(options, response_type=None)[source]
Parameters:
Return type:

Any

async aclose()[source]
Return type:

None

RequestOptions

class etoropy.RequestOptions(method, path, query=None, body=None, request_id=None)[source]

Bases: object

Parameters:
method: str
path: str
query: dict[str, str | int | bool | None] | None = None
body: Any = None
request_id: str | None = None

RateLimiter

class etoropy.RateLimiter(options=None)[source]

Bases: object

Token-bucket rate limiter for outgoing HTTP requests.

Tracks request timestamps in a sliding window (default: 20 requests per 10 seconds). acquire() blocks the caller when the bucket is full until a slot opens.

On HTTP 429 responses, call penalize(retry_after_s) to force all subsequent requests to wait for the server-specified back-off period.

Parameters:

options (RateLimiterOptions | None (default: None))

async acquire()[source]
Return type:

None

penalize(retry_after_s)[source]
Parameters:

retry_after_s (float)

Return type:

None

property queue_size: int
property current_usage: int
property is_penalized: bool
dispose()[source]
Return type:

None

RateLimiterOptions

class etoropy.RateLimiterOptions(max_requests=20, window_s=10.0)[source]

Bases: object

Parameters:
  • max_requests (int (default: 20))

  • window_s (float (default: 10.0))

max_requests: int = 20
window_s: float = 10.0

RetryOptions

class etoropy.http.RetryOptions(attempts=3, delay=1.0, backoff_multiplier=2.0, jitter=True, should_retry=<factory>, get_retry_after_s=None, on_retry=None)[source]

Bases: object

Configuration for the retry() helper.

Parameters:
  • attempts (int (default: 3)) – Maximum number of tries (including the first).

  • delay (float (default: 1.0)) – Base delay in seconds before the first retry.

  • backoff_multiplier (float (default: 2.0)) – Multiplied to the delay after each attempt.

  • jitter (bool (default: True)) – If True, adds +/-25% random jitter to the computed delay.

  • should_retry (Callable[[BaseException], bool] (default: <factory>)) – Predicate that receives the exception and returns True if the request should be retried.

  • get_retry_after_s (Callable[[BaseException], float | None] | None (default: None)) – Optional callback that extracts a server-supplied retry-after delay from the exception (e.g. from a 429 response).

  • on_retry (Callable[[int, float, BaseException], None] | None (default: None)) – Optional callback fired before each retry sleep, receiving (attempt_number, wait_seconds, exception).

attempts: int = 3
delay: float = 1.0
backoff_multiplier: float = 2.0
jitter: bool = True
should_retry: Callable[[BaseException], bool]
get_retry_after_s: Callable[[BaseException], float | None] | None = None
on_retry: Callable[[int, float, BaseException], None] | None = None