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 →
HTML to PDF API in seven languages.
Open-source client libraries on GitHub. Same API surface across runtimes — learn once, deploy anywhere.
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);
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));
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";
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"])
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']);
});
});
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"
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";
Render any web page. Pixel-perfect.
Full HTML5, CSS3 & JavaScript
Same engine as the .NET PDF library — modern web, modern PDFs. WebKit, Blink and Chromium rendering engines available.
Every page-setup knob you need
Page sizes from A0 to Legal, custom dimensions, portrait/landscape, individual margins, custom headers and footers.
PDF metadata & viewer behavior
Title, author, keywords, passwords, viewer page mode, hide toolbar, fit window. The output behaves the way you set it.
Hide elements, scope to a section
CSS selectors to hide unwanted elements, or convert only one element. Render the receipt, not the wrapper page.
Automatic bookmarks from selectors
Generate PDF bookmarks from H1/H2/CSS classes. Long documents get a real navigation tree, generated for free.
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.
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.
https://selectpdf.com/api2/convert/?key=YOUR_KEY&url=https://example.com
https://selectpdf.com/api2/convert/
{
"key": "YOUR_LICENSE_KEY",
"url": "https://example.com"
}
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.
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.
Five HTML to PDF API endpoints. One key.
All endpoints under https://selectpdf.com/api2/. The same license key authenticates every call.
/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 →
/api2/asyncjob/
Poll an async conversion. Returns the PDF once ready, or HTTP 202 with progress while still rendering. More details →
/api2/webelements/
Retrieve PDF coordinates of matched HTML elements, in tandem with pdf_web_elements_selectors. More details →
/api2/usage/
Read your subscription: current plan, monthly limit, used credits and remaining credits. More details →
/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 →
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.
- POST
/api2/convert/withasync=True(and the rest of your parameters). - The server replies
202 Acceptedand returns the job ID in theX-SelectPdf-Job-Idresponse header. - Poll
GET /api2/asyncjob/?key=YOUR_KEY&job_id=…at a reasonable cadence. - Each poll:
202= still processing ·200= PDF returned in the response body ·499= conversion failed (plain-text reason in the body).
https://selectpdf.com/api2/asyncjob/?key=YOUR_KEY&job_id=JOB_ID
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-Elementsheader. Large payloads are split across numberedX-SelectPdf-Web-Elements-Header-Nheaders, withX-SelectPdf-Web-Elements-Headers-Countgiving 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.
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.
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.
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.
Content-Type: application/pdf.X-SelectPdf-Job-Id header; poll /api2/asyncjob/ until the PDF is ready.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.Retry-After header tells you how long to back off before retrying.Retry-After header gives a suggested back-off.v2).pdf_web_elements_selectors.production or demo.worker (every conversion is dispatched to a worker process; the legacy in-process branch was retired).-1 means unlimited.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.
https://selectpdf.com/api2/usage/?key=YOUR_KEY&get_history=True
https://selectpdf.com/api2/usage/
{
"key": "YOUR_LICENSE_KEY",
"get_history": "True"
}
{
"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 }
]
}
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.
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.
HTML to PDF API, answered.
Eight quick answers about the REST API. For the full surface, see the 90-parameter reference.
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.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.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.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.