Errors and retries
How failed requests are reported, and which requests are retried.
Errors
Every exception class the SDK defines derives from SynthientError. A request that fails with a non-success HTTP status raises an APIError:
import synthient
try:
client.lookup_ip("8.8.8.8")
except synthient.APIError as error:
if error.status_code == 402:
print("out of lookup credits")
elif error.status_code == 429 and error.retry_after is not None:
print("rate limited, retry after", error.retry_after)
elif error.status_code == 429:
print("monthly request budget spent")
else:
print("request", error.request_id, "failed:", error.detail or error.title)
- Before a request: An invalid argument, such as a missing API key or a malformed IP address, raises
ValueError, and no request is sent. An API key, IP address, domain, or export ID of the wrong type raisesTypeErrorinstead. - Closed client: A closed client raises
RuntimeError. - Decoding: A response body that cannot be decoded raises a plain
SynthientError.
See Errors for what each status means.
SynthientError
The base class of every SDK error, including grpcschema.SchemaError.
APIConnectionError
A subclass of SynthientError raised when the connection failed, such as after a DNS, connection, or TLS failure, or broke while an API response or stream was being read. The underlying httpx exception is chained as __cause__. Reading a download() body is plain httpx, so a failure there raises the httpx exception itself, such as httpx.ReadError.
APITimeoutError
A subclass of APIConnectionError raised when a request timed out while connecting, sending, or waiting for data. A timeout while you read a download() body raises httpx.ReadTimeout instead.
APIError
A subclass of SynthientError raised when the API, or the storage host during download(), answered with a non-success HTTP status.
- Name
status_code- Type
- int
- Description
The HTTP status code, such as 402 when the account is out of credits.
- Name
type- Type
- str
- Description
The problem type URI, for example
"https://docs.synthient.com/errors/unauthorized", or""when the body had none.
- Name
title- Type
- str
- Description
A short summary of the problem.
- Name
detail- Type
- str
- Description
Explains this occurrence of the problem. For a plain-text error, such as the edge's 401 for a malformed key, it is the response body.
- Name
errors- Type
- dict[str, list[str]]
- Description
Validation messages by request field, set on 422 responses.
- Name
request_id- Type
- str
- Description
Identifies the request when contacting Synthient support. It comes from the
X-Request-Idheader, or the problem'sinstancewhen the header is absent.
- Name
retry_after- Type
- float | None
- Description
Seconds the server asked the caller to wait before retrying, or
Noneif it did not say.
- Logging:
str(error)contains only the HTTP status code and its standard phrase, such as402 Payment Required, so it is safe to log. - Untrusted fields:
type,title,detail,errors, andrequest_idcontain untrusted server data and may include sensitive information, so log them deliberately. - Cause: If reading the error body failed, the httpx exception is chained as
__cause__.
Retries and timeouts
Which requests are retried:
- Account and feed requests are retried on 500, 502, 503, and 504 responses, on 429 responses that carry
Retry-After, and on network failures and timeouts before a response arrives, but not on certificate or proxy errors. A failure while reading a response body or stream is not retried. - Billed lookups (
lookup_ip,lookup_ips, andlookup_domain) are retried only on 429 responses that carryRetry-After, which the rate limiter returns before doing any work, so a lookup is never charged twice.healthis retried the same way, so an unhealthy API is reported at once. - A 429 without
Retry-Aftermeans the key's monthly request budget is spent or it has too many open streams, so it is raised without a retry. - Downloads from the export storage host are never retried.
How retries wait:
- Retries back off exponentially from one second, doubling up to 30 seconds, with 25% jitter.
- The wait is never shorter than
Retry-After. - A
Retry-Afterlonger than a minute raises the error instead of retrying. - Set the retry count with
max_retries.
The timeout argument bounds each network operation of an attempt, such as connecting or a single read, not a whole call with its retries. The default waits up to 10 seconds to connect and 60 seconds for each read or write:
import httpx
import synthient
client = synthient.Client(timeout=httpx.Timeout(30.0, connect=5.0))
A timeout raises APITimeoutError, except while you read a download() body, where httpx raises httpx.ReadTimeout.
With AsyncClient, bound a whole call, including its retry waits, with asyncio.timeout() on asyncio, or with anyio.fail_after(), which works on both asyncio and Trio:
import anyio
with anyio.fail_after(10):
info = await client.lookup_ip("8.8.8.8")
print(info.ip)