> For the complete documentation index, see [llms.txt](/llms.txt)

# Retries & timeouts

Clients retry transient failures automatically, with exponential backoff and jitter. Tune
it per-client at construction:

**TypeScript** (regenerated + byte-diffed in CI `a0f8af4b462c`)

```ts
import { Client } from 'glotto-sdk';

const client = new Client({
  maxAttempts: 4,
  initialDelayMs: 200,
  maxDelayMs: 5000,
  jitter: true,
  timeoutMs: 30000,
});
```

**React Native** (regenerated + byte-diffed in CI `a1fefe7d3458`)

```ts
import { Client } from 'glotto-sdk';

const client = new Client({
  maxAttempts: 4,
  initialDelayMs: 200,
  maxDelayMs: 5000,
  jitter: true,
  timeoutMs: 30000,
});
```

**Python** (regenerated + byte-diffed in CI `2242160f8958`)

```python
from glotto_sdk import Client

client = Client(
    max_attempts=4,
    initial_delay_ms=200,
    max_delay_ms=5000,
    jitter=True,
    timeout_ms=30000,
)
```

**Go** (regenerated + byte-diffed in CI `f0c565cc46f8`)

```go
package main

import (
    "time"
    "context"
    "fmt"

    sdk "example.com/glotto-sdk-go"
)

func main() {
    ctx := context.Background()
    client := sdk.NewClient(sdk.WithToken("<token>"))
    result, err := client.Pets.CreatePet(ctx, sdk.PetCreate{Name: "<name>", Species: "cat"}, sdk.WithTimeout(10 * time.Second), sdk.WithMaxRetries(2), sdk.WithHeader("X-Trace", "example"))
    if err != nil {
        panic(err)
    }
    fmt.Println(result)
}
```

**Java** (regenerated + byte-diffed in CI `12c05e098737`)

```java
import com.glotto.Client;

public class Snippet {
    public static void main(String[] args) {
        Client client = Client.builder().token("<token>").maxAttempts(4).initialDelayMs(200).maxDelayMs(5000).jitter(true).timeoutMs(30000).build();
    }
}
```

**Kotlin** (regenerated + byte-diffed in CI `0f5e53ae2522`)

```kotlin
import com.glotto.Client

fun main() {
    val client = Client(token = "<token>", maxAttempts = 4, initialDelayMs = 200, maxDelayMs = 5000, jitter = true, timeoutMs = 30000)
    println(client)
}
```

**C#** (regenerated + byte-diffed in CI `c5013ce0e88b`)

```csharp
using Glotto;

var client = new Client(new GlottoClientOptions
{
    MaxAttempts = 4,
    InitialDelayMs = 200,
    MaxDelayMs = 5000,
    Jitter = true,
    TimeoutMs = 30000,
});
```

**PHP** (regenerated + byte-diffed in CI `1acd16081b46`)

```php
<?php
require 'vendor/autoload.php';

$client = new Glotto\Client(token: '<token>', maxAttempts: 4, initialDelayMs: 200, maxDelayMs: 5000, jitter: true, timeoutMs: 30000);
```

**Ruby** (regenerated + byte-diffed in CI `a2b854f6593f`)

```ruby
require 'glotto'

client = Glotto::Client.new(token: '<token>', max_attempts: 4, initial_delay_ms: 200, max_delay_ms: 5000, jitter: true, timeout_ms: 30_000)
request_options = Glotto::RequestOptions.new(timeout_ms: 5000, max_retries: 0)
```

**Rust** (regenerated + byte-diffed in CI `716e9ec181d1`)

```rust
let client = Client::default().with_token("<token>").with_max_attempts(4).with_initial_delay_ms(200).with_max_delay_ms(5000).with_jitter(true).with_timeout_ms(30000);
```

**Swift** (regenerated + byte-diffed in CI `0cc357352baf`)

```swift
import Foundation
import GlottoSdk

let client = Client(token: "<token>", maxAttempts: 4, initialDelay: 0.2, maxDelay: 5, jitter: true, timeoutMs: 30000)
let options = RequestOptions(headers: ["X-Trace": "example"], timeoutMs: 10000, maxRetries: 2)
let result = try await client.pets.createPet(body: PetCreate(name: "<name>", species: PetCreateSpecies.cat), options: options)
```

**Dart** (regenerated + byte-diffed in CI `67567d6df435`)

```dart
import 'package:glotto_sdk/glotto_sdk.dart';

final client = Client(token: "<token>", maxAttempts: 4, initialDelayMs: 200, maxDelayMs: 5000, jitter: true, timeoutMs: 30000);
```

**Elixir** (regenerated + byte-diffed in CI `b97af402f1d7`)

```elixir
client = Glotto.new(token: "<token>", max_attempts: 4, initial_delay_ms: 200, max_delay_ms: 5_000, jitter: true, timeout_ms: 30_000)
```

## What gets retried

By default the client retries **safe requests** — `GET`/`HEAD` — plus `5x` responses,
`429`, and transport errors. **Mutations are not retried blindly:** a `POST` is retried
only when idempotency is enabled (`idempotency: true` at construction — see [Idempotency](/docs/idempotency)), so a retry can't double-apply a write.

## Configuring defaults

Set the defaults for everyone in `glotto.yml#/client_settings/retry`
(`max_attempts`, `initial_delay`, `max_delay`, `jitter`); the constructor options above
override them per-client.

## Request timeouts

Each attempt is bounded by an overall request timeout. It defaults to **30 seconds**, and
`client_settings.default_timeout` sets a different spec-wide default — a duration string
of the same grammar as the retry delays (`60s`, `500ms`); a value outside that grammar,
or a zero timeout, is rejected at validation. The generated client bakes the resolved
value as its client-level default. Resolution runs from **per-call → method →
nearest resource → client instance → spec-wide `client_settings` → engine default**. Configure resource
and method preferences with
[`default_request_options`](/docs/glotto-yml-client-behavior#default_request_options).

Every language exposes request controls through its native options, keyword
arguments, context, or cancellation handle. The generated README shows your
SDK's spelling. A per-call retry limit counts retries **after** the initial
attempt: zero makes one attempt. Request-specific headers and timeouts do not
change the client defaults for later calls.

A buffered request's timeout includes reading its response body. For SSE,
NDJSON, and binary downloads, the timeout covers establishment through the first
byte or an empty response. A WebSocket's opening timeout ends at the successful
upgrade. The established stream can outlive that timeout; caller cancellation
and explicit closure remain active. An SDK does not reconnect or replay a
stream after exposing its first byte.

## Capping total retry time

`max_attempts` bounds how many tries happen; `retry.max_elapsed` bounds how *long* they
take overall. Set it (a duration string, e.g. `max_elapsed: 90s`) and every SDK adds an
overall wall-clock deadline spanning all attempts: before committing to a backoff wait,
the client checks that the post-backoff resume time still fits the budget — if it
doesn't, the last response or error surfaces immediately, as if attempts were exhausted.
The generated client exposes it as a constructor knob (`maxElapsedMs` in
TypeScript/React Native/Dart, `max_elapsed_ms`/`with_max_elapsed_ms` in Python, Ruby,
Elixir, and Rust, `MaxElapsedMs` in C#, `maxElapsedMs` in Java/Kotlin, `$maxElapsedMs`
in PHP, `maxElapsed` seconds in Swift, `WithMaxElapsed` in Go), so a caller can still
override the spec-wide default per client. Leave `max_elapsed` unset and no deadline
applies — attempts and per-attempt timeouts are the only bounds, exactly as before.
