§ ONLINE API HTML → PDF · REST · ANY LANGUAGE

HTML to PDF REST API. The one you forget about.

Two parameters. One endpoint. A perfect PDF. SelectPdf converts any URL or raw HTML to PDF over REST — full HTML5/CSS3/JavaScript, custom headers, automatic bookmarks, no installation. From C# or any .NET app, install the official client with dotnet add package SelectPdf.Api, or POST to the endpoint with HttpClient; the clients for Java, PHP, Python, Node.js, Ruby and Perl work the same way. Already on the older endpoint? See the v1 legacy reference →


§QUICKSTART

One request, one PDF.

Send your API key and the HTML (or a url) to the endpoint; the response body is the PDF. The request needs an API key — get a free one, 200 conversions with no credit card.

Content-Type: application/x-www-form-urlencoded
curl https://selectpdf.com/api2/convert/ \
  -d "key=YOUR_KEY" \
  --data-urlencode "html=<h1>Hello World</h1>" \
  -o document.pdf

Get a free API key · API pricing · Parameter reference · The full REST request


§CLIENT LIBRARIES

HTML to PDF API in seven languages.

Open-source client libraries on GitHub. Same API surface across runtimes — learn once, deploy anywhere. Version 1.6.0 adds tagged PDF and PDF standards, ZUGFeRD / Factur-X electronic invoices and a keyless demo mode.

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.

VERSION1.6.0
LICENSEMIT
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.

VERSION1.6.0
LICENSEMIT
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.

VERSION1.6.0
LICENSEMIT
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.

VERSION1.6.0
LICENSEMIT
§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"
}
Or from C# with HttpClient, no client library
Convert.cs
using System.Net.Http.Json;

using var http = new HttpClient();
var response = await http.PostAsJsonAsync(
    "https://selectpdf.com/api2/convert/",
    new { key = "YOUR_API_KEY", url = "https://example.com" });
response.EnsureSuccessStatusCode();

byte[] pdf = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("example.pdf", pdf);
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 98-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

Six 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 →
POST /api2/invoice/ Convert an HTML invoice into a ZUGFeRD / Factur-X hybrid electronic invoice — one PDF/A-3 file with the invoice XML embedded. POST multipart only, because the XML is uploaded as a file. 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. More details →

The same key reaches two more REST APIs on the same account and the same monthly allowance: the PDF Merge API (/api2/pdfmerge/) combines uploaded files and URLs into a single PDF, and the PDF to Text API (/api2/pdftotext/) extracts text from a PDF or searches it. Both meter by page count — every 50 pages is one conversion.

§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.

§ACCESSIBILITY & STANDARDS

Accessible and archivable PDF.

Set tagged=true and the API emits a tagged PDF: a logical structure tree covering headings, paragraphs, lists, tables, figures with alternate text, links and reading order — what a screen reader needs to make sense of the document, and what accessibility procurement asks for by name.

pdf_standard targets a conformance level: PDF/A for long-term archiving, PDF/X for graphics exchange, PDF/SiqQ for documents that will be digitally signed. PDF/A-3a is the accessible archival level, and produces a tagged document on its own.

Tagged output needs the Blink or Chromium engine. If you don’t specify one, the API picks Chromium for you and tells you so in the X-SelectPdf-Engine response header.

Content-Type: application/x-www-form-urlencoded
curl https://selectpdf.com/api2/convert/ \
  -d "key=YOUR_KEY" \
  -d "url=https://example.com" \
  -d "tagged=true" \
  -d "pdf_standard=pdfa3a" \
  -d "doc_language=en-US" \
  -o accessible.pdf

See the accessibility & standards parameters for the full list of conformance targets.

§ELECTRONIC INVOICING

Electronic invoicing, in one call.

POST /api2/invoice/ turns an HTML invoice into a ZUGFeRD / Factur-X hybrid electronic invoice. You get one PDF/A-3 file: the human-readable invoice your customer opens, with the machine-readable XML embedded inside it for their accounting system.

Six profiles are supported — MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED and XRECHNUNG, the German public-sector profile. The API picks the correct attachment relationship for the profile you choose and names the embedded file the way the standard requires, so you don’t have to read the specification to get it right.

The invoice is produced as PDF/A-3a by default — the accessible level, so the visible invoice is readable by assistive technology as well as archivable.

Content-Type: multipart/form-data
curl https://selectpdf.com/api2/invoice/ \
  -F "key=YOUR_KEY" \
  -F "url=https://your-app.example/invoices/INV-2026-001" \
  -F "zugferd_profile=en16931" \
  -F "zugferd_xml=@factur-x.xml" \
  -o invoice.pdf

Every conversion parameter applies here too — page size, margins, headers, footers, engine. The invoice endpoint adds zugferd_xml, zugferd_profile, zugferd_relationship and zugferd_schema.

§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. For one-off conversions in the browser, use the free online HTML to PDF converter, which has no watermark.

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 limit exceeded: all of your plan's concurrent calls are in use. Wait a moment and retry; the response body names the limit. On the demo endpoint, also the per-IP or daily limit, with a Retry-After header giving the back-off in seconds.
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-EngineRendering engine actually used. Returned only when the API changed it for you — a tagged or pdf_standard=pdfa3a request that did not name an engine is promoted off the default to Chromium. More details →
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.

Nine quick answers about the REST API. For the full surface, see the 98-parameter reference.

Yes. The SelectPdf HTML to PDF API converts a URL or raw HTML to PDF at https://selectpdf.com/api2/convert/ and returns the PDF in the response body. From C#, use the official open-source .NET client (dotnet add package SelectPdf.Api, MIT license) or call the endpoint with HttpClient. A free 7-day trial includes 200 conversions, with no credit card.
Both use the same SelectPdf rendering engine and produce identical PDFs. The .NET library is self-hosted, on Windows, Linux and macOS with SelectPdf.Universal, 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. Prefer an embedded library on your own servers? SelectPdf.Universal runs the .NET library natively on Windows, Linux and macOS.
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 (wait a moment and retry; on the demo endpoint 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.