Errors & retries
What each endpoint can return is in the API reference, and request limits are in Rate limits. The SDK raises one exception per HTTP status, so you can catch the case you care about.
Exceptions
Section titled “Exceptions”str(exc) is the API’s error message, and exc.status_code, exc.body, exc.method and exc.path describe the request.
| Status | Exception | Typical cause |
|---|---|---|
| 400 | STXValidationException |
A bad parameter: status has invalid value: foo |
| 401 | STXAuthenticationException |
Bad key, bad signature, or a clock more than 30 s off |
| 403 | STXForbiddenException |
A read_only key on an order route |
| 404 | STXNotFoundException |
Unknown id, or one belonging to another account |
| 422 | STXRejectedException |
The exchange refused: closed market, insufficient funds, fractional quantity |
| 429 | STXRateLimitException |
Too many requests; retry_after in seconds when sent |
| 5xx | STXServerException |
Server failure |
All of those derive from STXAPIException, which derives from STXException. Three more:
STXTransportException: no answer at all (connection refused, DNS, TLS, timeout)STXConfigException: the client is misconfigured (unknown region, missing key, bad PEM)STXChannelException: a WebSocket join or control event was refused;.replyholds the reason
from stx import STX, STXNotFoundException, STXRejectedException, STXValidationException
with STX() as client: try: client.order("00000000-0000-0000-0000-000000000000") except STXNotFoundException as exc: print("404:", exc)
try: client.markets(status="not-a-status") except STXValidationException as exc: print(exc.status_code, exc)
market = next( m for m in client.iter_markets(status="open", trading=True, sort_by="event_start", sort_direction="desc") if m.event_status == "scheduled" ) try: client.place_order(market.market_id, "buy", "limit", quantity="1") except (STXRejectedException, STXValidationException) as exc: print(exc.status_code, exc)What is retried
Section titled “What is retried”| Failure | GET, DELETE |
POST |
|---|---|---|
| 429 | retried, waiting Retry-After |
retried, waiting Retry-After |
| 5xx | retried | not retried |
| Connection error or timeout | retried | not retried |
| 400, 401, 403, 404, 422 | not retried | not retried |
A POST that failed with a server error or a dropped connection may still have placed the order, so the SDK does not send it again. Use client_order_id and look the order up (see Avoid duplicate orders).
Every attempt is signed afresh. Backoff is exponential with jitter: 0.5 s, 1 s, 2 s, capped at 30 s, over 3 attempts by default. timeout is seconds per HTTP request (default 30).
from stx import NO_RETRY, STX, RetryPolicy
policy = RetryPolicy(max_attempts=5, initial_backoff=0.25, max_backoff=10)with STX(retry=policy, timeout=10.0) as client: print(len(client.markets(limit=1)))
with STX(retry=NO_RETRY) as client: print(client.me().scope)WebSocket failures
Section titled “WebSocket failures”The socket reconnects on its own (see Reconnects). A channel that the server closes or errors is rejoined; a rejoin refused as unauthorized closes that channel instead of retrying forever. ReconnectPolicy(max_attempts=...) bounds reconnects; after the last one, run_forever() returns and every channel’s iterator ends.
Logging
Section titled “Logging”The SDK logs to the stx logger (and stx.ws for the socket): requests at DEBUG, retries and reconnects at INFO and WARNING.
import logging
from stx import STX
logging.basicConfig(level=logging.INFO)logging.getLogger("stx").setLevel(logging.DEBUG)
with STX() as client: client.me()
