pdfedge
API REFERENCE · v0.2.0 · 20 OPERATIONS

pdfedge

Merge, split and reorder pages; stamp text, images, QR codes, watermarks and page numbers; fill form fields or place values on any PDF; protect and unlock; read text, markdown, fields and facts. Files go in by id, by URL, as base64, or as the request body, and every result comes back as a hosted file whose id is a valid input to the next call. Costs are in documents: one per 50 pages, counted on the larger of what went in and what came out; uploads and file reads are free. Plan ceilings are reported on every response in the X-Gw-* headers (X-Gw-Remaining, X-Gw-Reset), so clients and agents can pace themselves.

GETTING STARTED
Every request goes to https://api.pdfedge.com/ with your key in the X-API-Key header. What's left of your allowance rides back on every response in X-Gw-Remaining.
Get a key → · Sign in →
curl "https://api.pdfedge.com/v1/files" \
  -H "X-API-Key: YOUR_KEY"
FROM AN AI AGENT
This API is also an MCP server. Add https://mcp.pdfedge.com/ to Claude Code, Cursor or any MCP client with your key as a bearer token and every operation below becomes a tool — same key, same metering, same allowance. In a connector that only takes a URL, use the connector URL instead. Prompting a model by hand? Every operation has a Markdown version.
claude mcp add --transport http pdfedge https://mcp.pdfedge.com/ \
  --header "Authorization: Bearer YOUR_KEY"
ERRORS
Every operation can also answer with these, so they are listed once here rather than on each card.
400A parameter is missing, malformed or out of range
{
  "error": "string",
  "message": "string",
  "param": "string"
}
401Missing or invalid API key (from the gateway)

Files

GET/v1/files

List this account's files (uploads and results) that have not expired, newest first

The listing can trail an upload or a delete by up to a minute; GET /v1/files/{id} is always current.

PARAMETERS
kindquery · enum
Only uploads, or only results
one of: upload, result
Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/files" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "files": [
    {
      "id": "fil_8kq2wzm4tnb7hdc3vrex",
      "object": "file",
      "kind": "upload",
      "operation": null,
      "name": "contract.pdf",
      "format": "pdf",
      "content_type": "application/pdf",
      "pages": 4,
      "bytes": 91822,
      "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
      "keep": "1d",
      "created": "2026-10-11T18:10:40Z",
      "expires": "2026-10-12T18:10:40Z",
      "encrypted": false
    }
  ]
}
429Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

POST/v1/files

Upload a PDF (or a PNG or JPEG for stamps) and get an id to pass to any operation

Send the file as the request body with its Content-Type (--data-binary @contract.pdf -H 'Content-Type: application/pdf'), as multipart/form-data with a file field (-F file=@contract.pdf), or as JSON with url (copied from a public URL) or data (base64). The type comes from the bytes. Free. Kept 24 hours unless keep says longer; every operation also takes files by URL or base64 directly, so uploading first is optional.

REQUEST BODY · APPLICATION/JSON
{
  "url": "https://example.com/contracts/acme-msa.pdf",
  "name": "acme-msa.pdf",
  "keep": "7d"
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/files" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/contracts/acme-msa.pdf",
  "name": "acme-msa.pdf",
  "keep": "7d"
}'
RESPONSE
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "upload",
  "operation": null,
  "name": "contract.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 4,
  "bytes": 91822,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "encrypted": false
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

GET/v1/files/{id}

One file's record: its URL, pages, size and when it expires

PARAMETERS
idpath · stringREQUIRED
The file id
e.g. fil_8kq2wzm4tnb7hdc3vrex
Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/files/fil_8kq2wzm4tnb7hdc3vrex" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "merge",
  "name": "merged.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

DELETE/v1/files/{id}

Delete a file now instead of waiting for it to expire; its URL stops working

PARAMETERS
idpath · stringREQUIRED
The file id
e.g. fil_8kq2wzm4tnb7hdc3vrex
Open on its own page
REQUEST
curl -X DELETE "https://api.pdfedge.com/v1/files/fil_8kq2wzm4tnb7hdc3vrex" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "deleted": true
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Pdfs

POST/v1/pdfs

