# HTML to PDF API and MCP server for AI agents > Drop an HTML file and get a PDF. Or let Claude, ChatGPT, Cursor or your code do it with one API call. 100 free pages · 1 credit = 1 page · from $0.005/page · credits never expire · no subscription. ## Works with Claude, ChatGPT, Cursor and any MCP client No code needed. Paste this into your AI agent: ``` Read https://html-pdf.com/llms.txt and set up html-pdf.com so you can create PDFs for me. ``` Then ask: "Make a PDF invoice for Acme, $1,200, due next Friday." MCP server: `https://html-pdf.com/mcp` (Streamable HTTP, `Authorization: Bearer `). Tools: `html_to_pdf`, `url_to_pdf`, `get_credits`, `buy_credits`. Connect with OAuth (log in when asked) or a Bearer key. Setup for each client: https://html-pdf.com/mcp.md ```bash claude mcp add --transport http html-pdf https://html-pdf.com/mcp --header "Authorization: Bearer $HTML_PDF_KEY" ``` ## What agents make with it Invoices and receipts, reports, contracts and proposals, certificates and tickets, resumes and one-pagers, and automations in n8n, Make or Zapier via an HTTP request. ## Convert HTML to PDF with one API call ```bash # Get a key (or at https://html-pdf.com/login) curl -X POST https://html-pdf.com/v1/signup -H "Content-Type: application/json" -d '{"email":"you@example.com"}' # Open the link we email you to confirm (adds the 100 free credits) curl https://html-pdf.com/v1/pdf \ -H "Authorization: Bearer $HTML_PDF_KEY" \ -H "Content-Type: application/json" \ -d '{"html": "

Invoice #042

", "format": "A4"}' \ -o invoice.pdf ``` Paper sizes, margins, headers/footers with page numbers, custom CSS, JavaScript rendering. Reference: https://html-pdf.com/docs.md ## Pixel-perfect PDFs with headless Chrome, zero setup - Real Chrome: modern CSS, flexbox, grid, web fonts, JavaScript. - No Puppeteer, wkhtmltopdf or Chromium to run yourself. - 1 credit = 1 page; failed renders cost zero. - Private: HTML isn't stored; download links expire after 24 hours. - ~0.5 s for a one-page document; 120 requests/minute per account. ## Pay per page. No subscription. Credits never expire. | pack | pages | per page | you save | lasts at 10 pages/day | |---|---|---|---|---| | free | 100 | - | - | ~10 days | | $10 | 1,000 | $0.0100 | - | ~3 months | | $25 | 3,000 | $0.0083 | 17% | ~10 months | | $50 | 7,000 | $0.0071 | 29% | ~2 years | | $100 | 15,000 | $0.0067 | 33% | ~4 years | | $250 | 50,000 | $0.0050 | 50% | ~14 years | ## FAQ - **Can Claude or ChatGPT generate a PDF?** With code execution on, they can draw basic PDFs in Python; for HTML/CSS-designed, pixel-perfect PDFs, connect html-pdf.com as an MCP server. - **Is there a free HTML to PDF API?** Yes: 100 free pages per account, no credit card. - **What is a credit?** One PDF page. Failed conversions are free. Cap cost with `max_pages`. - **Do credits expire?** No. - **n8n / Make / Zapier?** Yes, with an HTTP request to `https://html-pdf.com/v1/pdf`. - **URL to PDF?** Send the page's HTML; it can load public images, fonts and CSS. - **Do you store documents?** No. Download links are deleted after 24 hours. --- # API reference Base URL `https://html-pdf.com` · send your HTML, get a PDF back · 1 credit = 1 PDF page. ## Authentication Send your key in the `Authorization` header (or `X-API-Key`): ``` Authorization: Bearer hp_live_... ``` Get a key by logging in at https://html-pdf.com/login, or with one call (ideal for agents): ```bash curl -X POST https://html-pdf.com/v1/signup \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' ``` ```json { "api_key": "hp_live_...", "credits": 0, "pending_credits": 100, "unit": "1 credit = 1 PDF page" } ``` The key is shown once. The email owner receives a link that confirms the email and adds the 100 free credits, then shows usage, keys and buying. Until the email is confirmed, conversions return `403 email_not_confirmed`. If the email already has an account you get `409 account_exists` and a login link is emailed instead. ## POST /v1/pdf Render HTML to PDF. ```bash curl https://html-pdf.com/v1/pdf \ -H "Authorization: Bearer $HTML_PDF_KEY" \ -H "Content-Type: application/json" \ -d '{ "html": "

