> ## Documentation Index
> Fetch the complete documentation index at: https://developers.resistant.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Polling 

Polling is the default way to retrieve analysis results from the Resistant Documents API. After you create a submission and upload the file, your system repeatedly calls the relevant result endpoint(s) until processing completes.

## When to use polling

Polling is recommended when you want:

* the simplest implementation (no additional infrastructure)
* predictable integration behavior
* full control over retry and timeout logic

If you want an event-driven integration, see:

* Amazon SQS event notifications (optional)
* Webhooks via Svix (optional)

***

## What endpoints are polled

Poll the endpoint(s) that correspond to the pipeline features you use:

* Fraud: `GET /v2/submission/{submission_id}/fraud`
* Quality: `GET /v2/submission/{submission_id}/quality`
* Classification: `GET /v2/submission/{submission_id}/classification`
* Adaptive Decision: `GET /v2/submission/{submission_id}/decision`
* Report: `GET /v2/submission/{submission_id}/report`

<Warning>
  Your polling should be scoped to the endpoints you actually consume. Don't poll endpoints you don't need.
</Warning>

***

## Recommended polling strategy

### Exponential backoff (required)

Use exponential backoff to avoid unnecessary load on the API.

Recommended behavior:

* start with a short delay
* increase delay exponentially
* add jitter to prevent synchronized client bursts
* cap the delay at **45 seconds**

```python theme={null}
import time, random

def poll_fraud_result(submission_id, token, max_retries=20):
    base_delay = 1  # seconds
    max_delay = 900  # 15 minutes hard cap
    
    for attempt in range(max_retries):
        response = requests.get(
            f"https://api.documents.resistant.ai/v2/submission/{submission_id}/fraud",
            headers={"Authorization": f"Bearer {token}"}
        )
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 404:
            # Not ready yet
            delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
            time.sleep(delay)
        else:
            response.raise_for_status()
    raise TimeoutError("Max polling retries exceeded")
```

**Recommended Polling Intervals**

| Document volume     | Initial delay             | Max delay  |
| ------------------- | ------------------------- | ---------- |
| Low (\<10/min)      | 3–5 seconds               | 30 seconds |
| Medium (10–100/min) | 2–3 seconds               | 20 seconds |
| High (>100/min)     | Switch to SQS or Webhooks | —          |

### Hard analysis timeout (15 minutes)

Stop polling after **15 minutes**. Treat this as a hard upper bound for analysis completion and fail gracefully (log and surface an actionable error).

***

## How to detect “completion”

Result endpoints return a response with a status field. Your integration should treat these as terminal states:

* `SUCCESS` → results available
* `FAILED` → processing failed
* `INVALID_INPUT` → input could not be processed
* `SKIPPED` → processing step did not run (e.g., not enabled)

> For each endpoint, handle terminal states consistently and avoid infinite loops.

## Handling errors and retries

### 429 Too Many Requests

If you receive 429, you are being throttled. Recommended behavior:

* increase delay (apply backoff)
* add jitter
* avoid parallel polling bursts across many submissions

### 5xx server errors

If you receive transient 5xx errors:

* retry with backoff
* log request/response metadata (submission\_id, timestamp, endpoint, status code)

### 4xx client errors

Most 4xx errors are not retriable (except 429). Treat them as failures and fix the request.
