/ API · v1

API reference.

Every endpoint currently mounted on login.21pdf.com/v1. Bearer auth over HTTPS.

v1 current REST · JSON Bearer auth

POST /v1/convert

Submit an HTML→PDF conversion

Async by default. Returns a job_id immediately; poll /v1/jobs/:id to track status, then /v1/jobs/:id/download for the PDF.

htmlstring
Full HTML document. One of html, url, or simple_html is required.
urlstring
Public URL to fetch. SSRF-checked at the HTTP boundary and again inside the Chromium request interceptor.
simple_htmlstring
HTML fragment — we wrap it in a minimal document.
options.page_sizestring
A3, A4, A5, Letter, Legal, Tabloid.
options.orientationstring
portrait or landscape.
options.margin_*number (mm)
margin_top, margin_bottom, margin_left, margin_right.
options.wait_for_network_idleboolean
Hold render until the network settles.
Live endpoint login.21pdf.com/v1
Request
POST /v1/convert HTTP/1.1
Host: login.21pdf.com
Authorization: Bearer qpdf_live_...
Content-Type: application/json

{
  "html": "<h1>Invoice #2041</h1><p>Total ₹73,805.82</p>",
  "options": {
    "page_size": "A4",
    "orientation": "portrait",
    "margin_top": 20, "margin_bottom": 20,
    "margin_left": 15, "margin_right": 15,
    "wait_for_network_idle": true
  }
}
Response · 202 Accepted
HTTP/1.1 202 Accepted
X-Quota-Used: 412
X-Quota-Limit: 10000

{
  "job_id": "job_01HW...",
  "status": "queued",
  "message": "job accepted"
}

GET /v1/jobs/:id

Fetch job status

Returns the job's current state. Statuses: queued, processing, succeeded, failed.

{
  "id": "job_01HW...",
  "status": "succeeded",
  "pages": 1,
  "created_at": "2026-04-24T09:21:04Z",
  "completed_at": "2026-04-24T09:21:06Z"
}

GET /v1/jobs/:id/download

Download the PDF

Streams the rendered PDF from MinIO. Only callable after the job reaches succeeded and within the retention window.

Content-Type: application/pdf
Content-Disposition: attachment; filename="job_01HW....pdf"

%PDF-1.7
%âãÏÓ
...

GET /v1/billing/plans

List public plans

Public endpoint. Returns the 4 public tiers (Free / Starter / Pro / Business) with pricing in paisa and monthly quotas.

{
  "plans": [
    { "id": "free",     "name": "Free",     "price_paisa": 0,      "quota_per_cycle": 20 },
    { "id": "starter",  "name": "Starter",  "price_paisa": 49900,  "quota_per_cycle": 1000 },
    { "id": "pro",      "name": "Pro",      "price_paisa": 199900, "quota_per_cycle": 10000 },
    { "id": "business", "name": "Business", "price_paisa": 499900, "quota_per_cycle": 50000 }
  ]
}

GET /v1/billing/subscription

Current subscription

Plan, usage this cycle, and current_period_end. Useful for dashboards and self-rate-limiting clients.

{
  "plan_id": "pro",
  "status": "active",
  "usage_this_cycle": 4120,
  "current_period_end": "2026-05-11T14:00:00Z"
}

POST /v1/billing/subscription

Start a Razorpay subscription

Creates a subscription on Razorpay and returns the hosted-checkout URL. Redirect the user there; activation arrives via webhook.

{
  "checkout_url": "https://rzp.io/i/abc123...",
  "subscription_id": "sub_..."
}

POST /v1/billing/subscription/cancel

Cancel at period end

Marks cancel_at_period_end=true. Access remains active until current_period_end; no refund.

{
  "status": "cancelled",
  "current_period_end": "2026-05-11T14:00:00Z"
}

GET /v1/keys

List API keys

Keys for the calling user. Prefix + last-used timestamp only — full secret is shown once on creation.

{
  "keys": [
    { "prefix": "qpdf_live_a2f9", "label": "bill21-prod", "last_used": "2026-04-23T18:02:11Z" }
  ]
}

POST /v1/keys

Mint a new API key

Returns the plaintext key exactly once. Store it; we can't show it again.

{
  "prefix": "qpdf_live_c4d7",
  "plaintext": "qpdf_live_c4d71b89e2..."
}

End of reference · base URL login.21pdf.com/v1