§ ONLINE API HTML → PDF · REST · ANY LANGUAGE

The HTML to PDF REST API you forget about.

Two parameters. One endpoint. A perfect PDF. SelectPdf converts any URL or raw HTML to PDF — full HTML5/CSS3/JavaScript, custom headers, automatic bookmarks, no installation. Already on the older endpoint? See the v1 legacy reference →


§CLIENT LIBRARIES

HTML to PDF API in seven languages.

Open-source client libraries on GitHub. Same API surface across runtimes — learn once, deploy anywhere.

Convert.cs
using System;
using SelectPdf.Api;

HtmlToPdfClient client = new HtmlToPdfClient(apiKey);
client.setPageSize(PageSize.A4);             // A0–A8, Letter, Legal
client.setPageOrientation(PageOrientation.Portrait);
client.setMargins(0);
client.setRenderingEngine(RenderingEngine.WebKit); // or Chromium
client.setShowPageNumbers(false);
client.setPdfBookmarksSelectors("H1, H2");   // outline from H1/H2

// --- Convert a live URL to a local PDF ---
client.convertUrlToFile(url, localFile);
Console.WriteLine("Pages: " + client.getNumberOfPages());

// --- Convert a raw HTML string to bytes (in-memory) ---
byte[] pdf = client.convertHtmlString("<h1>Hi</h1>");
Console.WriteLine("Credits left: " + client.CreditsRemaining);
.NET (C#) CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

Convert.java
package com.selectpdf;

HtmlToPdfClient client = new HtmlToPdfClient(apiKey);
client.setPageSize(ApiEnums.PageSize.A4);
client.setPageOrientation(ApiEnums.PageOrientation.Portrait);
client.setMargins(0);
client.setRenderingEngine(ApiEnums.RenderingEngine.WebKit);
client.setShowPageNumbers(false);
client.setPdfBookmarksSelectors("H1, H2");   // outline from H1/H2

// --- Convert a live URL to a local PDF ---
client.convertUrlToFile(url, localFile);
System.out.printf("Pages: %d%n", client.getNumberOfPages());

// --- Convert a raw HTML string to bytes (in-memory) ---
byte[] pdf = client.convertHtmlString("<h1>Hi</h1>");
UsageClient u = new UsageClient(apiKey);
System.out.println("Usage: " + u.getUsage(false));
JAVA CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

convert.php
require("SelectPdf.Api.php");

$client = new SelectPdf\Api\HtmlToPdfClient($apiKey);
$client->setPageSize(SelectPdf\Api\PageSize::A4);
$client->setPageOrientation(SelectPdf\Api\PageOrientation::Portrait);
$client->setMargins(0);
$client->setRenderingEngine(SelectPdf\Api\RenderingEngine::WebKit);
$client->setShowPageNumbers(false);
$client->setPdfBookmarksSelectors("H1, H2");  // outline from H1/H2

// --- Convert a live URL to a local PDF ---
$client->convertUrlToFile($url, $localFile);
echo "Pages: " . $client->getNumberOfPages() . "\n";

// --- Convert a raw HTML string to bytes ---
$pdf = $client->convertHtmlString("<h1>Hi</h1>");
$u = new \SelectPdf\Api\UsageClient($apiKey);
echo "Credits: " . $u->getUsage(false)["available"] . "\n";
PHP CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

convert.py
import selectpdf

client = selectpdf.HtmlToPdfClient(apiKey)
client.setPageSize(selectpdf.PageSize.A4)
client.setPageOrientation(selectpdf.PageOrientation.Portrait)
client.setMargins(0)
client.setRenderingEngine(selectpdf.RenderingEngine.WebKit)
client.setShowPageNumbers(False)
client.setPdfBookmarksSelectors("H1, H2")  # outline from H1/H2

# --- Convert a live URL to a local PDF ---
client.convertUrlToFile(url, localFile)
print("Pages:", client.getNumberOfPages())

# --- Convert a raw HTML string to bytes (in-memory) ---
pdf = client.convertHtmlString("<h1>Hi</h1>")
usage = selectpdf.UsageClient(apiKey).getUsage()
print("Credits:", usage["available"])
PYTHON CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

convert.js
var selectpdf = require('selectpdf');

var client = new selectpdf.HtmlToPdfClient(apiKey);
client.setPageSize('A4');
client.setPageOrientation('Portrait');
client.setMargins(0);
client.setRenderingEngine('WebKit');         // or 'Chromium'
client.setShowPageNumbers(false);
client.setPdfBookmarksSelectors('H1, H2');   // outline from H1/H2

// --- Convert a live URL to a local PDF ---
client.convertUrlToFile(url, localFile, function(err, file) {
    if (err) return console.error('Convert error:', err);
    console.log('Pages:', client.getNumberOfPages());

    var usage = new selectpdf.UsageClient(apiKey);
    usage.getUsage(false, function(e, d) {
        if (!e) console.log('Credits:', d['available']);
    });
});
NODE.JS CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

convert.rb
require 'selectpdf'

client = SelectPdf::HtmlToPdfClient.new(api_key)
client.page_size = SelectPdf::PageSize::A4
client.page_orientation = SelectPdf::PageOrientation::PORTRAIT
client.margins = 0
client.rendering_engine = SelectPdf::RenderingEngine::WEBKIT
client.page_numbers = false
client.pdf_bookmarks_selectors = 'H1, H2'  # outline from H1/H2

# --- Convert a live URL to a local PDF ---
client.convert_url_to_file(url, local_file)
print "Pages: #{client.number_of_pages}\n"

# --- Convert a raw HTML string to bytes (in-memory) ---
pdf = client.convert_html_string('<h1>Hi</h1>')
usage = SelectPdf::UsageClient.new(api_key).get_usage(false)
print "Credits: #{usage['available']}\n"
RUBY CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

convert.pl
use SelectPdf;
use JSON;

my $client = SelectPdf::HtmlToPdfClient->new($apiKey);
$client->setPageSize("A4");
$client->setPageOrientation("Portrait");
$client->setMargins(0);
$client->setRenderingEngine("WebKit");      # or "Chromium"
$client->setShowPageNumbers("False");
$client->setPdfBookmarksSelectors("H1, H2");  # outline

# --- Convert a live URL to a local PDF ---
$client->convertUrlToFile($url, $local_file);
print "Pages: " . $client->getNumberOfPages() . "\n";

# --- Convert a raw HTML string to bytes (in-memory) ---
my $pdf = $client->convertHtmlString("<h1>Hi</h1>");
my $u = SelectPdf::UsageClient->new($apiKey);
print "Credits: " . $u->getUsage(0)->{"available"} . "\n";
PERL CLIENT
Three lines, one PDF.

Bypass the raw HTTP wiring. The official client wraps the REST endpoint with named setters, file/stream/byte-array convenience methods, and language-idiomatic error handling.

§WHAT IT DOES

Render any web page. Pixel-perfect.

FIDELITY

Full HTML5, CSS3 & JavaScript

Same engine as the .NET PDF library — modern web, modern PDFs. WebKit, Blink and Chromium rendering engines available.

PAGE SETUP

Every page-setup knob you need

Page sizes from A0 to Legal, custom dimensions, portrait/landscape, individual margins, custom headers and footers.

DOCUMENT

PDF metadata & viewer behavior

Title, author, keywords, passwords, viewer page mode, hide toolbar, fit window. The output behaves the way you set it.

PRECISION

Hide elements, scope to a section

CSS selectors to hide unwanted elements, or convert only one element. Render the receipt, not the wrapper page.

STRUCTURE

Automatic bookmarks from selectors

Generate PDF bookmarks from H1/H2/CSS classes. Long documents get a real navigation tree, generated for free.

ZERO INSTALL

REST means any language, anywhere

GET or POST. JSON or form-encoded. .NET, Java, PHP, Python, Ruby, Node.js, Perl — or hand-roll it with cURL.

§HOW IT WORKS

Convert HTML to PDF — one endpoint, three required things.

Send GET or POST. Pass parameters via querystring (GET) or request body (POST). The body can be application/x-www-form-urlencoded or application/json — your choice.

GET — quick test from the browser
GET https://selectpdf.com/api2/convert/?key=YOUR_KEY&url=https://example.com
POST — what you'll use in production
POST https://selectpdf.com/api2/convert/
Content-Type: application/json
{
  "key": "YOUR_LICENSE_KEY",
  "url": "https://example.com"
}
key REQUIRED

Your API license key. Get one in 30 seconds — free for 7 days.

url URL · OR · HTML

The web page to convert. URL-encode the value.

html URL · OR · HTML

Or pass raw HTML instead. Pair with base_url to resolve relative paths.

Reminders. URL-encode url, html and base_url. Both HTTP and HTTPS work. If a parameter is missing, the documented default is used. See the full reference on the 90-parameter Parameters page.
Invoice PDF generated from a URL by the SelectPdf HTML to PDF REST API
Real PDF — generated by the SelectPdf Online API from a single POST
§ENDPOINTS

Five HTML to PDF API endpoints. One key.

All endpoints under https://selectpdf.com/api2/. The same license key authenticates every call.

GETPOST /api2/convert/ Convert URL or HTML to PDF. The main endpoint — synchronous unless async=True. Both verbs accepted: GET parameters as querystring, POST as JSON or form-urlencoded. More details →
GETPOST /api2/asyncjob/ Poll an async conversion. Returns the PDF once ready, or HTTP 202 with progress while still rendering. More details →
GETPOST /api2/webelements/ Retrieve PDF coordinates of matched HTML elements, in tandem with pdf_web_elements_selectors. More details →
GETPOST /api2/usage/ Read your subscription: current plan, monthly limit, used credits and remaining credits. More details →
GETPOST /api2/convert/demo/ No-key watermarked demo — same engine, with conversion limits and demo-only safeguards. Powers the free online HTML to PDF demo. More details →
§ASYNCHRONOUS CONVERSION

For long jobs that outlast your HTTP connection.

Recommended for long pages, multi-megabyte HTML inputs, or callers that cannot keep an HTTP connection open for the full conversion. Submit once, poll the result when it is ready.

  1. POST /api2/convert/ with async=True (and the rest of your parameters).
  2. The server replies 202 Accepted and returns the job ID in the X-SelectPdf-Job-Id response header.
  3. Poll GET /api2/asyncjob/?key=YOUR_KEY&job_id=… at a reasonable cadence.
  4. Each poll: 202 = still processing · 200 = PDF returned in the response body · 499 = conversion failed (plain-text reason in the body).
GET https://selectpdf.com/api2/asyncjob/?key=YOUR_KEY&job_id=JOB_ID

See async and job_id in the parameters reference.

§WEB ELEMENTS RETRIEVAL

PDF coordinates for HTML elements you select.

When you pass pdf_web_elements_selectors, the API records the final PDF position of every HTML element matching your CSS selectors. Use it to overlay annotations, links or interactive layers on the produced PDF.

  • Synchronous calls — the data is returned in the response: base64-encoded JSON in the X-SelectPdf-Web-Elements header. Large payloads are split across numbered X-SelectPdf-Web-Elements-Header-N headers, with X-SelectPdf-Web-Elements-Headers-Count giving the total chunks.
  • Asynchronous calls — once the PDF is ready, fetch the data from GET /api2/webelements/?key=YOUR_KEY&job_id=… instead of inspecting headers.
GET https://selectpdf.com/api2/webelements/?key=YOUR_KEY&job_id=JOB_ID

The result is a JSON array — one entry per matched element — with the element's HTML id, tag name, CSS class, and PDF rectangle coordinates. See pdf_web_elements_selectors in the parameters reference.

§DEMO ENDPOINT

Try the HTML to PDF API — no key required.

Same rendering engine as the paid endpoint, with conversion limits and a watermark. No signup. Powers the free online HTML to PDF demo on this site.

POST https://selectpdf.com/api2/convert/demo/
BEHAVIOR VS PRODUCTION
What changes on the demo.
  • No API key required — ignored if supplied.
  • Engine forced to Chromium.
  • max_load_time clamped to 15 s, min_load_time clamped to 5 s.
  • Output capped at the first 5 pages; watermark + footer notice appended.
  • Always synchronous — no async support.
  • Silently dropped: auth_username, auth_password, cookies, cookies_string, raw_parameters, pdf_name, async, pdf_web_elements_selectors.
  • Rejected with 400: user_password, owner_password, SSRF-flagged URLs (private IPs, cloud metadata, file:// etc.).
  • Request body capped at 1 MB — 413 Payload Too Large if exceeded.
RATE LIMITS
Demo-only throttling.

Three independent counters protect the free endpoint. Excess requests are rejected with the standard HTTP codes — back off, then retry.

PER-IP · HOUR30 req · 429
DAILY CAP · GLOBAL50,000 / day · 429
CONCURRENCY · GLOBAL4 in-flight · 503

For higher limits and unwatermarked output, grab a free 7-day trial key — 200 conversions, no credit card.

§RESPONSE

HTTP status, plain English.

Standard HTTP semantics. The body of every non-200 contains a plain-text explanation — no error shapes to parse, no enum to memorize.

200
OK
The conversion succeeded. The body is the PDF document with Content-Type: application/pdf.
202
Accepted — asynchronous job
The conversion was accepted asynchronously. The job ID is returned in the X-SelectPdf-Job-Id header; poll /api2/asyncjob/ until the PDF is ready.
400
Bad Request
url or html string not specified, parameter validation failure, or — on the demo endpoint — an SSRF-flagged URL. The response body explains in plain text.
401
Authorization Required
License key not specified or invalid. The response body contains an explanation in plain text.
413
Payload Too Large
Demo endpoint only: the request body exceeds the 1 MB demo limit.
429
Too Many Requests
Concurrency or rate limit exceeded. The Retry-After header tells you how long to back off before retrying.
499
Custom — Conversion Error
Something went wrong during the conversion (page didn't load, timeout, etc.). The body contains an explanation in plain text.
503
Service Unavailable
Demo endpoint only: the global concurrency cap is currently reached. The Retry-After header gives a suggested back-off.
RESPONSE HEADERS
X-SelectPdf-ApiAPI version (currently v2).
X-SelectPdf-PagesNumber of pages in the produced PDF document.
X-SelectPdf-Job-IdUUID of the asynchronous job (returned with HTTP 202).
X-SelectPdf-Web-ElementsBase64-encoded JSON of bounding boxes for elements matched by pdf_web_elements_selectors.
X-SelectPdf-Web-Elements-Header-NNumbered chunks of a large web-elements payload (split across multiple headers).
X-SelectPdf-Web-Elements-Headers-CountTotal number of web-elements chunks in the response.
X-SelectPdf-ModeEndpoint mode: production or demo.
X-SelectPdf-ExecutionServer execution path — always worker (every conversion is dispatched to a worker process; the legacy in-process branch was retired).
X-SelectPdf-Credits-TotalMonthly conversion credit limit on the calling key. -1 means unlimited.
X-SelectPdf-Credits-RemainingCredits remaining for the current month.
X-SelectPdf-Demo-ClampedDemo endpoint only: list of parameters whose values were clamped before conversion.
X-SelectPdf-Demo-DroppedDemo endpoint only: list of parameters discarded before conversion.
§USAGE TRACKING

Read your subscription. Programmatically.

Two ways to know where your quota stands. Every successful conversion already returns X-SelectPdf-Credits-Total and X-SelectPdf-Credits-Remaining response headers — read those for a zero-extra-cost live view of remaining credits. Use the dedicated /api2/usage/ endpoint below when you also need the subscription tier or a month-by-month history.

GET https://selectpdf.com/api2/usage/?key=YOUR_KEY&get_history=True
POST https://selectpdf.com/api2/usage/
application/json
{
  "key": "YOUR_LICENSE_KEY",
  "get_history": "True"
}
response.json
{
  "status": "License key active.",
  "subscription_type": "Entry Level",
  "limit": 2000,
  "used": 340,
  "available": 1660,
  "history": [
    { "year": 2026, "month": 4,
      "conversions": 340, "credits": 340 },
    { "year": 2026, "month": 3,
      "conversions": 1876, "credits": 1923 }
  ]
}
§LIMITS

Concurrency scales with your plan.

Each plan allows a fixed number of simultaneous requests. Excess requests are queued or rejected with a 429 Too Many Requests. One conversion credit covers up to 50 PDF pages — a 1-page and a 49-page PDF cost the same.

Free Trial
200 /mo
1req
Entry
2,000 /mo
2req
Standard
5,000 /mo
4req
Advanced
20,000 /mo
8req
Premium
50,000 /mo
8req
Ultra
100,000 /mo
16req
Dedicated
Unlimited /mo
16req
CREDIT MATH
50 pages = 1 credit

Each 50 pages of generated output counts as one conversion credit. A 12-page PDF, a 1-page PDF and a 49-page PDF all cost the same. See API pricing for plan details.

?FAQ

HTML to PDF API, answered.

Eight quick answers about the REST API. For the full surface, see the 90-parameter reference.

Both use the same SelectPdf rendering engine and produce identical PDFs. The .NET library is self-hosted on Windows (runs in-process inside your .NET app) with a perpetual per-developer license; the Online API is a REST endpoint you call from any language on any OS with metered monthly plans.
No. A 7-day free trial with 200 conversions is available with email signup only, and a no-key watermarked demo runs from this site without any signup at all.
Each 50 pages of generated output counts as one conversion credit. A 1-page, a 12-page and a 49-page PDF all cost one credit each; a 51-page PDF costs two. Monthly plans run from $19 to $449.
Set min_load_time to wait a known minimum before snapshotting the page, and max_load_time as the upper bound — up to 120 seconds. For pages that signal completion from JavaScript, set startup_mode to Manual and call SelectPdf.startConversion() from within the page.
Yes. The Online API is REST — call it from any OS and any language with an HTTP client. Official client libraries are published for .NET, Java, PHP, Python, Node.js, Ruby and Perl, all open source under the MIT license.
The HTML to PDF API exposes four engines via the engine parameter — WebKit (the default), WebKit Restricted, Blink and Chromium. Chromium is the modern CEF-based engine recommended for new integrations and is the engine the free demo endpoint uses internally. Set engine to the value you need; if you do not pass the parameter, the conversion runs on WebKit.
Transient: 429 Too Many Requests (back off and retry — the Retry-After header tells you how long), and 503 Service Unavailable on the demo endpoint when the global concurrency cap is reached. Permanent: 400 Bad Request (the parameters are wrong) and 401 Authorization Required (the license key is missing or invalid). 499 means the conversion itself failed — the body explains why.
Yes. POST with async=True returns 202 Accepted and a job ID in the X-SelectPdf-Job-Id header; poll GET /api2/asyncjob/?key=…&job_id=… until the PDF is returned. Useful when a single conversion can exceed your client's HTTP timeout.