Make a PDF from images (PNG or JPEG), one page each, on a paper size or at each image's own size

Phone photos come out the right way up: the JPEG's EXIF orientation is drawn in. fit contain (default) shows the whole image, cover fills the page, stretch ignores proportions. Send the images as multipart (-F file=@1.jpg -F file=@2.jpg) to skip uploading.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "images": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    "https://example.com/receipt-2.jpg"
  ],
  "size": "A4",
  "margin": 24
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/pdfs" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "images": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    "https://example.com/receipt-2.jpg"
  ],
  "size": "A4",
  "margin": 24
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "create",
  "name": "images.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 2,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Merge

POST/v1/merge

Merge PDFs into one, in order, optionally taking only some pages of each

Each file can be an id, a URL or base64, with pages to take part of it ("1-3,7") and password if it is encrypted. Form fields come through and stay fillable; a field name used twice gets _2, _3. Send the files as multipart (-F file=@a.pdf -F file=@b.pdf) to skip uploading.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "files": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    {
      "url": "https://example.com/terms.pdf",
      "pages": "1-2"
    }
  ],
  "name": "contract-with-terms.pdf"
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/merge" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "files": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    {
      "url": "https://example.com/terms.pdf",
      "pages": "1-2"
    }
  ],
  "name": "contract-with-terms.pdf"
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "merge",
  "name": "merged.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Split

POST/v1/split

Split a PDF into several: by ranges, every n pages, or one per page

Answers with a file per part, or with one zip (zip: true, needed past 500 parts). Form fields stay fillable in each part.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "ranges": [
    "1-2",
    "3-"
  ]
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/split" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "ranges": [
    "1-2",
    "3-"
  ]
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "object": "split",
  "files": [
    {
      "id": "fil_8kq2wzm4tnb7hdc3vrex",
      "object": "file",
      "kind": "result",
      "operation": "split",
      "name": "contract-pages-1-2.pdf",
      "format": "pdf",
      "content_type": "application/pdf",
      "pages": 2,
      "bytes": 284113,
      "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
      "keep": "1d",
      "created": "2026-10-11T18:10:40Z",
      "expires": "2026-10-12T18:10:40Z",
      "units": 1
    }
  ],
  "units": 1
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Pages

POST/v1/pages

Reorder, repeat, delete, rotate, add blank pages to, or resize the pages of a PDF

Page numbers in order, keep_pages, delete and rotate refer to the input; insert_blank positions refer to the result. Deleted pages' content is removed from the file, not just hidden. Forms, links and metadata stay.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "delete": "2",
  "rotate": {
    "3": 90
  }
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/pages" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "delete": "2",
  "rotate": {
    "3": 90
  }
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "pages",
  "name": "contract-edited.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Stamp

POST/v1/stamp

Stamp text, an image, a QR code, a watermark or page numbers onto a PDF's pages

Each stamp goes at one of nine positions (top-left … bottom-right, inset by margin) or at x, y in points from the top-left of the page as displayed, so rotated and cropped pages come out right. Presets: watermark (large, faint, along the diagonal) and page_numbers ("{{page}} of {{pages}}", bottom centre). Text in any script: name a Google Fonts family in font, and text Helvetica cannot encode switches to Noto Sans on its own.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "stamps": [
    {
      "type": "watermark",
      "text": "DRAFT"
    },
    {
      "type": "page_numbers",
      "pages": "2-",
      "start": 2
    },
    {
      "type": "qr",
      "data": "https://example.com/verify/1042",
      "position": "top-right",
      "size": 64
    }
  ]
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/stamp" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "stamps": [
    {
      "type": "watermark",
      "text": "DRAFT"
    },
    {
      "type": "page_numbers",
      "pages": "2-",
      "start": 2
    },
    {
      "type": "qr",
      "data": "https://example.com/verify/1042",
      "position": "top-right",
      "size": 64
    }
  ]
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "stamp",
  "name": "contract-stamped.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1,
  "stamped": 25
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Fill

POST/v1/fill

Fill a PDF's form fields by name, or place values on any PDF with overlay boxes (or use a saved template), then flatten

