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

# Async Run Pattern

> Upload a file, start an AI run, and poll for results — the standard pattern for extraction at scale.

Most extraction integrations follow a three-step pattern: upload the file, start a run, then poll until results are ready. This decouples ingestion from processing so your system doesn't have to block on a long-running AI call.

## Sync vs async — which to use?

<CardGroup cols={2}>
  <Card title="Synchronous (/extract)" icon="bolt">
    One HTTP call. Blocks until done, returns the result directly. Best for interactive integrations, small files, or when you need the result immediately. Use when processing typically completes in under 60 seconds.
  </Card>

  <Card title="Async (3-step)" icon="arrows-rotate">
    Upload, start, poll. Each step returns immediately. Best for large files, batch workloads, or when you want to decouple ingestion from processing.
  </Card>
</CardGroup>

## The three steps

<Steps>
  <Step title="Upload the file">
    `POST /projects/{project_name}/tables/{table_id}/files`

    Send the file along with its metadata. This creates a row in the extraction table associated with the file but does **not** start processing.

    Returns: `{ "row_id": "uuid" }` — hold onto this.
  </Step>

  <Step title="Start a run">
    `POST /projects/{project_name}/tables/{table_id}/run`

    Pass the `row_id` from step 1. This enqueues the AI job and returns immediately.

    Returns: `{ "run_id": "uuid" }` — use this to poll.

    Optional: pass `"zero_retention": true` to run without persisting intermediate results.
  </Step>

  <Step title="Poll for results">
    `GET /projects/{project_name}/tables/{table_id}/run/{run_id}`

    Poll until `status` is `done` or `error`. A typical poll interval is 2–5 seconds.

    Returns a `DataRow` with the current `status` and, when done, the extracted `data`.
  </Step>
</Steps>

## Run status values

| Status    | Meaning                                         |
| --------- | ----------------------------------------------- |
| `pending` | Queued, not yet started                         |
| `running` | AI is actively processing                       |
| `done`    | Extraction complete — `data` field is populated |
| `error`   | Processing failed                               |

## Full example

<CodeGroup>
  ```python Python theme={null}
  import time, requests, base64

  API_KEY = "YOUR_API_KEY"
  BASE = "https://api.cloudsquid.io/api"
  HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
  PROJECT = "my-project"
  TABLE_ID = "your-table-uuid"

  # Step 1: Upload
  with open("invoice.pdf", "rb") as f:
      file_b64 = base64.b64encode(f.read()).decode()

  upload = requests.post(
      f"{BASE}/projects/{PROJECT}/tables/{TABLE_ID}/files",
      headers=HEADERS,
      json={
          "file": file_b64,
          "filename": "invoice.pdf",
          "mimetype": "application/pdf",
          "file_type": "binary"
      }
  )
  row_id = upload.json()["row_id"]

  # Step 2: Start run
  run = requests.post(
      f"{BASE}/projects/{PROJECT}/tables/{TABLE_ID}/run",
      headers=HEADERS,
      json={"row_id": row_id}
  )
  run_id = run.json()["run_id"]

  # Step 3: Poll
  while True:
      result = requests.get(
          f"{BASE}/projects/{PROJECT}/tables/{TABLE_ID}/run/{run_id}",
          headers=HEADERS
      ).json()
      if result["status"] == "done":
          print(result["data"])
          break
      elif result["status"] == "error":
          raise RuntimeError("Extraction failed")
      time.sleep(3)
  ```

  ```bash cURL theme={null}
  # Step 1: Upload
  ROW_ID=$(curl -s -X POST \
    "https://api.cloudsquid.io/api/projects/my-project/tables/TABLE_ID/files" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"file\": \"$(base64 -i invoice.pdf)\", \"filename\": \"invoice.pdf\", \"mimetype\": \"application/pdf\", \"file_type\": \"binary\"}" \
    | jq -r '.row_id')

  # Step 2: Start run
  RUN_ID=$(curl -s -X POST \
    "https://api.cloudsquid.io/api/projects/my-project/tables/TABLE_ID/run" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"row_id\": \"$ROW_ID\"}" \
    | jq -r '.run_id')

  # Step 3: Poll
  while true; do
    RESULT=$(curl -s \
      "https://api.cloudsquid.io/api/projects/my-project/tables/TABLE_ID/run/$RUN_ID" \
      -H "X-API-Key: YOUR_API_KEY")
    STATUS=$(echo $RESULT | jq -r '.status')
    if [ "$STATUS" = "done" ]; then
      echo $RESULT | jq '.data'
      break
    elif [ "$STATUS" = "error" ]; then
      echo "Extraction failed"; break
    fi
    sleep 3
  done
  ```
</CodeGroup>

## File type options

<Note>
  The `file_type` field controls how Cloudsquid reads the `file` value:

  | `file_type` | `file` field                | Use when                                        |
  | ----------- | --------------------------- | ----------------------------------------------- |
  | `binary`    | Base64-encoded file content | Uploading directly from disk                    |
  | `uri`       | Signed URL string           | File is hosted remotely — Cloudsquid fetches it |
  | `multipart` | Array of parts              | Email (RFC 822) with attachments                |
</Note>

<Card title="Prefer one call? Use synchronous extraction" icon="bolt" href="/api-reference/unified/synchronous-extraction-for-a-file-end-to-end">
  The `/extract` endpoint blocks until done and returns results in a single response.
</Card>
