Asynchronous jobs

A synchronous request holds the connection until the answer is ready. For long documents, large batches or workflows that should not wait, use asynchronous mode: send mode=async, receive a job_id at once, and fetch the result when it is ready.

The flow

  1. Submit. POST /v1/convert (or /v1/convert/batch) with mode=async. The answer is 202 Accepted with a job_id, status: "pending" and a status_url.
  2. Wait. The job moves from pending to processing to completed (or failed).
  3. Fetch. GET /v1/jobs/{job_id} returns the job, with the result in data once it is completed. Or pass a webhook_url when you submit and DocSlice calls you when the job ends (see Webhooks).
Start an asynchronous conversion
curl -s https://api.docslice.app/v1/convert \
  -H "Authorization: Bearer $DOCSLICE_API_KEY" \
  -F "file=@invoice.pdf" \
  -F "format=json" \
  -F "mode=async"
import json
import os
import requests

api_key = os.environ["DOCSLICE_API_KEY"]

with open("invoice.pdf", "rb") as handle:
    response = requests.post(
        "https://api.docslice.app/v1/convert",
        headers={"Authorization": f"Bearer {api_key}"},
        files={"file": ("invoice.pdf", handle, "application/pdf")},
        data={"format": "json", "mode": "async"},
        timeout=120,
    )

print(response.status_code)  # 202: accepted, the work continues in the background
print(json.dumps(response.json(), indent=2))
const fs = require("node:fs");

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("invoice.pdf")], { type: "application/pdf" }), "invoice.pdf");
form.append("format", "json");
form.append("mode", "async");

fetch("https://api.docslice.app/v1/convert", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` },
  body: form,
}).then(async (response) => {
  console.log(response.status); // 202: accepted, the work continues in the background
  console.log(JSON.stringify(await response.json(), null, 2));
});
<?php
$curl = curl_init('https://api.docslice.app/v1/convert');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DOCSLICE_API_KEY')],
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('invoice.pdf', 'application/pdf', 'invoice.pdf'),
        'format' => 'json',
        'mode' => 'async',
    ],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 120,
]);
$body = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE) . PHP_EOL; // 202: accepted, the work continues in the background
echo $body;
curl_close($curl);

Poll until it ends

Start a job and wait for the result
JOB=$(curl -s https://api.docslice.app/v1/convert \
  -H "Authorization: Bearer $DOCSLICE_API_KEY" \
  -F "file=@invoice.pdf" \
  -F "mode=async" | grep -o '"job_id":"[^"]*"' | cut -d'"' -f4)

for attempt in $(seq 1 60); do
  RESULT=$(curl -s "https://api.docslice.app/v1/jobs/$JOB" -H "Authorization: Bearer $DOCSLICE_API_KEY")
  case "$RESULT" in
    *'"status":"completed"'* | *'"status":"failed"'*) echo "$RESULT"; break ;;
  esac
  sleep 2
done
import json
import os
import time
import requests

api_key = os.environ["DOCSLICE_API_KEY"]
headers = {"Authorization": f"Bearer {api_key}"}

with open("invoice.pdf", "rb") as handle:
    accepted = requests.post(
        "https://api.docslice.app/v1/convert",
        headers=headers,
        files={"file": ("invoice.pdf", handle, "application/pdf")},
        data={"mode": "async"},
        timeout=120,
    ).json()

for _ in range(60):
    job = requests.get(f"https://api.docslice.app/v1/jobs/{accepted['job_id']}", headers=headers, timeout=30).json()
    if job["status"] in ("completed", "failed"):
        break
    time.sleep(2)

print(json.dumps(job, indent=2))
const fs = require("node:fs");

const headers = { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` };

async function main() {
  const form = new FormData();
  form.append("file", new Blob([fs.readFileSync("invoice.pdf")], { type: "application/pdf" }), "invoice.pdf");
  form.append("mode", "async");
  const accepted = await (await fetch("https://api.docslice.app/v1/convert", { method: "POST", headers, body: form })).json();

  let job;
  for (let attempt = 0; attempt < 60; attempt++) {
    job = await (await fetch(`https://api.docslice.app/v1/jobs/${accepted.job_id}`, { headers })).json();
    if (job.status === "completed" || job.status === "failed") break;
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }
  console.log(JSON.stringify(job, null, 2));
}

main();
<?php
function call($url, $options = []) {
    $curl = curl_init($url);
    curl_setopt_array($curl, $options + [
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DOCSLICE_API_KEY')],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 120,
    ]);
    $body = curl_exec($curl);
    curl_close($curl);
    return json_decode($body, true);
}

$accepted = call('https://api.docslice.app/v1/convert', [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('invoice.pdf', 'application/pdf', 'invoice.pdf'),
        'mode' => 'async',
    ],
]);

for ($attempt = 0; $attempt < 60; $attempt++) {
    $job = call('https://api.docslice.app/v1/jobs/' . $accepted['job_id']);
    if (in_array($job['status'], ['completed', 'failed'], true)) {
        break;
    }
    sleep(2);
}
echo json_encode($job, JSON_PRETTY_PRINT);

Polling every couple of seconds is plenty. Stop when status is completed or failed, and give up after a sensible number of attempts.

Job statuses

status Meaning
pending Accepted, waiting to start
processing Being converted
completed Done: the result is in data
failed It did not finish: the reason is in error_message

What a job contains

id, status, file_name, file_type, pages_count, output_format, ocr_applied, is_batch with total_files, successful_files and failed_files, data, input_truncated, result_expired, error_message, webhook_url, webhook_status, created_at and completed_at.

Results are kept for 24 hours

The content of a finished job is erased 24 hours after it completed. The job record stays, so you can still see its status and page count, but data becomes null and result_expired is true. Save the result on your side as soon as you receive it.

Listing your jobs

GET /v1/jobs returns your latest jobs, newest first. Use limit (1 to 100, default 20).

List your latest jobs
curl -s "https://api.docslice.app/v1/jobs?limit=5" \
  -H "Authorization: Bearer $DOCSLICE_API_KEY"
import json
import os
import requests

response = requests.get(
    "https://api.docslice.app/v1/jobs",
    headers={"Authorization": f"Bearer {os.environ['DOCSLICE_API_KEY']}"},
    params={"limit": 5},
    timeout=30,
)
print(json.dumps(response.json(), indent=2))
fetch("https://api.docslice.app/v1/jobs?limit=5", {
  headers: { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` },
})
  .then((response) => response.json())
  .then((data) => console.log(JSON.stringify(data, null, 2)));
<?php
$curl = curl_init('https://api.docslice.app/v1/jobs?limit=5');
curl_setopt_array($curl, [
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DOCSLICE_API_KEY')],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);
echo curl_exec($curl);
curl_close($curl);

You can only read your own jobs: asking for another user's job returns 403, and an unknown id returns 404.