Password-protect DOCX API
Send a Word file and an EncryptPassword to POST /v2/convert/docx/to/protect and get the same document back encrypted the way Office 2010 and later encrypt it. The file keeps its name and .docx extension; inside, the original package is wrapped in an encrypted container.
- Endpoint
- POST /v2/
convert/ docx/ to/ protect - Accepts
- docx
- Sample timing
- 34 ms median of 5 runsHow we measure
- Request size
- Up to 10 MB on the Free plan or 100 MB on paid plans, all files together
Request and response
Every sample below ran against the API before this page was published. Set PDFCONVERSIONAPI_KEY to your key and run it as is.
curl -sS -X POST \
https://api.pdfconversionapi.com/v2/convert/docx/to/protect \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@volunteer-handbook.docx \
--form-string EncryptPassword=tool-shed-42 \
-o response.json
# FileData is base64. Save the first file in converted/,
# or print the JSON error.
if jq -e '.Files' response.json > /dev/null; then
mkdir -p converted
name=$(jq -r '.Files[0].FileName' response.json)
jq -r '.Files[0].FileData' response.json \
| base64 --decode > "converted/$name"
else
cat response.json
fiimport base64
import os
import requests
key = os.environ["PDFCONVERSIONAPI_KEY"]
response = requests.post(
"https://api.pdfconversionapi.com/v2/convert/docx/to/protect",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("volunteer-handbook.docx", "rb"))],
data={
"EncryptPassword": "tool-shed-42",
},
timeout=180,
)
result = response.json()
if not response.ok:
raise SystemExit(f"{result['Code']}: {result['Message']}")
os.makedirs("converted", exist_ok=True)
for file in result["Files"]:
path = os.path.join("converted", file["FileName"])
with open(path, "wb") as out:
out.write(base64.b64decode(file["FileData"]))
print(path, file["FileSize"], "bytes")
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
const form = new FormData();
const file = "volunteer-handbook.docx";
form.append("File", new Blob([await readFile(file)]), file);
form.append("EncryptPassword", "tool-shed-42");
const url = "https://api.pdfconversionapi.com/v2/convert/docx/to/protect";
const key = process.env.PDFCONVERSIONAPI_KEY;
const response = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: form,
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.Code}: ${result.Message}`);
}
await mkdir("converted", { recursive: true });
for (const file of result.Files) {
const path = join("converted", file.FileName);
await writeFile(path, Buffer.from(file.FileData, "base64"));
console.log(path, file.FileSize, "bytes");
}
{
"ConversionCost": 1,
"ConversionTime": 0.034,
"Files": [
{
"FileName": "volunteer-handbook.docx",
"FileExt": "docx",
"FileSize": 49152,
"FileData": "0M8R4KGxGuEAAAAAAAAAAAAA…"
}
]
}Response from our test run on 2026-11-03. ConversionTime shows the median of 5 runs. FileData is shortened here; the real response carries the whole file as base64.
The sample, converted
The same file the code sends. Download the input and the output and compare them yourself.
- Input
- volunteer-handbook.docx (43 KB)
- Output
- volunteer-handbook.docx (48 KB)
- Source
- A two-page volunteer handbook for a fictional tool library, written for this site, with a stale table of contents, a footnote, a comment, a tracked change, a table and a picture. (CC0-1.0). How it was made
- Made by
- 2026-11-04, API build
9eeeb0640aba
Parameters
Send these as form fields next to the file. Names are case-insensitive. If you set one of the documented options below that we don't support yet, the request fails with a 400 that names it instead of returning a file made without it.
| Parameter | What it does |
|---|---|
EncryptPasswordstring | The password the document will ask for when it is opened. Required: leaving it out or empty returns 400 "EncryptPassword is required". Case matters (the sample opens with tool-shed-42, not TOOL-SHED-42), and characters outside ASCII work.Values: 1–255 characters |
Passwordstring | The current password, when the file you send is already encrypted. The API opens it with this password and encrypts it again with EncryptPassword, which is how you change a password in one call. Without it, an encrypted upload returns 422. |
FileNamestring | Name of the protected file, without extension: FileName=locked-handbook returned locked-handbook.docx. |
StoreFileboolean, default false | Keep the encrypted file on our side for 3 hours and return a link to it instead of the data. |
What carries over, and what doesn't
Kept
- The whole document, byte for byte: the converter encrypts the uploaded package as it is, so opening it with the password gives back the same file
- Everything inside it, from the sample's comment and tracked change to the footnote and the banner picture
- The file name and extension:
volunteer-handbook.docxcomes back asvolunteer-handbook.docx
Lost or not yet supported
- Readability without the password, which is the purpose. Search indexers, previewers and our other routes see only an encrypted container until the password is supplied.
- Editing restrictions are not set. This route adds a password to open; it does not make the file read-only or limit changes.
- Size: the 44,003-byte handbook became 49,152 bytes: the encrypted container adds its key information and a directory, and is stored in 512-byte blocks (96 of them).
Limits
- DOCX in, DOCX out; the .doc format can't be protected here and returns 422.
EncryptPasswordup to 255 characters, the limit Office applies; longer returns 400.- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together; 120 seconds per conversion, then 504.
Where teams use it
Emailing generated contracts or pay statements
Encrypt each document with a password the recipient already knows, such as an account number, before it leaves your system. The attachment can't be read if it is forwarded to the wrong inbox.
Encrypting files before they go to shared storage
Protect HR or legal documents before writing them to a file share or a bucket that many people or services can list.
Rotating passwords on stored documents
Send the protected file with the old Password and a new EncryptPassword. In our test the result opened with the new password and was refused with the old one.
Questions
What encryption is used?
Office's agile encryption, as described in Microsoft's MS-OFFCRYPTO: AES with a 256-bit key in CBC mode, SHA-512 hashing, a 16-byte random salt and 100,000 hashing rounds, plus an HMAC so tampering is detected. A new salt is drawn for every request, so protecting the same file twice gives two different outputs.
How do I open the protected file with this API?
Send it to any DOCX route with Password. Our sample, protected with tool-shed-42, converts to text with Password=tool-shed-42 and gives exactly the same text as the unprotected file. A wrong password returns 422 "the input file is password-protected".
How do I remove the password again?
Send the file to /v2/convert/docx/to/docx with Password. The DOCX that comes back is not encrypted.
Can I protect a .doc, .xlsx or .pptx?
A Word 97-2003 .doc returns 422 "this DOCX is already password-protected, or is an older binary Office file"; convert it with /v2/convert/doc/to/docx first. Excel and PowerPoint files have their own routes, /v2/convert/xlsx/to/protect and /v2/convert/pptx/to/protect.
Convert your first DOCX file in a minute.