Skip to main content
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
Your polling should be scoped to the endpoints you actually consume. Don’t poll endpoints you don’t need.

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
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 volumeInitial delayMax delay
Low (<10/min)3–5 seconds30 seconds
Medium (10–100/min)2–3 seconds20 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.