Folio

Quickstart

Create a key in the dashboard, then send it a document.

curl -X POST https://quirelabs.com/api/folio/v1/convert \
  -H "Authorization: Bearer $FOLIO_KEY" \
  -H "x-filename: report.docx" \
  --data-binary @report.docx
{
  "markdown": "# Quarterly report\n\nRevenue grew across every region…",
  "format": "docx",
  "bytes": 24576,
  "durationMs": 4.2,
  "usage": { "documents": 1, "ocrPages": 0, "used": 118, "included": 10000 }
}

The filename is a hint, not a requirement. Formats are detected from the content signature first, so a .xlsx that is really a .docx converts as what it actually is. Only CSV has no signature to find, which is the one case where the extension decides.

A PDF with scans in it

Nothing changes about the request. The response gains a pages breakdown.

curl -X POST https://quirelabs.com/api/folio/v1/convert \
  -H "Authorization: Bearer $FOLIO_KEY" \
  -H "x-filename: contract.pdf" \
  --data-binary @contract.pdf
{
  "markdown": "Master services agreement…\n\n# Schedule 4: Termination…",
  "format": "pdf",
  "durationMs": 936.6,
  "pages": { "total": 3, "text": 2, "ocr": 1, "ocrPages": [2] },
  "usage": { "documents": 1, "ocrPages": 1, "used": 119, "included": 10000 }
}

Two pages were read from their own text and cost nothing. Page 2 was a scan and went to OCR. ocrPages names it, so the charge is checkable against the document rather than something to take on trust.

Node

import { readFile } from 'node:fs/promises';

const response = await fetch('https://quirelabs.com/api/folio/v1/convert', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.FOLIO_KEY}`,
    'x-filename': 'contract.pdf',
  },
  body: await readFile('contract.pdf'),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.code}: ${error.message}`);
}

const { markdown, pages } = await response.json();

Python

import os, requests

with open("contract.pdf", "rb") as document:
    response = requests.post(
        "https://quirelabs.com/api/folio/v1/convert",
        headers={
            "Authorization": f"Bearer {os.environ['FOLIO_KEY']}",
            "x-filename": "contract.pdf",
        },
        data=document,
    )

response.raise_for_status()
markdown = response.json()["markdown"]

Try it without a key first

The playground runs the same conversion in your browser. Nothing is uploaded, and it will tell you how many pages of your own document would need OCR before you decide anything.