# Client Create and configure the client, and read the metadata of every API response. `Client` is the synchronous client and `AsyncClient` is its counterpart for asyncio and Trio. Both take the same arguments and have the same methods, and every `AsyncClient` method is a coroutine. A `Client` pools connections and is safe to share across threads, so create one and reuse it for every request. Use one `AsyncClient` per event loop. ## Constructor Every argument after `api_key` is keyword-only. **Constructor** ```python title="Client" def __init__( self, api_key: str | None = None, *, base_url: str = DEFAULT_BASE_URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | httpx.Timeout | None = DEFAULT_TIMEOUT, http_client: httpx.Client | None = None, user_agent: str | None = None, response_hook: ResponseHook | None = None, ) -> None ``` ```python title="AsyncClient" def __init__( self, api_key: str | None = None, *, base_url: str = DEFAULT_BASE_URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | httpx.Timeout | None = DEFAULT_TIMEOUT, http_client: httpx.AsyncClient | None = None, user_agent: str | None = None, response_hook: ResponseHook | None = None, ) -> None ``` ```python import synthient client = synthient.Client(max_retries=4, user_agent="my-app/1.0") ``` - **Network:** Creating a client sends no request. - **Input:** A missing API key, a key that is not printable ASCII, or an invalid argument raises `ValueError`. An API key that is not a string raises `TypeError`. ## Arguments - `api_key` `str | None` — The API key. `None` reads the `SYNTHIENT_API_KEY` environment variable. Surrounding whitespace is trimmed, and an empty key raises `ValueError`. A key that is not a string raises `TypeError`. - `base_url` `str` — The API base URL, including the version path. The default is `DEFAULT_BASE_URL`. It must be an `http` or `https` URL with a host and no query or fragment, and a trailing slash is removed. - `max_retries` `int` — How many times a failed request is retried. The default is `2`, and `0` disables retries. A negative or non-integer value raises `ValueError`. See [Retries and timeouts](https://docs.synthient.com/sdks/python/errors#retries-and-timeouts). - `timeout` `float | httpx.Timeout | None` — The httpx timeout for every request. The default, `httpx.Timeout(60.0, connect=10.0)`, waits up to 10 seconds to connect and 60 seconds for each read or write, which also suits streams because the API sends a keepalive line every 15 seconds. A number sets every phase, and `None` disables the timeout. Negative, non-finite, and non-numeric values raise `ValueError`. - `http_client` `httpx.Client | None` — An httpx client to send requests with, for proxies, TLS, or transports. On `AsyncClient` it is an `httpx.AsyncClient`. The SDK never closes or modifies it. API redirects are not followed, and the SDK's `timeout` applies to every request it sends. Storage requests exclude its default headers, cookies, query parameters, and authentication to protect credentials and presigned URLs. - `user_agent` `str | None` — Appended to the `User-Agent` header, such as `"my-app/1.2"`. The header starts with `synthient-python/` followed by the SDK version. - `response_hook` `ResponseHook | None` — Called with the `ResponseMetadata` of every API response. See [Response metadata](#response-metadata). ## Closing Closes the connection pool. ```python def close(self) -> None async def aclose(self) -> None ``` - **Cleanup:** A `with` block on `Client`, or an `async with` block on `AsyncClient`, closes the client on exit. A client never closes an `http_client` you passed in. - **Repeat calls:** Calling it again does nothing. - **Errors:** Requests on a closed client raise `RuntimeError`. ## AsyncClient The async client is used the same way, with `await` on every method: ```python import asyncio import synthient async def main() -> None: async with synthient.AsyncClient() as client: info = await client.lookup_ip("8.8.8.8") if info.intelligence is not None: print(info.intelligence.risk_score, info.intelligence.categories) asyncio.run(main()) ``` Feed streams and downloads are async context managers, entered with `async with`. Events from a stream and exports from `iter_exports` are read with `async for`, and a download's body with `aiter_bytes()`. The [Feeds](https://docs.synthient.com/sdks/python/feeds) page shows each one. ## Constants - `DEFAULT_BASE_URL` `str` — `"https://api.synthient.com/api/v4"`, the base URL of the Synthient HTTP API. - `__version__` `str` — The SDK version, such as `"1.0.0"`. It is sent in the `User-Agent` header. ## Response metadata The `response_hook` argument is called synchronously with a `ResponseMetadata` after each API response's headers arrive. ```python import synthient def log_response(meta: synthient.ResponseMetadata) -> None: print(meta.status_code, meta.request_id, meta.headers.get("ratelimit-remaining")) client = synthient.Client(response_hook=log_response) ``` - **Coverage:** The hook sees retries, pages, stream handshakes, and errors. Storage download responses are excluded. - **Async:** The hook is a plain function on `AsyncClient` too. - **Errors:** An exception the hook raises propagates to the caller. - **Concurrency:** A client shared across threads calls it from each of those threads. ### ResponseHook ```python ResponseHook = Callable[[ResponseMetadata], None] ``` ### ResponseMetadata A frozen dataclass describing one HTTP response from the Synthient API. - `status_code` `int` — The HTTP status code of the response. - `request_id` `str` — The `X-Request-Id` header, or `""`. - `headers` `httpx.Headers` — A copy of the response headers, so changing it does not affect the client. Headers can contain sensitive data, such as presigned download URLs. There is no rate limit type. Read the `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers from `headers` when they are present. Limits are shared by every key of a team. See [Rate Limits](https://docs.synthient.com/rate-limits).