Parsli documentation

Build with the API

Large and slow files

The three-step upload-and-poll route. No size ceiling, no request timeout — the only reliable way to handle documents you did not choose.


The inline route is bounded by request body size and request wall-clock. This route is bounded by neither: bytes go straight to storage without passing through the API, and a worker does the extraction.

Use it for anything large, multi-page, or slow — and for anything a user chose, because you cannot predict what they will upload.

The three steps

1. Ask for an upload URL

bash
curl -X POST https://parsli.co/api/v1/upload-url \
  -H "Authorization: Bearer ext_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "file_name": "statement.pdf", "file_type": "application/pdf" }'
json
{
  "upload_url": "https://...storage.../object/upload/sign/...",
  "storage_path": "8c1f.../6f3a.../api/9b2e.../statement.pdf",
  "expires_in": 7200
}

expires_in is seconds. Upload within that window or request a fresh URL.

2. PUT the bytes to that URL

bash
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @statement.pdf

This request does not carry your API key — the signed URL is the credential, and it is single-purpose and short-lived. The bytes never touch the Parsli API, which is precisely why there is no size ceiling here.

3. Submit the path for extraction

bash
curl -X POST https://parsli.co/api/v1/extract \
  -H "Authorization: Bearer ext_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storage_path": "8c1f.../6f3a.../api/9b2e.../statement.pdf",
    "file_name": "statement.pdf",
    "file_type": "application/pdf",
    "file_size": 48291043
  }'
json
{
  "success": true,
  "document_id": "b71c9d02-...",
  "status": "processing",
  "poll_url": "/api/v1/documents/b71c9d02-..."
}

You get a document_id immediately. Extraction happens in the background.

Important: A storage_path submission is always processed asynchronously, even for a small file. You will get status: "processing" and never an inline result on this route. Do not write a client that expects results here.

Collecting the result

GET /api/v1/documents/{document_id}

bash
curl https://parsli.co/api/v1/documents/b71c9d02-... \
  -H "Authorization: Bearer ext_YOUR_API_KEY"
json
{
  "document_id": "b71c9d02-...",
  "status": "completed",
  "file_name": "statement.pdf",
  "page_count": 42,
  "results": { "account_number": "...", "transactions": [] },
  "error": null
}
statusMeaning
processingStill working. Poll again.
completedDone. results is populated.
errorFailed. error explains why; credits were refunded.

Polling politely

javascript
async function waitForResult(documentId, { timeoutMs = 15 * 60_000 } = {}) {
  const started = Date.now()
  let delay = 2_000

  while (Date.now() - started < timeoutMs) {
    const res = await fetch(`https://parsli.co/api/v1/documents/${documentId}`, {
      headers: { Authorization: `Bearer ${process.env.PARSLI_API_KEY}` },
    })
    const doc = await res.json()

    if (doc.status === "completed") return doc.results
    if (doc.status === "error") throw new Error(doc.error ?? "Extraction failed")

    await new Promise((r) => setTimeout(r, delay))
    delay = Math.min(delay * 1.5, 30_000)  // back off; large scans take minutes
  }
  throw new Error("Timed out waiting for extraction")
}

Start around two seconds and back off. A forty-page scan takes minutes, and polling it every second just burns both our budgets.

Better still: do not poll at all. Configure an outbound Webhook on the parser and Parsli calls you when the document finishes. See receiving results.

Choosing a route programmatically

javascript
const INLINE_LIMIT = 4 * 1024 * 1024  // conservative: base64 adds ~33%

async function extract(buffer, name, type) {
  return buffer.byteLength > INLINE_LIMIT
    ? extractViaUpload(buffer, name, type)
    : extractInline(buffer, name, type)
}

Size is the easy signal. The one it misses is a small file that is slow — a dense two-page scan can outlast an inline request. If you see 503 on inline extractions, move that class of document to this route.

Cost

Identical to the inline route: one Credit per Page, refunded on failure. The route you choose does not change the price.

Something here wrong or missing? Tell us — we treat it as a bug.