# Client Create and configure the client, and read the metadata of every API response. A `SynthientClient` is immutable and safe for concurrent use. Create one and reuse it for every request. It holds a connection pool, so close it when you are done, for example with try-with-resources. ## Constructors The no-argument constructor reads the API key from the `SYNTHIENT_API_KEY` environment variable. ```java public SynthientClient() public SynthientClient(String apiKey) ``` - **Input:** Surrounding whitespace is stripped from the key. A missing or blank key, or one that is not printable ASCII, throws `IllegalArgumentException`. - **Network:** Creating a client sends no request. ## Builder `SynthientClient.builder()` returns a `SynthientClient.Builder` for a client with custom options, and `build()` creates the client. ```java public static Builder builder() public SynthientClient build() ``` - `apiKey` `String` — Sets the API key. Without one, the client reads `SYNTHIENT_API_KEY`. - `baseUrl` `String` — Sets the API base URL, including the version path. The default is `DEFAULT_BASE_URL`. A URL that is not `http` or `https`, has no host, or has user info, a query, or a fragment throws `IllegalArgumentException`. - `maxRetries` `int` — Sets how many times a failed request is retried. The default is `2`, and `0` disables retries. A negative count throws `IllegalArgumentException`. See [Retries and timeouts](https://docs.synthient.com/sdks/java/errors#retries-and-timeouts). - `timeout` `Duration` — Sets the idle timeout, by default 60 seconds: the longest the client waits for the response headers, and then for each part of the body. It bounds silence, not a whole call, so streams and downloads can run for as long as data keeps arriving. The server sends a heartbeat line on every stream about every 15 seconds, so keep the timeout above that if you use streams. A timeout that is not positive throws `IllegalArgumentException`. - `httpClient` `OkHttpClient` — Sets the OkHttp client to derive the SDK's client from, for example to configure a proxy, TLS, or interceptors. The derived client shares its connection pool, proxy, TLS settings, interceptors, and connect timeout, but uses its own dispatcher and read and write timeouts, and no call timeout. It never caches responses or follows the API's redirects, so the API key never reaches the export storage host, and it never lets OkHttp resend a request, so a billed lookup is never sent twice. Redirects within export storage are followed, but never from https to http. Closing the `SynthientClient` leaves this client open. - `userAgent` `String` — Adds a product token, such as `"my-app/1.2"`, after the SDK's `User-Agent`, which is `synthient-java/` followed by the SDK version. - `responseHook` `Consumer` — Sets a function called with the metadata of every API response. See [Response metadata](#response-metadata). ```java static SynthientClient configuredClient() { return SynthientClient.builder() .maxRetries(4) .timeout(Duration.ofSeconds(30)) .userAgent("my-app/1.0") .build(); } ``` - **Input:** `build()` throws `IllegalArgumentException` if no key was set and `SYNTHIENT_API_KEY` is missing or blank, or if the key is not printable ASCII. - **Reuse:** A builder can be reused, and each `build()` creates an independent client. ## Constants - `SynthientClient.DEFAULT_BASE_URL` `String` — `"https://api.synthient.com/api/v4"`, the production API. - `SynthientClient.VERSION` `String` — The SDK version, such as `"1.0.0"`. It is sent in the `User-Agent` header. ## close Closes the client. ```java public void close() ``` - **Cancellation:** Calls waiting for a response are cancelled. - **Errors:** Later calls throw `IllegalStateException`. - **Lifetime:** Open streams and downloads keep running until they are closed. - **Cleanup:** A client built with your own `OkHttpClient` leaves that client open. ## Response metadata The builder's `responseHook` calls a function after each API response's headers arrive, including each retried attempt, each page, each stream connection, and responses that fail to decode. ```java static SynthientClient monitoredClient() { return SynthientClient.builder() .responseHook(meta -> { RateLimit rateLimit = meta.rateLimit(); if (rateLimit != null) { System.out.println(meta.statusCode() + " " + meta.requestId() + " " + rateLimit.remaining()); } }) .build(); } ``` - **Coverage:** Downloads from export storage are not reported. - **Concurrency:** The hook runs on the calling thread and must be safe for concurrent use. - **Errors:** An exception the hook throws fails the call. ### ResponseMetadata The metadata of one API response. `toString()` leaves out the headers. - `statusCode` `int` — The HTTP status of the response. - `requestId` `String` — The `X-Request-Id` header, or an empty string. - `rateLimit` `RateLimit` — The rate limit state, or `null` when the response has no complete `RateLimit-*` headers. - `headers` `Headers` — All response headers, as OkHttp `Headers`. The 307 of an export download carries the presigned URL in `Location`, which grants access to the file, so do not log the headers. ### RateLimit The state of a rate limit bucket, from the `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers. Buckets belong to the organization and are shared by all of its keys. See [Rate Limits](https://docs.synthient.com/rate-limits). - `limit` `int` — The bucket size. - `remaining` `int` — The requests left in the bucket. - `reset` `Duration` — The time until the bucket is full again. `ResponseMetadata` and `RateLimit` are records with public constructors, for tests.