API Reference
Request
Endpoints
The API accepts POST requests at the following two endpoints:
- HTML to PDF:
https://api.polydoc.tech/pdf/convert - HTML to Screenshot:
https://api.polydoc.tech/screenshot/convert
Request Headers
- Authorization
stringrequiredBearer token for authentication. Use your API key from the Dashboard.const axios = require('axios');const fs = require('fs');axios.post('https://api.polydoc.tech/pdf/convert', {source: 'https://example.com'}, {headers: {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json'},responseType: 'arraybuffer'}).then(response => {fs.writeFileSync('document.pdf', response.data);}).catch(error => {console.error(error);});import requestsheaders = {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json'}data = {'source': 'https://example.com'}response = requests.post('https://api.polydoc.tech/pdf/convert', headers=headers, json=data)with open('document.pdf', 'wb') as f:f.write(response.content)require 'net/http'require 'uri'require 'json'uri = URI.parse("https://api.polydoc.tech/pdf/convert")request = Net::HTTP::Post.new(uri)request["Authorization"] = "Bearer <YOUR API KEY>"request["Content-Type"] = "application/json"request.body = JSON.dump({"source" => "https://example.com"})req_options = {use_ssl: uri.scheme == "https",}response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|http.request(request)endFile.open("document.pdf", "wb") do |file|file.write(response.body)endpackage mainimport ("bytes""encoding/json""io""net/http""os")func main() {url := "https://api.polydoc.tech/pdf/convert"data := map[string]interface{}{"source": "https://example.com",}jsonData, _ := json.Marshal(data)req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))req.Header.Set("Authorization", "Bearer <YOUR API KEY>")req.Header.Set("Content-Type", "application/json")client := &http.Client{}resp, _ := client.Do(req)defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)os.WriteFile("document.pdf", body, 0644)}<?php$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.polydoc.tech/pdf/convert');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["source" => "https://example.com"]));$headers = ['Authorization: Bearer <YOUR API KEY>','Content-Type: application/json'];curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);$result = curl_exec($ch);if (curl_errno($ch)) {echo 'Error:' . curl_error($ch);}curl_close ($ch);file_put_contents('document.pdf', $result);?>use reqwest::blocking::Client;use serde_json::json;use std::error::Error;use std::fs::File;use std::io::Write;fn main() -> Result<(), Box<dyn Error>> {let client = Client::new();let res = client.post("https://api.polydoc.tech/pdf/convert").header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").json(&json!({"source": "https://example.com"})).send()?;let mut file = File::create("document.pdf")?;file.write_all(&res.bytes()?)?;Ok(())}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.net.http.HttpRequest.BodyPublishers;import java.nio.file.Files;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.util.Map;import com.fasterxml.jackson.databind.ObjectMapper;public class Main {public static void main(String[] args) {try {HttpClient client = HttpClient.newHttpClient();ObjectMapper objectMapper = new ObjectMapper();Map <String, Object> requestBody = Map.of("source", "https://example.com");HttpRequest request = HttpRequest.newBuilder().uri(new URI("https://api.polydoc.tech/pdf/convert")).header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").POST(BodyPublishers.ofString(objectMapper.writeValueAsString(requestBody))).build();HttpResponse <byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());if (response.statusCode() == 200) {Files.write(Paths.get("document.pdf"), response.body(), StandardOpenOption.CREATE);} else {System.err.println("Error: " + response.statusCode());}} catch (Exception e) {e.printStackTrace();}}}using System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Threading.Tasks;using System.IO;using Newtonsoft.Json;class Main {static async Task Main(string[] args) {using(HttpClient client = new HttpClient()) {var requestBody = new {source = "https://example.com"};var json = JsonConvert.SerializeObject(requestBody);var content = new StringContent(json, Encoding.UTF8, "application/json");client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<YOUR API KEY>");HttpResponseMessage response = await client.PostAsync("https://api.polydoc.tech/pdf/convert", content);if (response.IsSuccessStatusCode) {byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();await File.WriteAllBytesAsync("document.pdf", pdfBytes);} else {Console.WriteLine("Error: " + response.StatusCode);}}}}curl --location \--request POST 'https://api.polydoc.tech/pdf/convert' \--header 'Authorization: Bearer <YOUR API KEY>' \--header 'Content-Type: application/json' \--data-raw '{ "source": "https://example.com" }' \--output document.pdf - Content-Type
stringrequiredMust be set toapplication/jsonfor all requests with a JSON body.const axios = require('axios');const fs = require('fs');axios.post('https://api.polydoc.tech/pdf/convert', {source: 'https://example.com'}, {headers: {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json'},responseType: 'arraybuffer'}).then(response => {fs.writeFileSync('document.pdf', response.data);}).catch(error => {console.error(error);});import requestsheaders = {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json'}data = {'source': 'https://example.com'}response = requests.post('https://api.polydoc.tech/pdf/convert', headers=headers, json=data)with open('document.pdf', 'wb') as f:f.write(response.content)require 'net/http'require 'uri'require 'json'uri = URI.parse("https://api.polydoc.tech/pdf/convert")request = Net::HTTP::Post.new(uri)request["Authorization"] = "Bearer <YOUR API KEY>"request["Content-Type"] = "application/json"request.body = JSON.dump({"source" => "https://example.com"})req_options = {use_ssl: uri.scheme == "https",}response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|http.request(request)endFile.open("document.pdf", "wb") do |file|file.write(response.body)endpackage mainimport ("bytes""encoding/json""io""net/http""os")func main() {url := "https://api.polydoc.tech/pdf/convert"data := map[string]interface{}{"source": "https://example.com",}jsonData, _ := json.Marshal(data)req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))req.Header.Set("Authorization", "Bearer <YOUR API KEY>")req.Header.Set("Content-Type", "application/json")client := &http.Client{}resp, _ := client.Do(req)defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)os.WriteFile("document.pdf", body, 0644)}<?php$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.polydoc.tech/pdf/convert');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["source" => "https://example.com"]));$headers = ['Authorization: Bearer <YOUR API KEY>','Content-Type: application/json'];curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);$result = curl_exec($ch);if (curl_errno($ch)) {echo 'Error:' . curl_error($ch);}curl_close ($ch);file_put_contents('document.pdf', $result);?>use reqwest::blocking::Client;use serde_json::json;use std::error::Error;use std::fs::File;use std::io::Write;fn main() -> Result<(), Box<dyn Error>> {let client = Client::new();let res = client.post("https://api.polydoc.tech/pdf/convert").header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").json(&json!({"source": "https://example.com"})).send()?;let mut file = File::create("document.pdf")?;file.write_all(&res.bytes()?)?;Ok(())}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.net.http.HttpRequest.BodyPublishers;import java.nio.file.Files;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.util.Map;import com.fasterxml.jackson.databind.ObjectMapper;public class Main {public static void main(String[] args) {try {HttpClient client = HttpClient.newHttpClient();ObjectMapper objectMapper = new ObjectMapper();Map <String, Object> requestBody = Map.of("source", "https://example.com");HttpRequest request = HttpRequest.newBuilder().uri(new URI("https://api.polydoc.tech/pdf/convert")).header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").POST(BodyPublishers.ofString(objectMapper.writeValueAsString(requestBody))).build();HttpResponse <byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());if (response.statusCode() == 200) {Files.write(Paths.get("document.pdf"), response.body(), StandardOpenOption.CREATE);} else {System.err.println("Error: " + response.statusCode());}} catch (Exception e) {e.printStackTrace();}}}using System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Threading.Tasks;using System.IO;using Newtonsoft.Json;class Main {static async Task Main(string[] args) {using(HttpClient client = new HttpClient()) {var requestBody = new {source = "https://example.com"};var json = JsonConvert.SerializeObject(requestBody);var content = new StringContent(json, Encoding.UTF8, "application/json");client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<YOUR API KEY>");HttpResponseMessage response = await client.PostAsync("https://api.polydoc.tech/pdf/convert", content);if (response.IsSuccessStatusCode) {byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();await File.WriteAllBytesAsync("document.pdf", pdfBytes);} else {Console.WriteLine("Error: " + response.StatusCode);}}}}curl --location \--request POST 'https://api.polydoc.tech/pdf/convert' \--header 'Authorization: Bearer <YOUR API KEY>' \--header 'Content-Type: application/json' \--data-raw '{ "source": "https://example.com" }' \--output document.pdf - X-Sandbox
stringSet totrueto use the sandbox environment. Sandbox conversions do not count towards your monthly limit but produce output with a watermark. Omit or set tofalsefor production use.const axios = require('axios');const fs = require('fs');axios.post('https://api.polydoc.tech/pdf/convert', {source: 'https://example.com'}, {headers: {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json','X-Sandbox': 'true'},responseType: 'arraybuffer'}).then(response => {fs.writeFileSync('document.pdf', response.data);}).catch(error => {console.error(error);});import requestsheaders = {'Authorization': 'Bearer <YOUR API KEY>','Content-Type': 'application/json','X-Sandbox': 'true'}data = {'source': 'https://example.com'}response = requests.post('https://api.polydoc.tech/pdf/convert', headers=headers, json=data)with open('document.pdf', 'wb') as f:f.write(response.content)require 'net/http'require 'uri'require 'json'uri = URI.parse("https://api.polydoc.tech/pdf/convert")request = Net::HTTP::Post.new(uri)request["Authorization"] = "Bearer <YOUR API KEY>"request["Content-Type"] = "application/json"request["X-Sandbox"] = "true"request.body = JSON.dump({"source" => "https://example.com"})req_options = {use_ssl: uri.scheme == "https",}response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|http.request(request)endFile.open("document.pdf", "wb") do |file|file.write(response.body)endpackage mainimport ("bytes""encoding/json""io""net/http""os")func main() {url := "https://api.polydoc.tech/pdf/convert"data := map[string]interface{}{"source": "https://example.com",}jsonData, _ := json.Marshal(data)req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))req.Header.Set("Authorization", "Bearer <YOUR API KEY>")req.Header.Set("Content-Type", "application/json")req.Header.Set("X-Sandbox", "true")client := &http.Client{}resp, _ := client.Do(req)defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)os.WriteFile("document.pdf", body, 0644)}<?php$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'https://api.polydoc.tech/pdf/convert');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["source" => "https://example.com"]));$headers = ['Authorization: Bearer <YOUR API KEY>','Content-Type: application/json','X-Sandbox: true'];curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);$result = curl_exec($ch);if (curl_errno($ch)) {echo 'Error:' . curl_error($ch);}curl_close ($ch);file_put_contents('document.pdf', $result);?>use reqwest::blocking::Client;use serde_json::json;use std::error::Error;use std::fs::File;use std::io::Write;fn main() -> Result<(), Box<dyn Error>> {let client = Client::new();let res = client.post("https://api.polydoc.tech/pdf/convert").header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").header("X-Sandbox", "true").json(&json!({"source": "https://example.com"})).send()?;let mut file = File::create("document.pdf")?;file.write_all(&res.bytes()?)?;Ok(())}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.net.http.HttpRequest.BodyPublishers;import java.nio.file.Files;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.util.Map;import com.fasterxml.jackson.databind.ObjectMapper;public class Main {public static void main(String[] args) {try {HttpClient client = HttpClient.newHttpClient();ObjectMapper objectMapper = new ObjectMapper();Map <String, Object> requestBody = Map.of("source", "https://example.com");HttpRequest request = HttpRequest.newBuilder().uri(new URI("https://api.polydoc.tech/pdf/convert")).header("Authorization", "Bearer <YOUR API KEY>").header("Content-Type", "application/json").header("X-Sandbox", "true").POST(BodyPublishers.ofString(objectMapper.writeValueAsString(requestBody))).build();HttpResponse <byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());if (response.statusCode() == 200) {Files.write(Paths.get("document.pdf"), response.body(), StandardOpenOption.CREATE);} else {System.err.println("Error: " + response.statusCode());}} catch (Exception e) {e.printStackTrace();}}}using System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Threading.Tasks;using System.IO;using Newtonsoft.Json;class Main {static async Task Main(string[] args) {using(HttpClient client = new HttpClient()) {var requestBody = new {source = "https://example.com"};var json = JsonConvert.SerializeObject(requestBody);var content = new StringContent(json, Encoding.UTF8, "application/json");client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<YOUR API KEY>");client.DefaultRequestHeaders.Add("X-Sandbox", "true");HttpResponseMessage response = await client.PostAsync("https://api.polydoc.tech/pdf/convert", content);if (response.IsSuccessStatusCode) {byte[] pdfBytes = await response.Content.ReadAsByteArrayAsync();await File.WriteAllBytesAsync("document.pdf", pdfBytes);} else {Console.WriteLine("Error: " + response.StatusCode);}}}}curl --location \--request POST 'https://api.polydoc.tech/pdf/convert' \--header 'Authorization: Bearer <YOUR API KEY>' \--header 'Content-Type: application/json' \--header 'X-Sandbox: true' \--data-raw '{ "source": "https://example.com" }' \--output document.pdf
Rate Limit
The API is rate-limited to prevent abuse. The following rate limits apply, depending whether you are using the X-Sandbox header or not.
| Mode | Requests per Minute | Requests per Second |
|---|---|---|
| Production | 500 | 50 |
| Sandbox | 50 | 5 |
See the response section below for more information on how to keep track of your rate limit status in the response headers (X-RateLimit-*), and about the 429 status code that is returned when you exceed the rate limit.
API Parameters
The request body is a JSON object of API parameters. The smallest valid request only sets source; everything else is optional. The public schema matches the PdfOptions / ScreenshotOptions types in polydoc-types (layout may be omitted and is merged with server defaults).
Related guides: Templates, PDF/A, E-Invoice, CSS Paged Media, Cloud storage uploads, Webhook signatures, Integrations.
Main Params
- source
stringrequiredThe source of the conversion. This can be a URL, HTML content, or a template.{"source": "https://www.starwars.com/databank/yoda"}{"source": "<html><h1>Hello World</h1></html>"}{"source": "[template:a1b-c2d]"}More information on templatesTemplates guide - filename
stringThe filename of the output file. If not specified, the filename will be automatically generated.{"source": "https://www.starwars.com/databank/yoda","filename": "yoda.pdf"} - tag
stringOptional short label for logging and tracing (max 30 characters). Not shown inside the PDF.{"source": "https://www.starwars.com/databank/yoda","tag": "project-x"} - templateData
object{"source": "[template:a1b-c2d]","templateData": {"invoice_number": "INV-2026-042","customer_name": "Jane Smith"}} - webhook
objectWhen present, the gateway completes conversion first, then either returns the file on the original response (sync) or 202 JSON while delivering in the background (async). Outbound webhook request body = raw PDF or image bytes; header X-Signature = HMAC-SHA256 of the body. The webhook object is stripped before the converter runs.- url
stringrequiredThe URL of the webhook. - async
booleanWhentrue(default), respond with 202 Accepted and JSON while the file is POSTed to your webhook in the background. Whenfalse, wait for webhook delivery (including retries), then respond with 200 OK and the same PDF or image bytes as the response body. - method
GET|POST|PUT|PATCH|DELETEHTTP method for the webhook request. Default isPOST. - headers
objectExtra headers on the webhook request (e.g. authorization). - timeout
numberWebhook HTTP timeout in milliseconds for the outbound call. - retries
integerHow many times to retry a failed webhook delivery. - retryDelay
numberDelay in milliseconds between webhook retry attempts.
{"source": "https://www.starwars.com/databank/yoda","webhook": {"url": "https://webhook.site/8b6d1c2b-7d9c-4d2b-9f1e-5c3b4e1c6d8d","async": true,"method": "POST","headers": {"Authorization": "Bearer <YOUR WEBHOOK KEY>"}}} - timeout
numberMaximum time for the conversion in milliseconds (Chromium navigation, rendering, and PDF generation). Default30000(30 seconds). Maximum600000(10 minutes). Free plan: capped at30000(30 seconds).{"source": "https://www.starwars.com/databank/yoda","timeout": 120000}
Layout Params
The whole layout object is optional: omitted fields use converter defaults (for example Letter size and standard margins). For CSS Paged Media workflows, see CSS Paged Media and layout.preferCSSPageSize.
- {"source": "https://www.starwars.com/databank/yoda","layout": {"displayHeaderFooter": false}}
- headerTemplate
stringThe header of the PDF page.The header template of the page in HTML format. It supports the following HTML classes to automatically fill in the corresponding values:date: The current date.title: The title of the web page.url: The URL of the web page.pageNumber: The current page number.totalPages: The total number of pages.
{"source": "https://www.starwars.com/databank/yoda","layout": {"footerTemplate": "<div>Some HTML, see other tab</div>"}}<div style='display: flex; justify-content: space-between; color: #ddd'><!-- Left aligned content --><div>Author</div><!-- Center aligned content --><div class='title'></div><!-- Right aligned content --><div><span class='pageNumber'></span>/<span class='totalPages'></span></div></div> - {"source": "https://www.starwars.com/databank/yoda","layout": {"footerTemplate": "<div>Some HTML, see other tab</div>"}}<div style='display: flex; justify-content: space-between; color: #ddd'><!-- Left aligned content --><div>Author</div><!-- Center aligned content --><div class='title'></div><!-- Right aligned content --><div><span class='pageNumber'></span>/<span class='totalPages'></span></div></div>
- format
string | objectThe format of the PDF page. This can be a paper format (accepted values areLetter,Legal,Tabloid,Ledger,A0,A1,A2,A3,A4,A5,A6) or an object of the width and height of the page. The default format isLetter.Paramters for the custom format:- width
string|numberrequiredThe width of the page as number or with units (cm,mm,in,px). - height
string|numberrequiredThe height of the page as number or with units (cm,mm,in,px).
{"source": "https://www.starwars.com/databank/yoda","layout": {"format": "A4"}}{"source": "https://www.starwars.com/databank/yoda","layout": {"format": {"width": "8.5in","height": "11in"}}} - landscape
booleanWhether to print in landscape orientation.{"source": "https://www.starwars.com/databank/yoda","layout": {"landscape": true}} - margin
objectThe margins of the PDF page. Each margin can be specified as number or with units (cm,mm,in,px).- top
string|numberThe top margin of the page. The default value is2cm. - right
string|numberThe right margin of the page. The default value is1cm. - bottom
string|numberThe bottom margin of the page. The default value is2cm. - left
string|numberThe left margin of the page. The default value is1cm.
{"source": "https://www.starwars.com/databank/yoda","layout": {"margin": {"top": "1in","right": "1in","bottom": "1in","left": "1in"}}} - preferCSSPageSize
booleanWhether to prefer the CSS@pagesize declaration over theformatoption. The default value isfalse.{"source": "https://www.starwars.com/databank/yoda","layout": {"preferCSSPageSize": true}}@page {size: 15in 11in;} - omitBackground
booleanHides default white background and allows generating PDFs with transparency. The default value isfalse.{"source": "https://www.starwars.com/databank/yoda","layout": {"omitBackground": true}} - printBackground
booleanWhether to print background graphics. The default value isfalse.{"source": "https://www.starwars.com/databank/yoda","layout": {"printBackground": true}} - pageRanges
stringThe page ranges to print, e.g.1-5, 8, 11-13.{"source": "https://www.starwars.com/databank/yoda","layout": {"pageRanges": "1-5, 8, 11-13"}} - scale
numberScales the rendering of the web page. The value must be between0.1and2.0. The default value is1.0.{"source": "https://www.starwars.com/databank/yoda","layout": {"scale": 1.5}} - outline
booleanWhentrue, the generated PDF includes a document outline (often labeled Bookmarks in desktop PDF apps): a hierarchical table of contents in the viewer's navigation pane, with entries derived from HTML heading elements (h1–h6). Use real heading tags in your document for a meaningful structure.{"source": "<html><body><h1>Title</h1><h2>Section</h2><p>...</p></body></html>","layout": {"format": "A4","outline": true}} - tagged
booleanWhentrue, Chromium emits a tagged PDF: a logical structure tree (tags) for accessibility—better support for screen readers, some viewers, and workflows that rely on PDF/UA-style structure. This is an accessibility feature and is separate from the visual print layout.{"source": "https://www.starwars.com/databank/yoda","layout": {"tagged": true}}
PDF Params
- metadata
objectThe metadata to include in the PDF.- title
stringThe title of the PDF. - subject
stringThe subject of the PDF. - keywords
string[]The keywords of the PDF. - producer
stringThe producer of the PDF. - creator
stringThe creator of the PDF.
{"source": "https://www.starwars.com/databank/yoda","pdf": {"metadata": {"title": "Yoda","author": "Star Wars","subject": "The Jedi Master","keywords": ["jedi", "star wars"],}}} - encryption
objectPassword-based encryption applied after PDF generation (qpdf). Provide at least one ofuserPassword(open password) orownerPassword. Each password must be at least 4 characters. Incompatible withpdf.pdfaand witheInvoice.- userPassword
stringPassword required to open the document (viewer “open” password). - ownerPassword
stringOwner password for full access when permissions are restricted.
{"source": "https://www.starwars.com/databank/yoda","pdf": {"encryption": {"userPassword": "user-secret","ownerPassword": "owner-secret"}}} - watermark
objectThe watermark options for the PDF. The watermark will be added to each page of the PDF.- source
stringrequiredThe source of the watermark. This can be a text string or an image. Images can be URLs or base64 encoded strings. Supported image formats are PNG and JPEG. - type
text|imagerequiredThe type of the watermark. - options
objectThe options for the watermark, depending on the type.Text- fontFamily
Courier|Helvetica|TimesRomanThe font family of the text. The default isHelvetica. - fontSize
numberThe font size (inpt) of the text. The default is100. - fontColor
stringThe font color of the text. Supported formats are named colors,hexandrgb, e.g.,grey,#dddddd,rgb(128,128,128). The default isgrey. - fontStyle
italic|normalThe font style of the text. The default isnormal. - fontWeight
bold|normalThe font weight of the text. The default isnormal. - opacity
numberThe opacity of the text. The value must be a number between0and1. The default is0.1. - rotation
numberThe rotation of the text in degrees. The value must be between-360and360. The default is0. - offsetX
numberThe horizontal offset of the text. The default is0. - offsetY
numberThe vertical offset of the text. The default is0. - repeat
repeat|repeat-x|repeat-y|no-repeatThe repeat mode of the text. The default isrepeat.
Image- scale
numberThe scale of the image. The default is1. - opacity
numberThe opacity of the image. The value must be a number between0and1. The default is0.1. - rotation
numberThe rotation of the image in degrees. The value must be between-360and360. The default is0.
- offsetX
numberThe horizontal offset of the image. The default is0. - offsetY
numberThe vertical offset of the image. The default is0. - repeat
repeat|repeat-x|repeat-y|no-repeatThe repeat mode of the image. The default isrepeat.
{"source": "https://www.starwars.com/databank/yoda","pdf": {"watermark": {"source": "DRAFT","type": "text","options": {"fontFamily": "Courier","fontSize": 85,"fontColor": "lightgrey","fontStyle": "italic","fontWeight": "bold","opacity": 0.75,"rotation": 45,"offsetX": -20,"offsetY": 10,"repeat": "repeat"}}}}{"source": "https://www.starwars.com/databank/yoda","pdf": {"watermark": {"source": "https://example.com/watermark.png","type": "image","options": {"scale": 0.5,"opacity": 0.75,"rotation": 45,"offsetX": -20,"offsetY": 10,"repeat": "repeat"}}}}{"source": "https://www.starwars.com/databank/yoda","pdf": {"watermark": {"source": "data:image/png;base64,<YOUR IMAGE BASE64 STRING>","type": "image","options": {"scale": 0.5,"opacity": 0.75,"rotation": 45,"offsetX": -20,"offsetY": 10,"repeat": "repeat"}}}} - pdfa
objectWhen set, the generated PDF is post-processed toward PDF/A (ISO 19005) via pdf2pdfa. Conceptual overview and examples: PDF/A guide.- level
"1b"|"2b"|"3b"requiredThe PDF/A conformance level to target."3b"is recommended for HTML-to-PDF workflows because Chromium's renderer produces transparency, which PDF/A-1b forbids. - verify
booleanWhentrue, runs VeraPDF after conversion and requires a compliant PDF/A result. If validation fails, the API returns 422 withcode: "PDFA_VERIFICATION_FAILED"and no PDF body. On success you receive the PDF with 200 like any other conversion. Whenfalseor omitted, conversion is best-effort without this enforcement. Defaults tofalse.
{"source": "https://www.starwars.com/databank/yoda","pdf": {"pdfa": {"level": "3b","verify": true}}}
E-Invoice Params
Add eInvoice to any PDF conversion request to embed a structured invoice into the output PDF. The result is a hybrid PDF — human-readable as a normal PDF and machine-parseable by accounting software — conforming to the ZUGFeRD (Germany) and Factur-X (France / EU) standards. Both formats use the same underlying CII XML and are legally accepted across the EU. For a walkthrough and payload tips, see the E-Invoice guide.
- standard
facturx | zugferdrequiredE-invoice standard to embed.facturxandzugferdproduce equivalent hybrid documents and are interoperable; the choice determines the attachment name and label inside the XML.facturx— recommended for France and cross-border EU invoicing.zugferd— common for German domestic invoicing.
{"source": "<html>...</html>","eInvoice": {"standard": "facturx","profile": "en16931","invoice": { ... }}} - profile
stringrequiredCompliance profile that determines how much data must be present in the XML. Useen16931for standard EU B2B invoices.Value When to use minimumPayment data only; no line items. Very lightweight. basicwlPayment data without line items. basicLine items included, no allowances or charges. en16931Full EN 16931 standard — the recommended choice for most B2B invoices. extendedSuperset of EN 16931 with extra fields for complex invoicing scenarios. {"eInvoice": {"standard": "facturx","profile": "en16931","invoice": { ... }}} - verify
booleanWhentrue, runs VeraPDF on the assembled PDF and requires PDF/A-3b compliance. Non-compliance yields 422 withcode: "PDFA_VERIFICATION_FAILED"(JSONpdfadetails); no PDF is returned. On success you get 200 with the PDF body. When omitted orfalse, no strict verification gate is applied. Defaults tofalse.{"eInvoice": {"standard": "facturx","profile": "en16931","verify": true,"invoice": { ... }}} - invoice
objectrequiredThe invoice data to encode as CII XML.- number
stringrequiredInvoice number. Must be unique per issuer. - issueDate
stringrequiredIssue date in YYYY-MM-DD format, e.g. 2026-03-15. - dueDate
stringPayment due date in YYYY-MM-DD format. - currencyCode
stringrequiredISO 4217 currency code, e.g. EUR, USD, GBP. - seller
objectrequiredInvoice issuer (your company).- name
stringrequiredLegal company or person name. - address
objectrequiredPostal address.- line1
stringrequiredStreet and house number. - line2
stringAdditional address line (floor, suite, c/o, etc.). - city
stringrequiredCity. - postalCode
stringrequiredPostal or ZIP code. - countryCode
stringrequiredISO 3166-1 alpha-2 country code, e.g. DE, FR, NL.
- taxId
stringVAT registration number. Strongly recommended for EU B2B invoices (e.g. DE123456789, FR98765432100). - email
stringContact email address. - phone
stringContact phone number.
- buyer
objectrequiredInvoice recipient (your customer).- name
stringrequiredLegal company or person name. - address
objectrequiredPostal address.- line1
stringrequiredStreet and house number. - line2
stringAdditional address line (floor, suite, c/o, etc.). - city
stringrequiredCity. - postalCode
stringrequiredPostal or ZIP code. - countryCode
stringrequiredISO 3166-1 alpha-2 country code, e.g. DE, FR, NL.
- taxId
stringVAT registration number. Strongly recommended for EU B2B invoices (e.g. DE123456789, FR98765432100). - email
stringContact email address. - phone
stringContact phone number.
- buyerReference
stringBuyer's purchase order number or reference, e.g. PO-2026-7788. - note
stringFree-text note added to the invoice, e.g. a thank-you message or special instructions. - paymentTerms
stringHuman-readable payment terms, e.g. "Net 30 days" or "2% discount within 10 days". - paymentMeans
objectBank account details for payment. Recommended when invoices are processed automatically.- typeCode
stringrequiredUNTDID 4461 payment means code. Common values: "30" (credit transfer / bank transfer), "58" (SEPA credit transfer). - iban
stringIBAN of the payee bank account. - bic
stringBIC / SWIFT code of the bank. - accountName
stringAccount holder name. - paymentReference
stringPayment reference or remittance information.
- lines
object[]requiredLine items. At least one is required.- description
stringrequiredProduct or service description. - quantity
numberrequiredNumber of units. - unitCode
stringUN/ECE unit of measure code. Common values: C62 (pieces/units), HUR (hours), DAY (days), KGM (kilograms). Defaults to C62. - unitPrice
numberrequiredNet price per unit (excluding VAT). - lineTotal
numberrequiredquantity × unitPrice (net, before tax). - vatRate
numberVAT rate in percent, e.g. 19.0. Defaults to 0. - vatCategoryCode
stringEN 16931 VAT category code. S = standard rate, Z = zero-rated, E = exempt. Defaults to S.
- taxSummary
object[]Aggregated VAT totals per rate. Calculated automatically fromlineswhen omitted. Provide explicitly when your invoice has multiple VAT rates or when you need precise rounding control.- categoryCode
stringrequiredVAT category code (S, Z, E, …). - rate
numberrequiredVAT rate in percent. - taxableAmount
numberrequiredNet amount this rate applies to. - taxAmount
numberrequiredCalculated VAT amount.
- totalNetAmount
numberrequiredSum of all line totals (net, before tax). - totalTaxAmount
numberrequiredTotal VAT amount. - totalGrossAmount
numberrequiredtotalNetAmount + totalTaxAmount. This is the amount the buyer pays.
{"source": "<html><h1>Invoice INV-2026-042</h1></html>","eInvoice": {"standard": "facturx","profile": "en16931","verify": true,"invoice": {"number": "INV-2026-042","issueDate": "2026-03-15","dueDate": "2026-04-14","currencyCode": "EUR","seller": {"name": "Acme GmbH","address": {"line1": "Hauptstrasse 1","city": "Berlin","postalCode": "10115","countryCode": "DE"},"taxId": "DE123456789"},"buyer": {"name": "Customer AG","address": {"line1": "Rue du Parc 5","city": "Paris","postalCode": "75008","countryCode": "FR"},"taxId": "FR98765432100"},"buyerReference": "PO-2026-7788","paymentTerms": "Net 30 days","paymentMeans": {"typeCode": "58","iban": "DE89370400440532013000","bic": "COBADEFFXXX","accountName": "Acme GmbH"},"lines": [{"description": "Consulting services (8 h)","quantity": 8,"unitCode": "HUR","unitPrice": 150.00,"lineTotal": 1200.00,"vatRate": 19.0,"vatCategoryCode": "S"}],"totalNetAmount": 1200.00,"totalTaxAmount": 228.00,"totalGrossAmount": 1428.00}}}
Screenshot Params
Render Params
Controls page load and scripting before print. Paged.js and similar libraries often need waitForSelector, mediaType, and layout preferCSSPageSize — see Paged.js and Vivliostyle.
- css
stringThe CSS content to inject into the page.{"source": "https://www.starwars.com/databank/yoda","render": {"css": "body { background-color: red; }"}} - disableJavascript
booleanWhether to disable JavaScript on the page. The default value isfalse.{"source": "https://www.starwars.com/databank/yoda","render": {"disableJavascript": true}} - javascript
stringThe JavaScript content to inject into the page.{"source": "https://www.starwars.com/databank/yoda","render": {"javascript": "alert('Hello, World!');"}} - waitForSelector
stringThe CSS selector to wait for before rendering the page. This can be useful to ensure the page is ready to be rendered, especially in case of lazy loading. See alsowaitForFunction.// Wait for the <div class="chart">...</div> element to be rendered{"source": "https://example.com/chart","render": {"waitForSelector": ".chart"}} - waitForFunction
stringThe JavaScript function to wait for before rendering the page. This can be useful to ensure the page is ready to be rendered, especially in case of lazy loading. See alsowaitForSelector.// Wait for jQuery to be ready{"source": "https://example.com/jquery","render": {"waitForFunction": "window.jQuery"}} - mediaType
print|screenThe media type of the page. The default value isscreen.{"source": "https://www.starwars.com/databank/yoda","render": {"mediaType": "print"}} - blockAds
booleanWhentrue, ads and trackers are blocked during page load using EasyList / uBlock Origin–compatible filter lists. Only effective whensourceis a URL; has no effect on HTML string sources. Blocking ads can produce cleaner output and reduce conversion time by preventing chatty ad-network requests from delaying network idle. The default value isfalse.{"source": "https://www.example.com/report","render": {"blockAds": true}}
Request Params
- userAgent
stringThe user agent to use for the request.{"source": "https://www.starwars.com/databank/yoda","request": {"userAgent": "Mozilla/5.0"}} - The cookies to use for the request. Each cookie must contain either the
urlordomainparameter.- name
stringrequiredThe name of the cookie. - value
stringrequiredThe value of the cookie. - url
stringThe URL of the cookie. - domain
stringThe domain of the cookie. - path
stringURL path scope for the cookie. - expires
numberExpiry time as a Unix timestamp in seconds. - secure
booleanWhether the cookie is secure. - httpOnly
booleanWhether the cookie is http-only. - sameSite
Strict|Lax|NoneSameSite attribute for the cookie.
{"source": "https://www.starwars.com/databank/yoda","request": {"cookies": [{"name": "cookieName","value": "cookieValue","url": "https://www.starwars.com","secure": true,"httpOnly": true}]}} - httpHeaders
objectThe HTTP headers to use for the request. Each header must be a key-value pair.{"source": "https://www.starwars.com/databank/yoda","request": {"httpHeaders": {"X-My-Header": "my-value","Accept-Language": "en-US","DNT": "1"}}} - authentication
objectThe authentication credentials for the HTTP authentication, using the HTTP Basic scheme.{"source": "https://www.starwars.com/databank/yoda","request": {"authentication": {"username": "user","password": "pass"}}}
Cloud Storage Params
When set, the gateway uploads the generated file to your bucket using the presigned URL (HTTP PUT). Step-by-step setup for major providers lives under Cloud storage.
- cloudStorage
objectTarget upload: PolyDoc PUTs the PDF or screenshot bytes to this URL after conversion.- presignedUrl
stringrequiredA presigned HTTP PUT URL for your object store (S3, GCS, Azure, etc.).
{"source": "https://www.starwars.com/databank/yoda","cloudStorage": {"presignedUrl": "https://example.com/presigned-url"}}
Response
HTTP Status Codes
The API returns the following HTTP status codes.
| Code | Meaning | Description |
|---|---|---|
200 | OK | Successful conversion. Usually the response body is the PDF or image file. Same when a webhook is used with async: false (webhook is delivered first, then the file is returned). In some modes the body is JSON instead: e.g. cloudStorage upload confirmation, or screenshot encoding: "base64" (payload under data.base64). |
202 | Accepted | Conversion finished and a webhook was provided with async omitted or true (default): JSON response while the file is sent to your webhook in the background. |
400 | Bad Request | Invalid request body or schema validation failure, template resolution error, or business-rule rejection (e.g. incompatible pdf.pdfa and pdf.encryption). For URL-based sources, navigation errors on the provided URL also surface here. |
401 | Unauthorized | Missing Authorization: Bearer header or invalid API token. |
403 | Forbidden | The authenticated user account is disabled and may not use the API. |
404 | Not Found | The requested path does not exist. |
408 | Request Timeout | The converter stopped waiting for rendering (e.g. page load, navigation, or in-page script) within the timeout you set on the request (up to 10 minutes on paid plans). Returned by the converter and forwarded by the gateway with a JSON error body. |
413 | Payload Too Large | Request body exceeds the gateway size limit, or the converter rejected the payload (e.g. HTML size limit without subscription). |
422 | Unprocessable Entity | The request was syntactically valid but the promised artifact could not be produced or verified for this input. |
429 | Too Many Requests | Per-minute or per-second rate limit exceeded (tracked separately for production and sandbox traffic). |
500 | Internal Server Error | Unexpected gateway error, conversion engine crash, or other failures not mapped to a more specific code. |
502 | Bad Gateway | Conversion succeeded but either uploading to your cloudStorage presigned URL failed, or (with webhook and async: false) webhook delivery failed after retries. |
503 | Service Unavailable | The converter is busy serving another job (after gateway retries) or a required downstream dependency is unavailable. |
504 | Gateway Timeout | The gateway stopped waiting for the converter (or another internal hop timed out). The gateway's converter proxy budget is set above the maximum request timeout (10 minutes on paid plans) so that an overrun during rendering usually surfaces as 408 Request Timeout from the converter with a JSON body instead. If you still see 504 on long jobs, check timeouts on any reverse proxy or load balancer in front of the API. |
Headers
Besides defaults from the HTTP stack, typical responses include the following. HTTP header names are case-insensitive per RFC 7230 §3.2; the casing shown here is for readability only — Polydoc may send the same name in any case (e.g. X-RateLimit-Limit or x-ratelimit-limit) and compliant clients normalize on the receiving side.
| Header | Description |
|---|---|
X-Conversion-Id | Canonical id for this conversion. The same value appears as the conversion id in your conversion history. Present on all PDF and screenshot conversion responses (including errors) so you can correlate logs and support tickets. |
X-Credit-Used | Integer quota units charged for this attempt — same formula as persisted usage: each block of your per-job data quota (default 5 MB of output) counts as one unit, with a minimum of one unit when the conversion engine ran but returned no file (e.g. converter 4xx/5xx). Validation errors before the conversion engine starts use 0. |
Content-Type | application/pdf or an image type for a successful binary body; application/json for JSON-only responses (errors, webhook acceptance, cloud-storage confirmation, base64 screenshot mode). |
Content-Disposition | Present on successful PDF or image downloads (attachment with filename). Omitted for JSON bodies. |
Content-Length | Length of the response body when the gateway sends raw PDF or image bytes. |
X-RateLimit-Limit | Maximum requests allowed in the rate-limit window that supplied this header (see rate limiting). Added by the gateway's rate-limit plugin. |
X-RateLimit-Remaining | How many requests remain in that window before a 429 response. |
X-RateLimit-Reset | Seconds until the rate-limit window resets. |
Retry-After | Present on 429 Too Many Requests from the gateway's rate-limit plugin (window-reset seconds) and on upstream 429 responses forwarded from the conversion service (capacity overflow). Value is the seconds to wait before retrying. |
Payload
The successful 200 response contains the PDF or screenshot file. The error payload is a JSON object with details about the error. The HTTP status code is returned on the response line only; it is not repeated inside the JSON body. To correlate a request with support or your conversion history, read the X-Conversion-Id response header.
{
"success": false,
"error": "Bad Request",
"message": "The parameter 'layout.scale' must be a number between 0.1 and 2.0."
}