I kept rebuilding the same PDF plumbing — so I turned it into one API

작성자

카테고리:

← 피드로
DEV Community · Inkspan.dev · 2026-07-22 개발(SW)
Cover image for I kept rebuilding the same PDF plumbing — so I turned it into one API

Inkspan.dev

Every app eventually needs documents. Invoices. Reports. Exports. Parsing whatever a user uploads. And every single time, you end up doing the same unglamorous work: wiring up headless Chrome or wkhtmltopdf, fighting font installs, babysitting it under load, and then writing a brittle parser to get data back out of PDFs.

I did this three times across different projects before admitting it should just be infrastructure. So I built Inkspan — one API for the whole document lifecycle: generate, manipulate, and extract.

The three things it does

Generate — HTML, a template + JSON data, or Markdown → pixel-faithful PDF.
Manipulate — merge, split, compress, watermark, rasterize to images, fill AcroForm fields.
Extract — pull text and tables into structured JSON.

Authenticate with a key, POST to an endpoint, get a document (or JSON) back. No rendering fleet to run.

Generating a PDF from a template

The endpoint I use most is generate/template — pass an HTML template with {{ }} placeholders and a JSON data object:

import requests

res = requests.post(
    "https://api.inkspan.dev/v1/generate/template",
    headers={"X-API-Key": "ink_live_..."},
    json={
        "template": "<h1>Invoice {{ number }}</h1>"
                    "{% for item in items %}<p>{{ item.desc }}: ${{ item.amt }}</p>{% endfor %}"
                    "<h3>Total: ${{ total }}</h3>",
        "data": {
            "number": "INV-1042",
            "items": [{"desc": "API Pro Plan", "amt": 49}, {"desc": "Overage", "amt": 12}],
            "total": 61,
        },
    },
)
open("invoice.pdf", "wb").write(res.content)

Enter fullscreen mode Exit fullscreen mode

That’s the whole thing. No Chromium in your container, no libpango in your Dockerfile.

The part I actually care about: honest extraction

Parsing PDFs is famously unreliable, and my real gripe with existing tools is that they return confident-looking garbage on a document they couldn’t actually read. So Inkspan’s extraction endpoints return a confidence score and a review_recommended flag:

res = requests.post(
    "https://api.inkspan.dev/v1/extract/tables",
    headers={"X-API-Key": "ink_live_..."},
    files={"file": open("report.pdf", "rb")},
)
print(res.json())
# {
#   "table_count": 1,
#   "tables": [{"page": 1, "rows": [["Item","Qty","Price"], ["Widget","5","$10"]]}],
#   "confidence": 0.95,
#   "review_recommended": false
# }

Enter fullscreen mode Exit fullscreen mode

Born-digital PDFs (anything generated by software — most invoices, statements, exports) extract cleanly and score high. When confidence is low, you get told to review it rather than silently shipping wrong data into your database. That single design choice has saved me more headaches than any accuracy benchmark.

How it’s built

Nothing exotic, deliberately:

  • FastAPI on Railway for the API.
  • WeasyPrint for HTML→PDF, PyMuPDF + pypdf for manipulation and extraction.
  • Supabase for API keys and usage metering.
  • Usage-based billing so it scales with what you actually use.

The value isn’t a novel algorithm — it’s that you don’t have to run and maintain any of this yourself.

Try it

There’s a free tier (100 documents/month), and signup is self-serve — you get a key on the spot at inkspan.dev. It’s also on RapidAPI if that’s your preferred billing.

I’d genuinely like feedback on the API design — especially the extraction confidence model, and which document operations you’d want next. What’s the PDF task you’re tired of rebuilding?

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다