Back to API Docs

Async Jobs

For large files and long-running operations, use async processing. Submit a job, poll for completion, then download the result. Jobs that exceed sync limits are automatically converted to async.

Auto-Async Thresholds

Requests exceeding these limits are automatically processed as async jobs, even without ?async=true. The response will be a 202 with a job ID instead of the usual result.

OperationSync Limit
Text / HTML / Markdown / JSON to PDF5 MB input
Simple OCR (Tesseract)20 pages
Advanced OCR (DocTR)10 pages
Vision OCR (Claude AI)3 pages

Submit an Async Job

POSThttps://app.alternapdf.com/api/v1/<any-endpoint>?async=true

Append ?async=true to any processing endpoint to force async execution. The request body and parameters remain the same as the synchronous version.

Response (202 Accepted)

JSON Response (202)
{
  "job_id": "job_abc123def456",
  "status": "pending",
  "message": "Job queued for processing",
  "status_url": "https://app.alternapdf.com/api/v1/jobs/job_abc123def456",
  "result_url": "https://app.alternapdf.com/api/v1/jobs/job_abc123def456/result"
}

Get Job Status

GEThttps://app.alternapdf.com/api/v1/jobs/{job_id}

Poll this endpoint to check the current status of a job. Status transitions: pending processing complete or failed. A job can also be cancelled.

cURL
curl -X GET "https://app.alternapdf.com/api/v1/jobs/job_abc123def456" \
  -H "X-API-Key: YOUR_API_KEY"
JSON Response (complete)
{
  "job_id": "job_abc123def456",
  "status": "complete",
  "created_at": "2024-01-15T10:30:00Z",
  "completed_at": "2024-01-15T10:30:45Z",
  "result_url": "https://app.alternapdf.com/api/v1/jobs/job_abc123def456/result"
}

Download Job Result

GEThttps://app.alternapdf.com/api/v1/jobs/{job_id}/result

Download the result of a completed job. For conversion endpoints this returns the output file (PDF, image, etc.). For OCR and extraction endpoints this returns the JSON result.

cURL
curl -X GET "https://app.alternapdf.com/api/v1/jobs/job_abc123def456/result" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output result.pdf

List Jobs

GEThttps://app.alternapdf.com/api/v1/jobs

Query Parameters

ParameterTypeRequiredDescription
statusstringNoFilter by status: pending, processing, complete, failed, cancelled
limitintegerNoMax number of jobs to return. Default: 20
cURL
curl -X GET "https://app.alternapdf.com/api/v1/jobs?status=complete&limit=10" \
  -H "X-API-Key: YOUR_API_KEY"
JSON Response
{
  "jobs": [
    {
      "job_id": "job_abc123def456",
      "status": "complete",
      "created_at": "2024-01-15T10:30:00Z",
      "completed_at": "2024-01-15T10:30:45Z"
    },
    {
      "job_id": "job_xyz789ghi012",
      "status": "complete",
      "created_at": "2024-01-15T09:15:00Z",
      "completed_at": "2024-01-15T09:16:12Z"
    }
  ],
  "total": 2,
  "limit": 10
}

Cancel a Job

DELETEhttps://app.alternapdf.com/api/v1/jobs/{job_id}

Cancel a pending or processing job. Returns 204 No Content on success. Already completed or failed jobs cannot be cancelled.

cURL
curl -X DELETE "https://app.alternapdf.com/api/v1/jobs/job_abc123def456" \
  -H "X-API-Key: YOUR_API_KEY"

# Returns 204 No Content on success

Complete Polling Example

End-to-end example: submit a large OCR job, poll until complete, then download the result.

Python (full polling workflow)
import requests
import time

API_KEY = "YOUR_API_KEY"
BASE = "https://app.alternapdf.com"
HEADERS = {"X-API-Key": API_KEY}

# Step 1: Submit a large PDF for OCR (async)
with open("large-document-50-pages.pdf", "rb") as f:
    files = {"file": ("large-document-50-pages.pdf", f, "application/pdf")}
    data = {"output": "simple"}
    response = requests.post(
        f"{BASE}/api/v1/ocr/extract/advanced?async=true",
        headers=HEADERS,
        files=files,
        data=data,
    )

job = response.json()
job_id = job["job_id"]
print(f"Job submitted: {job_id}")
print(f"Status: {job['status']}")

# Step 2: Poll for completion
while True:
    status_response = requests.get(
        f"{BASE}/api/v1/jobs/{job_id}",
        headers=HEADERS,
    )
    status = status_response.json()
    print(f"Status: {status['status']}")

    if status["status"] == "complete":
        print("Job complete!")
        break
    elif status["status"] == "failed":
        print(f"Job failed")
        exit(1)
    elif status["status"] == "cancelled":
        print("Job was cancelled")
        exit(1)

    # Wait 2 seconds before polling again
    time.sleep(2)

# Step 3: Download the result
result_response = requests.get(
    f"{BASE}/api/v1/jobs/{job_id}/result",
    headers=HEADERS,
)

result = result_response.json()
print(f"Extracted text ({result['data']['word_count']} words):")
print(result["data"]["text"][:500])
JavaScript (full polling workflow)
const fs = require("fs");
const FormData = require("form-data");

const API_KEY = "YOUR_API_KEY";
const BASE = "https://app.alternapdf.com";
const headers = { "X-API-Key": API_KEY };

// Step 1: Submit a large PDF for OCR (async)
const form = new FormData();
form.append("file", fs.createReadStream("large-document-50-pages.pdf"));
form.append("output", "simple");

const submitResponse = await fetch(
  `${BASE}/api/v1/ocr/extract/advanced?async=true`,
  {
    method: "POST",
    headers: { ...headers, ...form.getHeaders() },
    body: form,
  }
);

const job = await submitResponse.json();
const jobId = job.job_id;
console.log(`Job submitted: ${jobId}`);

// Step 2: Poll for completion
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

while (true) {
  const statusResponse = await fetch(`${BASE}/api/v1/jobs/${jobId}`, {
    headers,
  });
  const status = await statusResponse.json();
  console.log(`Status: ${status.status}`);

  if (status.status === "complete") {
    console.log("Job complete!");
    break;
  } else if (status.status === "failed" || status.status === "cancelled") {
    console.error(`Job ${status.status}`);
    process.exit(1);
  }

  await sleep(2000);
}

// Step 3: Download the result
const resultResponse = await fetch(`${BASE}/api/v1/jobs/${jobId}/result`, {
  headers,
});
const result = await resultResponse.json();
console.log(`Extracted text (${result.data.word_count} words):`);
console.log(result.data.text.slice(0, 500));