data fills the form's own fields (POST /v1/extract with include fields lists their names, types and options) and any overlay fields you place by page and position. Text in any script works: values Helvetica cannot encode get Noto Sans, or the Google Fonts family in font. The answer lists what was filled, names that matched nothing (unknown) and values a field would not take (rejected, with why) — a bad value never fails the whole fill.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "data": {
    "full_name": "Ada Lovelace",
    "agree": true,
    "plan": "Pro",
    "signed_on": "2026-10-11"
  },
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ]
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/fill" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "data": {
    "full_name": "Ada Lovelace",
    "agree": true,
    "plan": "Pro",
    "signed_on": "2026-10-11"
  },
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ]
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "fill",
  "name": "application-filled.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1,
  "fields": {
    "filled": [
      "full_name",
      "agree",
      "plan",
      "signed_on"
    ],
    "unknown": [],
    "rejected": [],
    "xfa_removed": false,
    "flattened": true
  }
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Templates

GET/v1/templates

List this account's templates, most recently changed first

Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/templates" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "templates": [
    {
      "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
      "name": "Membership application",
      "kind": "form",
      "pages": 2,
      "fields": 1,
      "updated": "2026-10-11T18:10:40Z"
    }
  ]
}
429Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

POST/v1/templates

Save a form template: a PDF plus where each value goes, so fills need only its id and the data

The PDF's own form fields are found and listed (form_fields); fields adds overlay boxes for blanks that are just lines on the page. An encrypted source is opened once with password and stored without it. Free; kept until deleted, up to 500 per account.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "name": "Membership application",
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ]
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/templates" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "name": "Membership application",
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ]
}'
RESPONSE
201Created
{
  "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
  "object": "template",
  "kind": "form",
  "name": "Membership application",
  "pages": 2,
  "bytes": 48211,
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ],
  "form_fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "plan",
      "type": "dropdown",
      "options": [
        "Basic",
        "Pro"
      ]
    }
  ],
  "defaults": {
    "flatten": true
  },
  "created": "2026-10-11T18:10:40Z",
  "updated": "2026-10-11T18:10:40Z"
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

GET/v1/templates/{id}

One template: its overlay fields, the PDF's own form fields, and its defaults

PARAMETERS
idpath · stringREQUIRED
The template id
e.g. tpl_3h8kq2wzm4tnb7hdc3vr
Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
  "object": "template",
  "kind": "form",
  "name": "Membership application",
  "pages": 2,
  "bytes": 48211,
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ],
  "form_fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "plan",
      "type": "dropdown",
      "options": [
        "Basic",
        "Pro"
      ]
    }
  ],
  "defaults": {
    "flatten": true
  },
  "created": "2026-10-11T18:10:40Z",
  "updated": "2026-10-11T18:10:40Z"
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

PUT/v1/templates/{id}

Change a template's name, fields or defaults, or give it a new source PDF

PARAMETERS
idpath · stringREQUIRED
The template id
e.g. tpl_3h8kq2wzm4tnb7hdc3vr
REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "name": "Membership application v2"
}
Open on its own page
REQUEST
curl -X PUT "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Membership application v2"
}'
RESPONSE
200OK
{
  "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
  "object": "template",
  "kind": "form",
  "name": "Membership application",
  "pages": 2,
  "bytes": 48211,
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ],
  "form_fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "plan",
      "type": "dropdown",
      "options": [
        "Basic",
        "Pro"
      ]
    }
  ],
  "defaults": {
    "flatten": true
  },
  "created": "2026-10-11T18:10:40Z",
  "updated": "2026-10-11T18:10:40Z"
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

DELETE/v1/templates/{id}

Delete a template and its stored source PDF

PARAMETERS
idpath · stringREQUIRED
The template id
e.g. tpl_3h8kq2wzm4tnb7hdc3vr
Open on its own page
REQUEST
curl -X DELETE "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
  "object": "template",
  "deleted": true
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

GET/v1/templates/{id}/source

The template's source PDF, as stored (unlocked)

PARAMETERS
idpath · stringREQUIRED
The template id
e.g. tpl_3h8kq2wzm4tnb7hdc3vr
Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr/source" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200The PDF
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Protect

