Ink & Order for Developers
Ink & Order for Developers

API documentation

Base URL: https://api.inkandordertools.com. Interactive OpenAPI reference: /docs.

1. Authentication

Every API call needs your key in the Authorization header:

Authorization: Bearer io_live_xxxxxxxxxxxxxxxxxxxxxxxx

Get a free key on the developer home page or with POST /v1/signup:

curl -X POST https://api.inkandordertools.com/v1/signup \
     -H "Content-Type: application/json" -d '{"email":"you@company.com"}'

The key is returned once (HTTP 201). We store only a SHA-256 hash of it. One key per email; a few signups per network per day. The samples below read it from the INKORDER_API_KEY environment variable.

2. POST /v1/convert

Send a multipart/form-data request with the file in the field file. The response body is the PDF (Content-Type: application/pdf). Headers X-Quota-Used and X-Quota-Limit show your monthly count.

EngineExtensions
Headless Chromium.html .htm
LibreOffice.docx .doc .odt .rtf .txt .md .xlsx .xls .ods .csv .pptx .ppt .odp .epub

The type is chosen from the file extension. Max upload 25 MB.

3. GET /v1/usage

Returns your plan, quota, usage this calendar month (UTC), and this month's conversion events (metadata only).

{
  "key_prefix": "io_live_Abc1", "plan": "free", "quota": 50, "used": 2, "remaining": 48,
  "period_start": "2026-09-01T00:00:00.000+00:00",
  "events": [{"ts": "...", "input_type": "docx", "bytes_in": 36909, "bytes_out": 25610,
              "duration_ms": 640, "success": 1, "error": null}]
}

4. Errors

StatusMeaningWhat to do
401Missing or invalid API keyCheck the Authorization: Bearer header.
413File larger than 25 MBSplit or compress the input.
415Unsupported file typeUse one of the extensions above.
429Monthly quota used upUpgrade your plan or wait for the 1st of the month (UTC).
502Conversion failed (e.g. corrupt file)Check the file opens locally; retry once. Failed conversions don't count.

Error bodies are JSON: {"detail": "..."}.

5. Quotas, plans & billing

PlanFiles / monthPrice
Free50$0
Starter1,000$29/mo
Pro10,000$99/mo

Only successful conversions count. Quotas reset on the 1st of each month (UTC). Rejected (401/415/429) and failed requests are free. Paid plans are monthly subscriptions billed through Stripe; cancel any time and your key returns to the Free plan.

The developer API is separate from the Ink & Order PDF app, which stays pay once and runs on your device.

6. Data & deletion

7. Code samples

Each sample reads INKORDER_API_KEY (and optionally INKORDER_API_URL) from the environment.

curl

#!/usr/bin/env bash
# Usage: INKORDER_API_KEY=io_live_... ./curl_quickstart.sh report.docx
set -euo pipefail
INKORDER_API_URL="${INKORDER_API_URL:-https://api.inkandordertools.com}"
IN="$1"; OUT="${IN%.*}.pdf"

# Convert: send the file as multipart field "file", get a PDF back.
curl -sS --fail-with-body \
  -H "Authorization: Bearer $INKORDER_API_KEY" \
  -F "file=@$IN" \
  "$INKORDER_API_URL/v1/convert" -o "$OUT"
echo "saved $OUT"

# Check this month's usage (JSON: plan, quota, used, remaining, events).
curl -sS -H "Authorization: Bearer $INKORDER_API_KEY" "$INKORDER_API_URL/v1/usage"
echo

Python (requests)

"""Usage: INKORDER_API_KEY=io_live_... python python_requests.py report.docx"""
import os
import sys

import requests

BASE = os.environ.get("INKORDER_API_URL", "https://api.inkandordertools.com")
HEADERS = {"Authorization": f"Bearer {os.environ['INKORDER_API_KEY']}"}


def convert(path: str) -> str:
    with open(path, "rb") as f:
        r = requests.post(f"{BASE}/v1/convert", headers=HEADERS,
                          files={"file": (os.path.basename(path), f)}, timeout=120)
    if r.status_code == 429:
        raise SystemExit("Monthly quota used up - upgrade your plan.")
    r.raise_for_status()  # 401 bad key, 415 unsupported type, 502 conversion failed
    out = os.path.splitext(path)[0] + ".pdf"
    with open(out, "wb") as f:
        f.write(r.content)
    return out


if __name__ == "__main__":
    print("saved", convert(sys.argv[1]))
    u = requests.get(f"{BASE}/v1/usage", headers=HEADERS, timeout=30).json()
    print(f"used {u['used']}/{u['quota']} on {u['plan']}")

Node.js (built-in fetch, Node 18+)

// Usage: INKORDER_API_KEY=io_live_... node node_fetch.mjs page.html   (Node 18+; no dependencies)
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";

const BASE = process.env.INKORDER_API_URL ?? "https://api.inkandordertools.com";
const headers = { Authorization: `Bearer ${process.env.INKORDER_API_KEY}` };

async function convert(file) {
  const form = new FormData();
  form.append("file", new Blob([await readFile(file)]), path.basename(file));
  const res = await fetch(`${BASE}/v1/convert`, { method: "POST", headers, body: form });
  if (!res.ok) throw new Error(`Ink & Order API ${res.status}: ${await res.text()}`);
  const out = file.replace(/\.[^.]+$/, "") + ".pdf";
  await writeFile(out, Buffer.from(await res.arrayBuffer()));
  return out;
}

console.log("saved", await convert(process.argv[2]));
const u = await (await fetch(`${BASE}/v1/usage`, { headers })).json();
console.log(`used ${u.used}/${u.quota} on ${u.plan}`);

8. Guides