PDF API docs
Docweave is a PDF API built for AI agents and developers: one REST or MCP call generates a PDF from HTML, a URL, or a template + JSON — or reads a PDF back to markdown. Use the reference below for auth, endpoints, options, and copy-paste samples.
Merge, batch, or run async with webhooks. Bearer-key auth, idempotency, per-document metering.
Quick start
- Create a free account at /signup — 50 PDFs/month, no card.
- In your dashboard, mint an API key (shown once — copy it).
- POST to
/api/v1/pdfwith that key (examples below). - Need more volume? Upgrade anytime at /pricing.
Authentication
Pass your key as Authorization: Bearer <key>. Include an idempotencyKey to make retries safe — a repeat returns the stored result instead of re-rendering (or re-billing).
POST /api/v1/pdf
The core endpoint. Send a source object and optional options. Returns JSON by default; add Accept: application/pdf for raw bytes.
Source types
| type: "html" | string | Raw HTML string to render |
| type: "url" | string | Public URL to screenshot (SSRF-guarded) |
| type: "markdown" | string | Markdown rendered with pro typography |
| type: "template" | string | Mustache template + data object |
curl https://docweave.dev/api/v1/pdf \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-H "Accept: application/pdf" \
-d '{
"source": { "type": "html", "html": "<h1>Hello, PDF</h1>" },
"options": { "format": "A4" }
}' \
--output hello.pdfimport { writeFile } from "node:fs/promises";
const res = await fetch("https://docweave.dev/api/v1/pdf", {
method: "POST",
headers: {
Authorization: "Bearer dw_live_your_key",
"Content-Type": "application/json",
Accept: "application/pdf",
},
body: JSON.stringify({
source: {
type: "template",
template: "<h1>Invoice {{ number }}</h1><p>Total: {{ total }}</p>",
data: { number: "INV-042", total: "$1,280.00" },
},
idempotencyKey: "inv-042",
}),
});
await writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));import requests
r = requests.post(
"https://docweave.dev/api/v1/pdf",
headers={"Authorization": "Bearer dw_live_your_key", "Accept": "application/pdf"},
json={"source": {"type": "url", "url": "https://example.com"}},
)
with open("out.pdf", "wb") as f:
f.write(r.content)package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"source": map[string]string{"type": "html", "html": "<h1>Hello</h1>"},
})
req, _ := http.NewRequest("POST", "https://docweave.dev/api/v1/pdf", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer dw_live_your_key")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/pdf")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := os.Create("hello.pdf")
io.Copy(out, resp.Body)
}require "net/http"
require "json"
require "uri"
uri = URI("https://docweave.dev/api/v1/pdf")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer dw_live_your_key",
"Content-Type" => "application/json",
"Accept" => "application/pdf",
})
req.body = { source: { type: "html", html: "<h1>Hello</h1>" } }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
File.binwrite("hello.pdf", res.body)<?php
$ch = curl_init("https://docweave.dev/api/v1/pdf");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer dw_live_your_key",
"Content-Type: application/json",
"Accept: application/pdf",
],
CURLOPT_POSTFIELDS => json_encode([
"source" => ["type" => "html", "html" => "<h1>Hello</h1>"],
]),
]);
$pdf = curl_exec($ch);
file_put_contents("hello.pdf", $pdf);import java.net.http.*;
import java.net.URI;
import java.nio.file.*;
var body = """
{"source": {"type": "html", "html": "<h1>Hello</h1>"}}""";
var req = HttpRequest.newBuilder()
.uri(URI.create("https://docweave.dev/api/v1/pdf"))
.header("Authorization", "Bearer dw_live_your_key")
.header("Content-Type", "application/json")
.header("Accept", "application/pdf")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
var res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("hello.pdf"), res.body());Page options
All options are optional. Pass them in the options object.
| format | "A4" | "A3" | "Letter" | "Legal" | "Tabloid" | Page size (default: A4) |
| landscape | boolean | Landscape orientation |
| margin | object | { top, right, bottom, left } as CSS sizes |
| scale | number | Render scale 0.1–2.0 |
| printBackground | boolean | Include CSS backgrounds/colors |
| header | string | Simple header text (supports {{page}}, {{pages}}) |
| footer | string | Simple footer text (supports {{page}}, {{pages}}) |
| headerHtml | string | Raw Chromium header template HTML |
| footerHtml | string | Raw Chromium footer template HTML |
| watermark | string | Diagonal text watermark on all pages |
| font | string | Google Font family name (auto-imported) |
Markdown source
Send source.type: "markdown" with a raw markdown string. Tables, code blocks, headings, lists — all rendered with professional typography. Combine with the font option to change the typeface.
curl https://docweave.dev/api/v1/pdf \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-H "Accept: application/pdf" \
-d '{
"source": {
"type": "markdown",
"markdown": "# Project Report\n\n## Summary\n\nQ3 revenue grew **42%** YoY.\n\n| Metric | Value |\n|--------|-------|\n| Revenue | $1.2M |\n| Users | 15,000 |"
},
"options": { "font": "Inter" }
}' --output report.pdfHeaders & footers
Use options.header and options.footer for simple text with page number placeholders. Use {{page}} for current page and {{pages}} for total. Set margins to give the header/footer room.
{
"source": { "type": "html", "html": "<h1>Contract</h1><p>Terms...</p>" },
"options": {
"header": "Acme Corp — Confidential",
"footer": "Page {{page}} of {{pages}}",
"margin": { "top": "80px", "bottom": "80px" }
}
}Watermarks
Add options.watermark to stamp a diagonal semi-transparent label across every page. Common values: "DRAFT", "CONFIDENTIAL", "SAMPLE".
{
"source": { "type": "html", "html": "<h1>Draft Proposal</h1><p>...</p>" },
"options": {
"watermark": "DRAFT",
"format": "A4"
}
}Google Fonts
Pass options.font with any Google Font family name. It is automatically imported and applied to the document body. No need to add <link> tags yourself.
{
"source": { "type": "html", "html": "<h1>Fancy Report</h1><p>With custom typography</p>" },
"options": {
"font": "Playfair Display",
"format": "A4"
}
}POST /api/v1/pdf/batch
Render up to 10 PDFs in a single request. Each item uses the same format as /api/v1/pdf. Returns an array of results (some may fail while others succeed). Each successful render counts as one against your monthly quota.
curl https://docweave.dev/api/v1/pdf/batch \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "source": { "type": "html", "html": "<h1>Invoice 1</h1>" } },
{ "source": { "type": "html", "html": "<h1>Invoice 2</h1>" } },
{ "source": { "type": "markdown", "markdown": "# Report" } }
]
}'
# → { "results": [{ "ok": true, ... }, { "ok": true, ... }, ...] }POST /api/v1/pdf/merge
Render 2–10 sources and combine them into a single multi-page PDF. Each source can be a different type (HTML, markdown, URL, template). The merge counts as one render.
curl https://docweave.dev/api/v1/pdf/merge \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-H "Accept: application/pdf" \
-d '{
"sources": [
{ "source": { "type": "html", "html": "<h1>Cover Page</h1>" } },
{ "source": { "type": "markdown", "markdown": "# Chapter 1\n\nContent..." } },
{ "source": { "type": "html", "html": "<h1>Appendix</h1>" } }
],
"fileName": "full-document.pdf"
}' --output full-document.pdfPOST /api/v1/pdf/async
For long-running renders or when you don't want to hold a connection open. Returns a job ID immediately (HTTP 202), then POSTs the result to your webhookUrl when the PDF is ready.
curl https://docweave.dev/api/v1/pdf/async \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "url", "url": "https://example.com/large-report" },
"webhookUrl": "https://your-app.com/webhooks/pdf-ready"
}'
# → { "jobId": "job_abc123", "status": "processing" }
# Your webhook receives:
# POST https://your-app.com/webhooks/pdf-ready
# { "jobId": "job_abc123", "status": "completed",
# "result": { "ok": true, "url": "...", "byteSize": 45231 } }POST /api/v1/read
The other direction: turn a PDF into text or markdown. Send a source that is a public URL or inline base64 bytes; get back extracted content, page count, and a needsOcr flag. URL rendering is SSRF-guarded, and reads are metered per document.
curl https://docweave.dev/api/v1/read \
-H "Authorization: Bearer dw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "url", "url": "https://example.com/invoice.pdf" },
"options": { "format": "markdown" }
}'
# → { "result": { "content": "…markdown…", "pageCount": 3,
# "hasTextLayer": true, "needsOcr": false } }Integrations
Docweave works with any tool that can make HTTP requests. Here are step-by-step guides for popular no-code platforms:
n8n
HTTP Request node → PDF in your workflow
Make (Integromat)
HTTP module → PDF in any scenario
Zapier
Webhooks by Zapier → PDF on any trigger
AI Agents (MCP)
generate_pdf + read_pdf tools via MCP server
Plans & limits
Free includes 50 PDFs/month. Starter (1,000), Pro (12,000), and Business (50,000) raise the monthly limit — see pricing. When you hit your limit the API returns 402. Only successful renders count; the counter resets each calendar month.
Rate limit: 120 requests/minute per API key. Batch items count individually toward quota but as one rate-limit hit. The async endpoint counts one render on completion.