DocRaptor
Elixir client for the DocRaptor HTML-to-PDF and XLS API.
Prefer document_content (with inlined CSS/assets) over document_url when
developing against localhost — DocRaptor cannot fetch private hosts without a
tunnel. See Development Testing & Localhost Servers.
Installation
def deps do
[
{:docraptor, "~> 0.1.0"}
]
end
Usage
Built on Req. High-level helpers cover DocRaptor’s
create/status/download/expire API; advanced callers can attach to a
%Req.Request{} directly.
{:ok, client} = DocRaptor.Client.new(api_key: "YOUR_API_KEY")
# test: true by default — watermarked, free quota
{:ok, %DocRaptor.Document{body: pdf, num_pages: pages}} =
DocRaptor.create_doc(client, %{
name: "hello.pdf",
document_type: :pdf,
document_content: "<html><body>Hello</body></html>"
})
{:ok, %DocRaptor.AsyncJob{status_id: status_id}} =
DocRaptor.create_async_doc(client, %{
name: "hello.pdf",
document_type: :pdf,
document_content: "<html><body>Hello</body></html>"
})
{:ok, document} = DocRaptor.await_async_doc(client, status_id)
Low-level Req pipeline
Req.new(method: :post, url: "/docs", json: body, decode_body: false)
|> DocRaptor.Client.attach(client)
|> Req.request()
Set test: false on the client (or per document) for production documents.
API surface
| DocRaptor HTTP API | Client |
|---|---|
POST /docs |
DocRaptor.create_doc/2 |
POST /async_docs |
DocRaptor.create_async_doc/2 |
POST /hosted_docs |
DocRaptor.create_hosted_doc/2 |
POST /hosted_async_docs |
DocRaptor.create_hosted_async_doc/2 |
GET /status/:id |
DocRaptor.get_async_doc_status/2 |
GET /download/:id |
DocRaptor.get_async_doc/2 |
PATCH /expire/:id |
DocRaptor.expire/2 |
| poll + download | DocRaptor.await_async_doc/3 |
| custom Req | DocRaptor.Client.attach/2 |
Document attrs are passed through to DocRaptor after validating name,
document_type/type, and document_content/document_url. That includes
every API parameter
(javascript, pipeline, referrer, callback_url, tag, hosted limits,
prince_options, …).
PDF tutorial parity
Official PDF tutorials
are HTML/CSS/JS (plus a few prince_options). There are no extra Elixir
endpoints to implement — if the request body can express it, this client can
send it.
| Tutorial | What you send |
|---|---|
| CSS Flexbox | Flexbox CSS in document_content |
| Custom Fonts | @font-face CSS (Typekit: also javascript: true + referrer) |
| Custom Page Size | @page { size: ... } CSS |
| Highcharts Chart | Chart HTML/JS + javascript: true |
| HTML and JavaScript | Script in content + javascript: true |
| Image-Based Watermark | Image + Prince @prince-overlay / flow CSS (test: false) |
| Page Numbers | @page + counter(page) / counter(pages) CSS |
| Print Protection | prince_options: %{encrypt: true, disallow_print: true} |
| Printer's Marks | Prince marks CSS in content |
| QR Code | QR HTML/image/JS in content (javascript: true if JS-generated) |
| Rotated Text & Images | CSS transform in content |
| Table of Contents | Prince TOC / heading CSS in content |
| Text-Based Watermark | Text + @prince-overlay / flow CSS (test: false) |
| Vector Images | SVG (or similar) in content |
Example: page numbers (CSS)
Mirrors How to create a PDF with Page Numbers:
{:ok, client} = DocRaptor.Client.new(api_key: "YOUR_API_KEY")
html = """
<div style="page-break-after: always;">Page 1</div>
<div style="page-break-after: always;">Page 2</div>
<div style="page-break-after: always;">Page 3</div>
<div id="page4" style="page-break-after: always;">Page 4, with reset page counter</div>
<style>
@page {
@bottom {
content: "Page " counter(page, upper-alpha) " of " counter(pages, decimal);
}
}
#page4 {
counter-reset: page 1;
}
</style>
"""
{:ok, %DocRaptor.Document{body: pdf}} =
DocRaptor.create_doc(client, %{
name: "page-numbers.pdf",
document_type: :pdf,
document_content: html
})
File.write!("page-numbers.pdf", pdf)
Example: print protection (prince_options)
Mirrors How to create a PDF with Print Protection:
{:ok, client} = DocRaptor.Client.new(api_key: "YOUR_API_KEY")
{:ok, %DocRaptor.Document{body: pdf}} =
DocRaptor.create_doc(client, %{
name: "print-protection.pdf",
document_type: :pdf,
document_content: "You can't print me!",
prince_options: %{
encrypt: true,
disallow_print: true
}
})
File.write!("print-protection.pdf", pdf)
Example: JavaScript chart (javascript: true)
Same pattern as the Highcharts and HTML + JavaScript tutorials — put the script in the HTML, enable DocRaptor’s JS engine:
{:ok, client} = DocRaptor.Client.new(api_key: "YOUR_API_KEY")
html = """
<div id="container" style="width: 600px; height: 400px;"></div>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script>
Highcharts.chart("container", {
title: { text: "Fruit consumption" },
series: [{ name: "Jane", data: [1, 0, 4] }]
});
</script>
"""
{:ok, %DocRaptor.Document{body: pdf}} =
DocRaptor.create_doc(client, %{
name: "highcharts.pdf",
document_type: :pdf,
document_content: html,
javascript: true,
ignore_console_messages: true,
pipeline: "10.1"
})
File.write!("highcharts.pdf", pdf)
Other tutorial knobs that pass through the same way:
# DocRaptor JS engine (charts / most page JS) — preferred default
javascript: true
# Prince JS engine (canvas / PDF-specific JS) — different engine; avoid enabling both
prince_options: %{javascript: true, max_passes: 3, media: "print"}
# Hosted async + callback
DocRaptor.create_hosted_async_doc(client, %{
name: "report.pdf",
document_type: :pdf,
document_content: html,
callback_url: "https://example.com/docraptor/callback",
hosted_download_limit: 25,
hosted_expires_at: "2026-12-31T23:59:59Z",
tag: "invoices"
})
See Usage for attrs, errors, sync/async/hosted choice, and pitfalls
(including when DocRaptor’s watermark tutorials use test: false).
Docs
- HexDocs: https://hexdocs.pm/docraptor
- Agent/integrator reference: Usage (also on HexDocs)
- DocRaptor API parameters: https://docraptor.com/documentation/api/parameters
- DocRaptor PDF tutorials: https://docraptor.com/documentation/pdf_generation/tutorials