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

# Polling

For long-running, asynchronous operations — a job you kick off and then wait on — the SDKs give
you two complementary surfaces: a **generated `poll<Method>()` companion** beside each operation
your spec marks pollable, and a **generic waiter** for any custom predicate.

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

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

const client = new Client({ token: '<token>' });
const result = await client.pets.pollGetPet('string');
```

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

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

const client = new Client({ token: '<token>' });
const result = await client.pets.pollGetPet('string');
```

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

```python
from glotto_sdk import Client

client = Client(token="<token>")
result = client.pets.poll_get_pet(pet_id='string')
```

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

```go
package main

import (
    "context"
    "fmt"

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

func main() {
    ctx := context.Background()
    client := sdk.NewClient(sdk.WithToken("<token>"))
    result, err := client.Pets.PollGetPet(ctx, "string")
    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) throws Exception {
        Client client = Client.builder().token("<token>").build();
        var result = client.pets().pollGetPet("string");
        System.out.println(result);
    }
}
```

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

```kotlin
import com.glotto.Client
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.flow.collect

fun main() = runBlocking {
    val client = Client(token = "<token>")
    val result = client.pets.pollGetPet("string")
    println(result)
}
```

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

```csharp
using Glotto;

var client = new Glotto.Client("<token>");
var result = await client.Pets.PollGetPet("string");
Console.WriteLine(result);
```

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

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

$client = new Glotto\Client(token: '<token>');
$result = $client->pets->pollGetPet('string');
var_dump($result);
```

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

```ruby
require 'glotto'

client = Glotto::Client.new(token: '<token>')
result = client.pets.poll_get_pet(pet_id: 'string')
puts result
```

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

```rust
let client = Client::default().with_token("<token>");
let result = client.pets().poll_get_pet("string", WaitForOptions::default()).await?;
```

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

```swift
import Foundation
import GlottoSdk

let client = Client(token: "<token>")
let result = try await client.pets.pollGetPet(petId: "string")
```

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

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

final client = Client(token: "<token>");
try {
  final result = await client.pets.pollGetPet("string", poll: const WaitForOptions());
} finally {
  client.close();
}
```

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

```elixir
client = Glotto.new(token: "<token>")
{:ok, result} = Glotto.Pets.poll_get_pet(client, "string")
```

## The generated `poll<Method>()` companion

When the spec marks an operation as pollable, the engine emits a companion beside it that
re-fetches on an interval until the resource reaches its terminal state, then returns the typed
result. It honors a deadline and (where idiomatic) an `AbortSignal` / `context` so a poll loop
never runs unbounded.

Because the companion owns the fetch, it sees the HTTP response — so it also honors the server's
**`Retry-After`** cadence hint between attempts, clamped to your `maxDelayMs` so a large hint can
never stall a wait past the budget you set. That is the one thing the generic waiter below cannot
do: you hand it a predicate over an already-decoded value, so the headers are gone by the time it
runs. If your API advertises a poll cadence, the companion is the surface that follows it.

The companion is named for the method it waits on, spelled the way that language spells names —
a method key `get` emits `pollGet` in TypeScript, React Native, Swift and Dart; `PollGet` in Go and
C#, whose methods are exported or PascalCased; `poll_get` in Python, Ruby, Rust, PHP and Elixir; and
`get_job` likewise emits `pollGetJob` / `PollGetJob` / `poll_get_job`. It is never a bare `poll()`.
Its return follows the language's own convention too: TypeScript resolves the typed result and
throws on timeout, while Elixir returns `{:ok, result}` / `{:error, :timeout}` like every other call
in that SDK. **It ships in every SDK we generate** — the generic waiter below remains available for
any predicate the spec does not model.

A few per-language shapes worth knowing:

- **Swift** — `async throws`, taking a trailing `poll: WaitForOptions = WaitForOptions()` after the
  usual request options, and waiting with `Task.sleep`, so cancelling the surrounding task cancels
  the wait (there is no `AbortSignal` to pass).
- **Java** — an overload pair beside the operation: `client.jobs().pollGet(id)` for the defaults, or
  `pollGet(id, intervalMs, maxAttempts, timeoutMs, maxDelayMs)` to set the budget — plus a
  `pollGetAsync` twin returning a `CompletableFuture`, matching the async twin every other Java
  method carries.

## The generic waiter

For anything the spec doesn't model, every SDK also exports a generic waiter —
`waitFor(predicate, options)` (TypeScript), `wait_until(fetch, predicate, …)` / `await_until`
(Python), `WaitUntil(ctx, fetch, predicate, …opts)` (Go), and the equivalent elsewhere. You supply
the predicate; it polls with the configured `intervalMs`,
`timeoutMs`, and `maxAttempts`, returning when the predicate is satisfied and raising a typed
timeout error (`PollingTimeoutError`) when it isn't.

## Tuning

Both surfaces take the same knobs — poll **interval**, overall **timeout**, and **max attempts** —
so you can bound how long a wait runs. Exceeding the timeout or attempt cap raises a typed error
rather than hanging. In Go the overall budget is your `context.WithTimeout` rather than a
`timeoutMs` option; in the C#, Java, Kotlin, PHP, and Ruby SDKs `timeoutMs` is opt-in — passing it
adds a wall-clock deadline on top of the attempt cap, and leaving it off keeps the wait
attempt-bounded.

## Backing off between polls

By default the wait polls on a fixed interval. Set **`maxDelayMs`** (`max_delay_ms` in the
snake_case SDKs, `WithPollMaxDelay` in Go) to switch to capped exponential backoff: the delay
starts at `intervalMs` and doubles each attempt up to the cap —
`min(maxDelayMs, intervalMs * 2^(attempt-1))`. The schedule is deterministic (no jitter), so a
wait is reproducible run-to-run. Python and Go poll with this backoff curve out of the box; for
the generic waiter everywhere else it turns on when you pass the cap. The **companion** always has
a cap — it defaults `maxDelayMs` to 30 seconds even where the generic waiter leaves it off — because
the cap is also what bounds a `Retry-After` hint, so there is always something for a large hint to
be clamped against.