POST/v1/protect

Encrypt a PDF with AES-256: a password to open it, and permissions (print, copy, edit)

Without owner_password, a random one is set and not returned, so the permissions cannot be lifted. Chain after other operations by passing the earlier result's id.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "user_password": "open-sesame",
  "permissions": {
    "copy": false,
    "modify": false
  }
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/protect" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "user_password": "open-sesame",
  "permissions": {
    "copy": false,
    "modify": false
  }
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "protect",
  "name": "contract-protected.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1,
  "encrypted": true,
  "algorithm": "AES-256",
  "opens_with_password": true
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Unlock

POST/v1/unlock

Remove the password from a PDF whose password you have, giving a plain copy

Send the password as password (or file.password). It does not guess or crack passwords. A file whose only protection is an owner password (it opens without one) unlocks without it, as any viewer opens it.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "password": "open-sesame"
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/unlock" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "password": "open-sesame"
}'
RESPONSE
200With response=binary: the file itself, with X-File-Id and X-File-Url headers
201Created
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "kind": "result",
  "operation": "unlock",
  "name": "contract-unlocked.pdf",
  "format": "pdf",
  "content_type": "application/pdf",
  "pages": 12,
  "bytes": 284113,
  "url": "https://files.pdfedge.com/f/fil_8kq2wzm4tnb7hdc3vrex.pdf",
  "keep": "1d",
  "created": "2026-10-11T18:10:40Z",
  "expires": "2026-10-12T18:10:40Z",
  "units": 1,
  "encrypted": false,
  "was_encrypted": true,
  "had_open_password": true
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Extract

POST/v1/extract

Read a PDF: text per page, markdown for LLMs, form fields with their options, and document facts

Pick what to read with include. Pages with no text layer (scans) are listed in ocr_needed instead of coming back as silent empty strings; OCR is not offered yet. Use include fields before POST /v1/fill to learn a form's field names. Nothing is stored.

REQUEST BODY · APPLICATION/JSON · REQUIRED
{
  "file": "https://example.com/contracts/acme-msa.pdf",
  "include": [
    "markdown",
    "meta"
  ]
}
Open on its own page
REQUEST
curl -X POST "https://api.pdfedge.com/v1/extract" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "https://example.com/contracts/acme-msa.pdf",
  "include": [
    "markdown",
    "meta"
  ]
}'
RESPONSE
200OK
{
  "object": "extraction",
  "source": "fil_8kq2wzm4tnb7hdc3vrex",
  "pages": 2,
  "text": [
    {
      "page": 1,
      "text": "Master Services Agreement\nBetween Acme Inc. and …"
    },
    {
      "page": 2,
      "text": ""
    }
  ],
  "ocr_needed": [
    2
  ],
  "meta": {
    "title": "Master Services Agreement",
    "author": "Acme Legal",
    "pages": 2,
    "page_sizes": [
      {
        "page": 1,
        "width": 612,
        "height": 792,
        "rotation": 0
      }
    ],
    "pdf_version": "1.7",
    "encrypted": false,
    "form": false,
    "xfa": false,
    "tagged": true
  },
  "units": 1
}
404No such file on this account, or it has expired
{
  "error": "string",
  "message": "string",
  "param": "string"
}
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.

Fonts

GET/v1/fonts

Fonts for stamps and fills: the 14 standard PDF fonts and a curated Google Fonts list

Any Google Fonts family works by name in font; this list is a curated set with the weights each has. Standard fonts need no embedding but only draw Latin-1 text; anything else switches to Noto Sans on its own.

Open on its own page
REQUEST
curl "https://api.pdfedge.com/v1/fonts" \
  -H "X-API-Key: YOUR_KEY"
RESPONSE
200OK
{
  "standard": [
    "Helvetica",
    "Helvetica-Bold",
    "Times-Roman",
    "Courier"
  ],
  "google": [
    {
      "name": "Inter",
      "category": "sans-serif",
      "weights": "100..900"
    }
  ],
  "fallback": "Noto Sans"
}
429Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)
Try it — sign in first
Runs on a sandbox key: 200 requests a month. Your real key is for production.