# Reliability and idempotency

The SDK retries transient failures only when repeating the request is safe. Every workflow submission carries an idempotency key so a network retry can replay the original run instead of creating another billable run.

## Use a business key for recoverable submissions

When you omit `idempotency_key`, the SDK creates a random key. That protects retries inside one method call. It cannot connect a new process invocation to the earlier submission.

For durable application work, derive a stable key from the operation your system considers unique.

```python
from makra import Makra

job_id = "catalog-refresh-2026-08-21-atlas-lamp"

with Makra() as client:
    run = client.submit_extract(
        ["https://shop.example/products/atlas-lamp"],
        {"price": "The current selling price"},
        idempotency_key=job_id,
    )
    print(run.id)
```

Reuse the same key only with the same request body. Reusing it for different URLs, schemas, execution modes, or configuration is a conflict and is not retried.

## What the SDK retries

Retryable HTTP statuses include request timeout, selected conflict and early-request statuses, rate limiting, and common server or gateway failures. The SDK also retries transport errors for methods marked safe. It honors `Retry-After` when present, otherwise it uses exponential backoff with jitter.

The default is two retries after the initial attempt. `max_retries=0` disables them.

```python
from makra import Makra

client = Makra(
    max_retries=4,
    retry_backoff=1.0,
    connect_timeout=10.0,
)
```

Streaming reconnects are separate from workflow submission. Once a run ID exists, the SDK reconnects with a GET on the run event endpoint and resumes from the last sequence. It does not repeat the original POST.

## Timeout does not mean cancellation

A client timeout means the client stopped waiting. A durable server run may still be queued or running. If a blocking or polling call times out and you have a run ID, reconcile with `get_run()` before submitting replacement work.

Use deferred submission when losing the inbound connection must not lose run identity.

## Handle rate limiting explicitly

`MakraRateLimitError` carries `retry_after` and may carry concurrency details. The built-in retry budget can still expire while the account is at its run limit. Queue work in your application and retry at the operation level with the same idempotency key.

```python
from makra import Makra, MakraRateLimitError

try:
    with Makra(max_retries=2) as client:
        client.extract(
            ["https://shop.example/products/atlas-lamp"],
            {"price": "The current selling price"},
            idempotency_key="atlas-price-refresh-17",
        )
except MakraRateLimitError as error:
    print(error.retry_after, error.concurrency)
    raise
```

Do not start unbounded local retries around the SDK. Bound attempts, preserve the idempotency key, and let a queue absorb sustained capacity pressure.

Next, read [Quality, validation, and drift](/markdown/makra-sdk/v0.0.3-beta/production/quality-validation-and-drift).
