All posts
Tutorial6 min read

Migrating from PDFShift to HTML2DocHub in 30 Minutes

Step-by-step guide to replacing your PDFShift integration with HTML2DocHub. Side-by-side code in curl, Python, and Node — plus response format, error handling, and a real cost comparison.

If you already have a working PDFShift integration, you're 90% of the way to an HTML2DocHub integration — the API shape is deliberately close. This guide walks through the handful of things that actually differ, in order. Budget 30 minutes start to finish.

Why migrate? Two reasons we hear most often:

  • Pricing. PDFShift charges from $9/month regardless of usage. HTML2DocHub is pure pay-per-use — ₹0.08/page with no monthly minimum. Our pricing page has the breakdown.
  • Transparency. Every job in your dashboard shows the exact amount charged, tied to a wallet ledger line. No bundled credits to decode.

Step 1: Create an account (2 min)

Sign up with your work email. You'll land in the dashboard with a starter credit already in your wallet — that's enough to render around 100 pages before you top up, no card required.

Step 2: Generate an API key (2 min)

Dashboard → API KeysCreate key. Name it something recognisable (production, staging) and copy the token — we show it once, then only the hashed prefix afterwards.

Treat it like any other secret: env var, not source control.

Step 3: Swap the request (20 min)

Here are the three things you have to change in your request code:

  1. The URL: api.pdfshift.io/v3/convert/pdf api.html2dochub.com/v1/render
  2. The auth: HTTP Basic <api_key>: X-API-Key header
  3. The body: top-level render options → nested under an options key; sourcehtml or url

curl

Before (PDFShift):

curl -X POST https://api.pdfshift.io/v3/convert/pdf \
  -u api:YOUR_PDFSHIFT_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "source": "https://example.com/invoice/42",
    "landscape": false,
    "format": "A4",
    "margin": "18mm"
  }' \
  -o invoice.pdf

After (HTML2DocHub):

curl -X POST https://api.html2dochub.com/v1/render \
  -H "X-API-Key: YOUR_HTML2DOCHUB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "pdf",
    "url": "https://example.com/invoice/42",
    "options": {
      "landscape": false,
      "format": "A4",
      "margin": "18mm"
    }
  }'

The response is JSON with a download_url. Fetch it to get the PDF bytes:

curl -L -o invoice.pdf "$(echo "$RESPONSE" | jq -r .download_url)"

Python (requests)

Before:

import requests

resp = requests.post(
    "https://api.pdfshift.io/v3/convert/pdf",
    auth=("api", PDFSHIFT_KEY),
    json={"source": html, "format": "A4", "margin": "18mm"},
)
resp.raise_for_status()
with open("out.pdf", "wb") as f:
    f.write(resp.content)

After:

import requests

resp = requests.post(
    "https://api.html2dochub.com/v1/render",
    headers={"X-API-Key": HTML2DOCHUB_KEY},
    json={
        "type": "pdf",
        "html": html,
        "options": {"format": "A4", "margin": "18mm"},
    },
)
resp.raise_for_status()
job = resp.json()

pdf = requests.get(job["download_url"]).content
with open("out.pdf", "wb") as f:
    f.write(pdf)

Node (fetch)

Before:

const res = await fetch("https://api.pdfshift.io/v3/convert/pdf", {
  method: "POST",
  headers: {
    Authorization: "Basic " + Buffer.from("api:" + PDFSHIFT_KEY).toString("base64"),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ source: html, format: "A4", margin: "18mm" }),
});
const pdfBuffer = Buffer.from(await res.arrayBuffer());
await fs.writeFile("out.pdf", pdfBuffer);

After:

const res = await fetch("https://api.html2dochub.com/v1/render", {
  method: "POST",
  headers: {
    "X-API-Key": HTML2DOCHUB_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    type: "pdf",
    html,
    options: { format: "A4", margin: "18mm" },
  }),
});
const job = await res.json();

const pdfRes = await fetch(job.download_url);
const pdfBuffer = Buffer.from(await pdfRes.arrayBuffer());
await fs.writeFile("out.pdf", pdfBuffer);

Step 4: Handle the response shape difference (3 min)

PDFShift streams the PDF bytes in the response body. HTML2DocHub returns JSON with a signed download_url that you fetch separately.

Why the difference? PDFs get large, and streaming a 5MB response straight through a REST call isn't kind to load balancers or retries. A signed URL also means you can hand the link directly to the end user without proxying the file through your server.

The response JSON has everything you need:

{
  "id": "9f3c…",
  "status": "completed",
  "output_pages": 2,
  "final_cost": 0.16,
  "download_url": "https://files.html2dochub.com/...",
  "created_at": "2026-04-22T10:14:33Z"
}

The download URL is valid for a short window (typically an hour) — if you need longer, store the PDF in your own storage after fetching it.

Step 5: Async jobs + webhooks (optional)

If you render large reports (50+ pages) you probably don't want to block on the request. Add "mode": "async" and a webhook_url:

{
  "type": "pdf",
  "mode": "async",
  "url": "https://example.com/report/2026-Q1",
  "webhook_url": "https://yours.com/pdf-ready",
  "tag": "q1-report"
}

The request returns immediately with status: "pending". When rendering finishes, we POST to your webhook with the same job object plus the download_url. See the webhooks section of the docs for retry and signature details.

Step 6: Verify, then ship (3 min)

Before you swap traffic over, run both APIs side by side on a handful of known documents:

  • Check page count matches
  • Eyeball page 1 of each output
  • Confirm your job ledger shows the expected charges at dashboard → Jobs

Once it looks good, flip the feature flag. The old PDFShift code can stay as a fallback for a week if you want.

Cost comparison on real traffic

A concrete example, using PDFShift's published 2026 pricing and HTML2DocHub's flat ₹0.08/page:

  • 10,000 PDFs/month × 2 pages average = 20,000 pages
  • HTML2DocHub: ₹1,600/month (~$19 USD)
  • PDFShift Business tier: $49/month for 15,000 PDFs
  • Savings: ~60%, and you can scale linearly instead of jumping tiers

The larger your volume, the bigger the gap — because PDFShift's tiers step up, HTML2DocHub's per-page rate stays flat.

Gotchas we've seen

  • Don't forget to top up your wallet before cutover. Sync render requests return 402 when your balance is insufficient — set a low-balance threshold on your account so you get emailed before it hits zero.
  • The source field is renamed. Pass HTML under html or a URL under url. Passing both is rejected.
  • Render options nest under options. PDFShift accepts format, margin, etc. at the top level. We keep them in a single object so the request shape stays consistent with our other renderers (image, PDF, URL).

Need help?

Hit a wall during migration? Check the API docs, or email hello@html2dochub.com. Real responses from a real developer, usually same day.

Try HTML2DocHub free

New accounts get free starter credit — enough to render around 100 pages before you top up. No subscription, no card on file.

Create account