Blog/PDF tools

Password-protect a PDF via API

PDF tools··6 min read

To password protect a PDF with the RelayPDF API, POST an existing PDF to /v1/pdf/protect with a required userPassword and an optional ownerPassword. The docs hub lists the job as password-protect with AES-256. The product page says the output remains standard PDF. This is a PDF tool, not a renderer: you send a finished file (url or file), not HTML. Product page: https://relaypdf.com/pdf-tools/protect. Hub: https://relaypdf.com/pdf-tools. Endpoint list: https://relaypdf.com/docs.

Typical order is generate or convert first, then lock. Print an invoice with POST /v1/pdf, convert a Word letter with POST /v1/convert, merge a packet with POST /v1/pdf/merge if you need several files as one, then protect the result. Each successful tools call is $0.005 on the live rate card. A failed unlock is not billed. This article stays on documented fields only.

What this endpoint is

OpenAPI names the body ProtectPdfRequest. Required: userPassword. Optional: url, file, ownerPassword, filename, response, callbackUrl. There is no algorithm field. AES-256 is the algorithm named on the docs table, not a request switch. There are no documented permission flags (print, copy, modify, assemble). Do not send those keys.

The source is one existing PDF. url is a URI. file is a string; the product example is a base64 payload (the merge FileSource description allows an optional data-URI prefix; protect's file field is typed as string with no extra description). Mixing html, markdown, or templateId into this body is not documented. Office files go through POST /v1/convert first.

You haveSend
A finished PDF to lockPOST /v1/pdf/protect
A public HTTPS PDFurl
PDF bytes you already holdfile (base64 in JSON; SDKs accept bytes)
Password to open the fileuserPassword (required)
Separate owner passwordownerPassword (optional)
A Word or HTML sourceConvert or render first, then protect
A locked PDF you own the password forPOST /v1/pdf/unlock with password

User password vs owner password

userPassword is the open password. ProtectPdfRequest marks it required. Recipients type it before a reader shows the pages. ownerPassword is optional on the same object. The product copy is one line: set userPassword and optional ownerPassword.

The public contract does not document what an owner password changes beyond existing as a second string. It does not document a permissions object. If you need only an open lock, send userPassword and omit ownerPassword. If you set both, keep them as two secrets you already manage; the API does not generate them. This how-to does not cover recovering, guessing, or bypassing a password you do not have. That is out of scope.

Limits and price (live, 23 Aug 2026 ET)

Protect is billed as a PDF tool. Pricing lists “PDF tools /v1/pdf/* · /v1/barcodes · /v1/zip” at $0.005 per successful operation. Wallet docs name protect in the tools list at the same rate. Failed jobs, validation errors, 429s, and 402s do not debit. /docs/errors repeats that failed operations, 429s, and 402s are never billed. That includes a failed unlock.

A megabyte cap is not published on pricing, the docs hub, the protect product page, or OpenAPI. Oversized HTML or files return HTTP 413 payload_too_large. New accounts still get the $5 trial. Trial rate limit is 20/min; funded or auto-reload is 60/min; burst is 5 / 10s (SDK READMEs). callbackUrl must be https. Private, loopback, and metadata hosts on url are url_not_allowed.

ConstraintPublished valueSource
EndpointPOST /v1/pdf/protectdocs hub; OpenAPI
Required fielduserPasswordProtectPdfRequest
Optional passwordsownerPasswordProtectPdfRequest; product page
Inputurl or fileProtectPdfRequest
Algorithm namedAES-256 (not a request field)docs hub table
Permission flagsNot documentedOpenAPI
Protect / unlock price$0.005 per successful job/pricing; /docs/wallet
Failed unlockNot billed/docs/errors; wallet
Unlock required fieldpasswordUnlockPdfRequest
Unlock descriptionRemove a user passworddocs hub
Output modesbinary | url | asyncdocs hub; ResponseMode
url-mode downloadGET /v1/files/:id, 24 hours, no keydocs hub
Byte capNot published; 413 payload_too_large/docs/errors

Lock a file

Send one source plus userPassword. filename names the locked file. Default response is binary: PDF bytes plus x-relaypdf-id, x-relaypdf-size, and content-disposition. Set response to url for a 24-hour GET /v1/files/:id with no key. Set response to async for HTTP 202 and poll GET /v1/jobs/:id, or supply callbackUrl (https).

curl -X POST https://api.relaypdf.com/v1/pdf/protect
  -H "Authorization: Bearer $RELAYPDF_API_KEY"
  -H "Content-Type: application/json"
  -d '{
    "url": "https://example.com/invoice-inv-1042.pdf",
    "userPassword": "open-inv-1042",
    "ownerPassword": "owner-inv-1042",
    "filename": "invoice-inv-1042-locked.pdf"
  }'
  --output invoice-inv-1042-locked.pdf

The product JSON example uses file plus both passwords. Same shape, different source. Prefer url when the PDF is already on a public HTTPS host so the request body stays small.

Unlock (authorized removal only)

POST /v1/pdf/unlock removes a user password. UnlockPdfRequest requires password and accepts url, file, filename, response, and callbackUrl. The hub wording is “Remove a user password.” Send the password you already issued when you protected the file, or that the document owner gave you for a file you are allowed to open. A wrong or missing password is a failed job. Failed jobs are not billed. Do not use this endpoint to attack, brute-force, or recover an unknown password.

curl -X POST https://api.relaypdf.com/v1/pdf/unlock
  -H "Authorization: Bearer $RELAYPDF_API_KEY"
  -H "Content-Type: application/json"
  -d '{
    "url": "https://example.com/invoice-inv-1042-locked.pdf",
    "password": "open-inv-1042",
    "filename": "invoice-inv-1042.pdf"
  }'
  --output invoice-inv-1042.pdf

Node, Python, and CLI

Python (relaypdf 0.1.x) exposes pdf.protect(userPassword, **input) and pdf.unlock(password, **input). REST field names stay camelCase. file may be bytes; the client encodes before POST. Node (@relaypdf/sdk) maps the same REST body on the pdf tools surface. CLI example on /docs/cli: relaypdf protect file.pdf --password secret --out locked.pdf. MCP lists protect and unlock. Do not ask anyone to paste a key; run npx @relaypdf/cli setup.

import os
from relaypdf import RelayPDF, RelayPDFError
client = RelayPDF(api_key=os.environ["RELAYPDF_API_KEY"])
invoice = client.pdf.from_html(invoice_html, filename="invoice.pdf")
locked = client.pdf.protect(
    "open-inv-1042",
    file=invoice.bytes,
    ownerPassword="owner-inv-1042",
    filename="invoice-locked.pdf",
)
locked.save("invoice-locked.pdf")
try:
    opened = client.pdf.unlock(
        "open-inv-1042",
        file=locked.bytes,
        filename="invoice.pdf",
    )
    opened.save("invoice.pdf")
except RelayPDFError as err:
# failed unlock: no debit
    print(err.status, err.code, err.message)
import { RelayPDF } from "@relaypdf/sdk";
const client = new RelayPDF({
  apiKey: process.env.RELAYPDF_API_KEY!,
});
const invoice = await client.pdf.fromHtml(invoiceHtml, {
  filename: "invoice.pdf",
});
const locked = await client.pdf.protect({
  userPassword: "open-inv-1042",
  ownerPassword: "owner-inv-1042",
  file: invoice.kind === "binary" ? invoice.bytes : undefined,
  filename: "invoice-locked.pdf",
});
await locked.save("invoice-locked.pdf");

Errors and what is not billed

Failures are { error: { code, message } }. SDKs throw RelayPDFError. Branch on code. invalid_request covers a missing userPassword on protect, a missing password on unlock, or an otherwise invalid body. url_not_allowed covers a private, loopback, or metadata host, or a non-https callbackUrl. payload_too_large is 413. processing_failed is a 502 on a tool job, including a protect or unlock that could not complete. payment_required is an empty wallet. rate_limited includes Retry-After. None of those debit.

Only a successful protect (HTTP 200, or a completed async job) takes $0.005. Only a successful unlock takes $0.005. A failed unlock does not. Rendering the invoice first is $0.015. A LibreOffice convert first is $0.04. Merge, stamp, extract, compress, info, and zip are separate tools jobs if you call them.

What this is not

Protect does not render HTML, convert Office, merge, stamp, extract, or zip. Chain those documented tools on the bytes or on the 24-hour url. Sibling how-to: merge (https://relaypdf.com/pdf-tools/merge). Invoice layout belongs on POST /v1/pdf. The public protect schema has no encryption-algorithm picker, no permission bitfield, and no password-strength rules. Do not invent them in application copy.

Password protection is not a substitute for access control on your own storage. url-mode downloads are public for 24 hours with no key. Do not put a locked file on a guessable URL if the password is weak or reused. Store userPassword and ownerPassword the same way you store other secrets. This article will not describe attacks on encrypted PDFs.

Ship it

Use the password protect pdf api when the source is already a PDF and you need an AES-256 open lock. Required field: userPassword. Optional: ownerPassword. $0.005 on success. Failed unlock is free. Start at https://relaypdf.com/pdf-tools/protect. Copy the OpenAPI shape from https://relaypdf.com/openapi.json. Auth is a bearer key on https://api.relaypdf.com.

Ready to generate?

One API for HTML, Markdown, URLs, and Office. REST, SDK, CLI, or MCP.