API
Upload, list, delete, and debug PDFs programmatically on API-enabled plans.
Authentication
Send your API key with the X-API-Key header. API access requires a verified email account.
X-API-Key: YOUR_API_KEYAPI key scopes
Limit what an API key can do from the API dashboard. Existing keys start with all scopes enabled.
upload Create PDFs and signed upload URLs
read List and read PDF metadata
delete Delete hosted PDFs
analytics Read analytics data
webhooks Manage webhook integrationsMultiple API keys
Create separate keys for production, staging, Zapier, Make, or internal tools. Each key has its own name, prefix, scopes, last-used timestamp, and revoke button. New keys are shown once when created.
Production key: upload, read, delete
Staging key: upload, read
Zapier key: upload
Reporting key: read, analyticsAPI request logs
The API dashboard shows recent requests with method, endpoint, status code, timestamp, IP address, key name, and error message. Use this to debug failed uploads, missing scopes, expired signed upload URLs, and rate limits.
POST /api/v1/upload-url 201
GET /api/v1/pdfs 403 API key is missing required scope: read
POST /api/v1/upload/{token} 410 Upload token expiredUpload PDF
curl -X POST https://pdfhost.se/api/v1/upload \
-H "X-API-Key: YOUR_API_KEY" \
-F "[email protected]"The response includes the PDF slug and hosted URL.
Signed upload URLs
Use signed upload URLs when your app wants a browser, mobile app, or no-code workflow to upload directly to PDFHost without exposing your API key. Signed URLs are one-use and expire after 15 minutes by default.
curl -X POST https://pdfhost.se/api/v1/upload-url \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "proposal.pdf",
"title": "Client proposal",
"folder": "Sales",
"tags": "proposal, client",
"max_bytes": 10485760
}'Then upload the PDF to the returned upload_url using multipart form data.
curl -X POST "RETURNED_UPLOAD_URL" \
-F "[email protected]"List PDFs
curl https://pdfhost.se/api/v1/pdfs \
-H "X-API-Key: YOUR_API_KEY"Search and filter PDFs
Use query parameters on the list endpoint to search by title, filename, slug, folder, or tags. This endpoint requires the read scope.
curl "https://pdfhost.se/api/v1/pdfs?q=proposal&folder=Sales&tag=client&limit=25" \
-H "X-API-Key: YOUR_API_KEY"Folder and tag summaries
Fetch folder and tag counts for building your own document browser.
curl https://pdfhost.se/api/v1/folders \
-H "X-API-Key: YOUR_API_KEY"
curl https://pdfhost.se/api/v1/tags \
-H "X-API-Key: YOUR_API_KEY"Update folder, tags, or title
Update document organization without changing the hosted URL. This endpoint requires the upload scope.
curl -X POST https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx/metadata \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Q4 Client Proposal",
"folder": "Sales",
"tags": ["proposal", "client", "q4"]
}'Analytics API
Fetch the same analytics shown in the dashboard: views, downloads, unique visitors, read-time trend, referrers, devices, browsers, and countries. This endpoint requires the analytics scope and an analytics-enabled plan.
curl "https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx/analytics?range=30" \
-H "X-API-Key: YOUR_API_KEY"Use range=7, range=30, range=90, or a custom date range.
curl "https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx/analytics?range=custom&start=2026-05-01&end=2026-05-28" \
-H "X-API-Key: YOUR_API_KEY"Analytics CSV export
Download daily views, downloads, and average read time as CSV.
curl "https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx/analytics.csv?range=30" \
-H "X-API-Key: YOUR_API_KEY" \
-o analytics.csvReplace PDF without changing the link
Use the replace endpoint when a proposal, menu, report, or catalog changes but the public URL, QR code, embed code, and analytics history should stay the same. This endpoint requires the upload scope.
curl -X POST https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx/replace \
-H "X-API-Key: YOUR_API_KEY" \
-F "[email protected]" \
-F "title=Updated client proposal"The response returns the same hosted URL.
{
"replaced": true,
"slug": "pdf_xxxxxxxxxxxx",
"url": "https://pdfhost.se/d/pdf_xxxxxxxxxxxx",
"download_url": "https://pdfhost.se/d/pdf_xxxxxxxxxxxx/download",
"size": 845120
}Delete PDF
curl -X DELETE https://pdfhost.se/api/v1/pdfs/pdf_xxxxxxxxxxxx \
-H "X-API-Key: YOUR_API_KEY"Language examples
Switch language to copy the same core API workflow in the stack you use.
curl -X POST https://pdfhost.se/api/v1/upload \
-H "X-API-Key: YOUR_API_KEY" \
-F "[email protected]" \
-F "folder=Sales" \
-F "tags=proposal,client"
curl https://pdfhost.se/api/v1/pdfs \
-H "X-API-Key: YOUR_API_KEY"const apiKey = "YOUR_API_KEY";
async function listPdfs() {
const res = await fetch("https://pdfhost.se/api/v1/pdfs", {
headers: { "X-API-Key": apiKey }
});
return await res.json();
}
async function deletePdf(slug) {
const res = await fetch(`https://pdfhost.se/api/v1/pdfs/${slug}`, {
method: "DELETE",
headers: { "X-API-Key": apiKey }
});
return await res.json();
}import fs from "fs";
import fetch from "node-fetch";
import FormData from "form-data";
const form = new FormData();
form.append("pdf", fs.createReadStream("./proposal.pdf"));
form.append("folder", "Sales");
form.append("tags", "proposal,client");
const res = await fetch("https://pdfhost.se/api/v1/upload", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", ...form.getHeaders() },
body: form
});
console.log(await res.json());import requests
api_key = "YOUR_API_KEY"
with open("proposal.pdf", "rb") as pdf:
response = requests.post(
"https://pdfhost.se/api/v1/upload",
headers={"X-API-Key": api_key},
files={"pdf": pdf},
data={"folder": "Sales", "tags": "proposal,client"},
timeout=60,
)
print(response.status_code)
print(response.json())<?php
$ch = curl_init("https://pdfhost.se/api/v1/upload");
$payload = [
"pdf" => new CURLFile(__DIR__ . "/proposal.pdf", "application/pdf", "proposal.pdf"),
"folder" => "Sales",
"tags" => "proposal,client",
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => ["X-API-Key: YOUR_API_KEY"],
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
echo $status . PHP_EOL;
echo $body . PHP_EOL;Signed upload with fetch
Ask your server for a signed upload URL, then send the PDF to that temporary URL.
const createUrl = await fetch("https://pdfhost.se/api/v1/upload-url", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
filename: "proposal.pdf",
title: "Client proposal",
max_bytes: 10485760
})
});
const signed = await createUrl.json();
const form = new FormData();
form.append("pdf", fileInput.files[0]);
const uploaded = await fetch(signed.upload_url, {
method: "POST",
body: form
});
console.log(await uploaded.json());Zapier setup
Use Webhooks by Zapier with a custom request.
Action: POST
URL: https://pdfhost.se/api/v1/upload-url
Headers:
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Data:
{
"filename": "zapier-upload.pdf",
"title": "Zapier upload",
"max_bytes": 10485760
}
Next action:
POST the PDF file to the returned upload_url as multipart field "pdf".Make setup
Use the HTTP module. First create the upload URL, then upload the file to the returned URL.
Module 1: HTTP / Make a request
Method: POST
URL: https://pdfhost.se/api/v1/upload-url
Headers:
X-API-Key: YOUR_API_KEY
Content-Type: application/json
Body type: Raw JSON
Module 2: HTTP / Upload a file
Method: POST
URL: upload_url from Module 1
Field name: pdfError responses
Errors use JSON and standard HTTP status codes. Check the API logs in your dashboard for the full message and request context.
{
"error": "API key is missing required scope: upload"
}
Common statuses:
401 Unauthorized
402 Payment required
403 Missing verification, plan, or scope
410 Signed upload URL expired
429 API rate limit exceededWebhook events
Create webhook endpoints from the API dashboard to receive document events in your app.
pdf.uploaded
pdf.deleted
pdf.viewed
pdf.downloaded
pdf.expiredWebhook signature verification
Each delivery includes X-PDFHost-Timestamp and X-PDFHost-Signature. Verify the signature with your webhook secret.
const crypto = require("crypto");
const signedPayload = timestamp + "." + rawBody;
const expected = "sha256=" + crypto
.createHmac("sha256", webhookSecret)
.update(signedPayload)
.digest("hex");
if (signature !== expected) {
throw new Error("Invalid PDFHost webhook signature");
}Example JSON response
{
"id": "42",
"slug": "pdf_xxxxxxxxxxxx",
"url": "https://pdfhost.se/d/pdf_xxxxxxxxxxxx",
"size": 532100
}