Invoice #042

Total: $10

", "format": "A4", "margin": "15mm", "footer_html": "
/
" }' \ -o invoice.pdf ``` ### Options | field | type | default | description | |---|---|---|---| | `html` | string | `html` or `url` | Full document or fragment. Fragments are wrapped in ``. | | `url` | string | `html` or `url` | Public `http(s)` page to render instead of `html`, e.g. `"https://example.com/invoice/42"`. Private/internal addresses are rejected. Can't be combined with `css`. | | `css` | string | | Extra CSS injected into ``. | | `format` | string | `A4` | `A3`, `A4`, `A5`, `A6`, `Letter`, `Legal`, `Tabloid`. | | `width`, `height` | string | | Custom size, e.g. `"210mm"`, `"8.5in"`, `"600px"`. Overrides `format`. | | `landscape` | bool | `false` | | | `margin` | string or object | ~1 cm | `"10mm"` or `{"top":"1in","right":"0.5in","bottom":"1in","left":"0.5in"}`. | | `print_background` | bool | `true` | Print background colors and images. | | `prefer_css_page_size` | bool | `false` | Use the size from CSS `@page { size: ... }`. | | `scale` | number | `1` | 0.1 to 2. | | `page_ranges` | string | all | e.g. `"1-3, 5"`. | | `header_html`, `footer_html` | string | | Repeated on every page. Placeholders: ``, `totalPages`, `date`, `title`. Needs enough top/bottom `margin`. Default font size is tiny; set `font-size` inline. | | `media` | string | `print` | `print` or `screen` CSS media. | | `wait_delay` | number | `0` | Seconds (0 to 10) to wait before rendering, for JS-built pages. | | `wait_for_expression` | string | | JS expression; render when it returns true, e.g. `"window.ready === true"`. | | `max_pages` | int | | Fail (no charge) if the PDF would have more pages. | | `filename` | string | `document.pdf` | Used in `Content-Disposition`. | | `response` | string | `binary` | `binary` (PDF body), `base64` (JSON), or `url` (JSON with a link valid 24h). | Unknown fields are rejected with `400 invalid_json` so typos don't fail silently. ### Response `200 application/pdf` with headers: | header | | |---|---| | `X-Pages` | pages in the PDF | | `X-Credits-Used` | credits charged (= pages) | | `X-Credits-Remaining` | balance after this request | | `X-Conversion-Id` | e.g. `cv_...` | With `"response": "base64"`: ```json { "id": "cv_...", "pages": 1, "credits_used": 1, "credits_remaining": 99, "filename": "document.pdf", "pdf_base64": "JVBERi0..." } ``` With `"response": "url"`: ```json { "id": "cv_...", "pages": 1, "credits_used": 1, "credits_remaining": 99, "url": "https://html-pdf.com/f/...", "expires_at": "2026-09-30T12:00:00Z" } ``` ### URL to PDF ```bash curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \ -d '{"url": "https://example.com", "media": "screen", "format": "Letter"}' -o page.pdf ``` The page is opened in headless Chrome and printed. Use `"media": "screen"` to keep the on-screen look, and `wait_delay` for pages that render with JavaScript. Pages behind a login won't work: fetch their HTML yourself and send it as `html`. ### Images, fonts and assets Reference remote assets by absolute `https://` URL, or inline them as `data:` URIs. To upload local assets, send `multipart/form-data` with `index.html` plus the files, and an optional `options` field containing the JSON options: ```bash curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \ -F "index.html=@index.html" -F "logo.png=@logo.png" -F "style.css=@style.css" \ -F 'options={"format":"Letter"}' -o out.pdf ``` In `index.html`, reference them by name: ``. ### Page breaks Use CSS: `break-before: page`, `break-inside: avoid`, `@page { size: A4; margin: 20mm }` (with `prefer_css_page_size: true`). ## GET /v1/credits ```json { "balance": 99, "used_total": 1, "email": "you@example.com", "unit": "1 credit = 1 PDF page", "top_up_url": "https://html-pdf.com/dashboard", "auto_topup": { "enabled": false, "threshold": 100, "pack": { "usd": 10, "credits": 1000 }, "card": null, "settings_url": "https://html-pdf.com/dashboard#autotopup" } } ``` `auto_topup.card` is the saved card's label (e.g. `"visa •••• 4242"`), or `null` before the first purchase. ## GET /v1/conversions Last 100 conversions: id, status, pages, bytes, duration_ms, source (`api`, `mcp`, `web`), created_at. Document content is never stored. ## POST /v1/checkout Get a Stripe payment link for a credit pack, e.g. for an agent to send to its human. Credits are added as soon as the payment succeeds. ```bash curl -X POST https://html-pdf.com/v1/checkout -H "Authorization: Bearer $HTML_PDF_KEY" -d '{"usd": 10}' ``` ```json { "checkout_url": "https://checkout.stripe.com/...", "usd": 10, "credits": 1000 } ``` Packs (`usd` → credits): 10 → 1,000 · 25 → 3,000 · 50 → 7,000 · 100 → 15,000 · 250 → 50,000. Running low? The account owner gets an email below 20 credits and at 0, and can enable **auto top-up** in the dashboard to refill automatically from a saved card. ## POST /v1/autotopup Turn auto top-up on or off so an automation never runs out: when the balance drops below `threshold`, the card saved at the first purchase is charged for the `usd` pack (at most once every 10 minutes; it turns itself off if a payment fails). Send any of the three fields; the others keep their value. `threshold` can be at most the pack size, so one top-up always lifts the balance above it. ```bash curl -X POST https://html-pdf.com/v1/autotopup -H "Authorization: Bearer $HTML_PDF_KEY" \ -d '{"enabled": true, "threshold": 200, "usd": 25}' ``` Returns `{"auto_topup": {...}}` as in `GET /v1/credits`. The account owner is emailed about every change. Without a saved card you get `409 no_saved_card` with a `checkout_url`: buy once, then turn it on. Agents should ask the user before turning it on. ## POST /v1/keys Create an additional API key for the account of the key you authenticate with. Revoke keys in the dashboard. ## Errors All errors are JSON: ```json { "error": { "code": "insufficient_credits", "message": "You have no credits left.", "hint": "Buy credits at ...", "top_up_url": "https://html-pdf.com/dashboard", "docs_url": "..." } } ``` | status | code | meaning | |---|---|---| | 400 | `missing_html`, `invalid_url`, `html_and_url`, `invalid_json`, `invalid_*` | Fix the request. | | 400 | `disposable_email` | `POST /v1/signup` with a temporary (temp-mail) address. Use a permanent email. | | 401 | `missing_api_key`, `invalid_api_key` | | | 402 | `insufficient_credits` | No credits left, or the PDF has more pages than your balance (then the error has `pages` and `balance`). Not charged. `checkout_url` is a ready Stripe payment link to give the user, for the pack that fits your recent usage (from $25 for 3,000 pages, see `checkout_pack`); also `top_up_url`, or `POST /v1/checkout` for other packs. | | 403 | `email_not_confirmed` | Free credits are added when the account's email is confirmed. Open the emailed link, then retry. | | 409 | `account_exists` | Signup with an existing email; login link emailed. | | 409 | `no_saved_card` | `POST /v1/autotopup` before any purchase. Pay `checkout_url` once, then retry. | | 413 | `too_large` | Body over 20 MB. | | 422 | `render_failed`, `max_pages_exceeded` | Not charged. | | 429 | `rate_limited` | 120 req/min per account. Retry after `Retry-After`. | | 503 | `busy`, `engine_unavailable` | Not charged. Retry. | | 504 | `render_timeout` | Over 30 s. Not charged. | Failed requests never cost credits. ## Limits - Request body: 20 MB - Render time: 30 s - Rate: 120 requests/minute per account - Download links (`response: "url"`, MCP): 24 hours ## Security note The renderer cannot reach private networks. Treat the HTML you send like code running in a sandboxed browser: it may load public URLs and run JavaScript. --- # MCP server Let your AI agent create PDFs directly. Remote MCP server, Streamable HTTP transport: ``` https://html-pdf.com/mcp ``` Auth: **OAuth 2.1**. Clients like Claude.ai, ChatGPT and Claude Code open an html-pdf.com login when you connect, so there is no key to copy. You can also send the header `Authorization: Bearer hp_live_...`, or use `https://html-pdf.com/mcp?api_key=hp_live_...` in clients that only accept a URL. No key yet? You don't need one. Add the server URL and your client opens an html-pdf.com login. New accounts get 100 free credits. ## Tools | tool | what it does | |---|---| | `html_to_pdf` | Render HTML to PDF. Returns a download URL (valid 24h), page count and credits left. Same options as the [API](/docs): `html`, `filename`, `format`, `landscape`, `margin`, `css`, `header_html`, `footer_html`, `print_background`, `media`, `wait_delay`, `max_pages`. | | `url_to_pdf` | Render a public web page (`url`) to PDF. Same options as `html_to_pdf` except `html`/`css`. | | `get_credits` | Remaining credits, top-up link and auto top-up status. | | `buy_credits` | Create a Stripe payment link for a credit pack (`usd`: 10, 25, 50, 100, 250) to give to the user. | | `set_auto_topup` | Turn auto top-up on or off (`enabled`, `threshold`, `usd`) after the first purchase. The owner is emailed about every change. | ## Claude Code ```bash claude mcp add --transport http html-pdf https://html-pdf.com/mcp \ --header "Authorization: Bearer $HTML_PDF_KEY" ``` ## Claude Desktop / claude.ai Customize → Connectors → **+** → Add custom connector (Team/Enterprise: an owner adds it under Organization settings → Connectors). Paste this URL, click **Connect** and log in with your email. ``` https://html-pdf.com/mcp ``` ## Cursor `~/.cursor/mcp.json`: ```json { "mcpServers": { "html-pdf": { "url": "https://html-pdf.com/mcp", "headers": { "Authorization": "Bearer hp_live_..." } } } } ``` ## VS Code `.vscode/mcp.json`: ```json { "servers": { "html-pdf": { "type": "http", "url": "https://html-pdf.com/mcp", "headers": { "Authorization": "Bearer ${input:html-pdf-key}" } } }, "inputs": [{ "id": "html-pdf-key", "type": "promptString", "description": "html-pdf.com API key", "password": true }] } ``` ## Codex `~/.codex/config.toml`: ```toml [mcp_servers.html-pdf] url = "https://html-pdf.com/mcp" bearer_token_env_var = "HTML_PDF_KEY" ``` ## Other clients Any client supporting remote MCP over Streamable HTTP works. For stdio-only clients use a bridge: ```bash npx -y mcp-remote https://html-pdf.com/mcp --header "Authorization: Bearer $HTML_PDF_KEY" ``` ## Prompt examples - "Turn this invoice data into a clean A4 PDF with page numbers in the footer." - "Generate a PDF of the weekly report as a landscape Letter page." - "How many html-pdf credits do I have left?" --- # Pricing Pay once, use for years. **1 credit = 1 PDF page.** Credits never expire. No subscription. Every new account gets **100 free credits** once you confirm your email. | pack | pages | per page | you save | lasts at 10 pages/day | |---|---|---|---|---| | free | 100 | - | - | ~10 days | | $10 | 1,000 | $0.0100 | - | ~3 months | | $25 | 3,000 | $0.0083 | 17% | ~10 months | | $50 | 7,000 | $0.0071 | 29% | ~2 years | | $100 | 15,000 | $0.0067 | 33% | ~4 years | | $250 | 50,000 | $0.0050 | 50% | ~14 years | Need more than 50,000 pages? Buy several packs, they stack and never expire. ## How credits are counted - A PDF with 3 pages uses 3 credits. - Failed conversions (render errors, timeouts, `max_pages` exceeded) use 0 credits. - A conversion needs enough credits for all its pages. If a document has more pages than your balance, you get a `402` with the page count and your balance, and nothing is charged. With auto top-up on, the document still completes and the top-up covers the difference. - Set `max_pages` on a request to cap what it can cost. ## Never run out: auto top-up After your first purchase, turn on **auto top-up** in the dashboard: choose a threshold (e.g. "below 200 credits") and a pack, and we charge your saved card automatically when the balance drops below it. It runs at most once every 10 minutes, and it switches itself off if a payment fails. Prefer to stay manual? You get an email when you drop below 20 credits and when you run out. ## Compared to monthly plans Monthly plans reset unused conversions every month. Here a $10 pack of 1,000 pages lasts until you use it, whether that's next week or in three years.