# pdfedge API reference

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.

Base URL: `https://api.pdfedge.com/`. Send your key in the `X-API-Key` header; the remaining allowance comes back on every response in `X-Gw-Remaining`.

MCP server: `https://mcp.pdfedge.com/` (bearer token = the same key).

## Errors every operation can return

- `400` A parameter is missing, malformed or out of range

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

- `401` Missing or invalid API key (from the gateway)

# Files

# 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

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `kind` | query | enum | no | Only uploads, or only results One of: upload, result |

### Responses

- `200` OK

```json
{
  "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
    }
  ]
}
```

- `429` Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)

### Example

```bash
curl "https://api.pdfedge.com/v1/files" \
  -H "X-API-Key: YOUR_KEY"
```

## 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)

```json
{
  "url": "https://example.com/contracts/acme-msa.pdf",
  "name": "acme-msa.pdf",
  "keep": "7d"
}
```

### Responses

- `201` Created

```json
{
  "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
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
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"
}'
```

## GET /v1/files/{id}

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

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The file id Example: fil_8kq2wzm4tnb7hdc3vrex |

### Responses

- `200` OK

```json
{
  "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
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
curl "https://api.pdfedge.com/v1/files/fil_8kq2wzm4tnb7hdc3vrex" \
  -H "X-API-Key: YOUR_KEY"
```

## DELETE /v1/files/{id}

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

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The file id Example: fil_8kq2wzm4tnb7hdc3vrex |

### Responses

- `200` OK

```json
{
  "id": "fil_8kq2wzm4tnb7hdc3vrex",
  "object": "file",
  "deleted": true
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
curl -X DELETE "https://api.pdfedge.com/v1/files/fil_8kq2wzm4tnb7hdc3vrex" \
  -H "X-API-Key: YOUR_KEY"
```

# Pdfs

# 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)

```json
{
  "images": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    "https://example.com/receipt-2.jpg"
  ],
  "size": "A4",
  "margin": 24
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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
}'
```

# Pages

# 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)

```json
{
  "files": [
    "fil_8kq2wzm4tnb7hdc3vrex",
    {
      "url": "https://example.com/terms.pdf",
      "pages": "1-2"
    }
  ],
  "name": "contract-with-terms.pdf"
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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"
}'
```

# 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)

```json
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "ranges": [
    "1-2",
    "3-"
  ]
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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-"
  ]
}'
```

# 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)

```json
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "delete": "2",
  "rotate": {
    "3": 90
  }
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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
  }
}'
```

# Stamps

# 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)

```json
{
  "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
    }
  ]
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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
    }
  ]
}'
```

# Forms

# 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)

```json
{
  "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
    }
  ]
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
  }
}
```

### Example

```bash
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
    }
  ]
}'
```

# Templates

# Templates

## GET /v1/templates

**List this account's templates, most recently changed first**

### Responses

- `200` OK

```json
{
  "templates": [
    {
      "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
      "name": "Membership application",
      "kind": "form",
      "pages": 2,
      "fields": 1,
      "updated": "2026-10-11T18:10:40Z"
    }
  ]
}
```

- `429` Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)

### Example

```bash
curl "https://api.pdfedge.com/v1/templates" \
  -H "X-API-Key: YOUR_KEY"
```

## 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)

```json
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "name": "Membership application",
  "fields": [
    {
      "name": "signed_on",
      "page": 2,
      "x": 360,
      "y": 702,
      "width": 160,
      "height": 18
    }
  ]
}
```

### Responses

- `201` Created

```json
{
  "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"
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
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
    }
  ]
}'
```

## GET /v1/templates/{id}

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

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The template id Example: tpl_3h8kq2wzm4tnb7hdc3vr |

### Responses

- `200` OK

```json
{
  "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"
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
curl "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr" \
  -H "X-API-Key: YOUR_KEY"
```

## PUT /v1/templates/{id}

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

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The template id Example: tpl_3h8kq2wzm4tnb7hdc3vr |

### Request body (application/json)

```json
{
  "name": "Membership application v2"
}
```

### Responses

- `200` OK

```json
{
  "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"
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
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"
}'
```

## DELETE /v1/templates/{id}

**Delete a template and its stored source PDF**

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The template id Example: tpl_3h8kq2wzm4tnb7hdc3vr |

### Responses

- `200` OK

```json
{
  "id": "tpl_3h8kq2wzm4tnb7hdc3vr",
  "object": "template",
  "deleted": true
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
curl -X DELETE "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr" \
  -H "X-API-Key: YOUR_KEY"
```

## GET /v1/templates/{id}/source

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

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `id` | path | string | yes | The template id Example: tpl_3h8kq2wzm4tnb7hdc3vr |

### Responses

- `200` The PDF
- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
curl "https://api.pdfedge.com/v1/templates/tpl_3h8kq2wzm4tnb7hdc3vr/source" \
  -H "X-API-Key: YOUR_KEY"
```

# Security

# 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)

```json
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "user_password": "open-sesame",
  "permissions": {
    "copy": false,
    "modify": false
  }
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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
  }
}'
```

# 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)

```json
{
  "file": "fil_8kq2wzm4tnb7hdc3vrex",
  "password": "open-sesame"
}
```

### Responses

- `200` With response=binary: the file itself, with X-File-Id and X-File-Url headers
- `201` Created

```json
{
  "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
}
```

### Example

```bash
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"
}'
```

# Extract

# 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)

```json
{
  "file": "https://example.com/contracts/acme-msa.pdf",
  "include": [
    "markdown",
    "meta"
  ]
}
```

### Responses

- `200` OK

```json
{
  "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
}
```

- `404` No such file on this account, or it has expired

```json
{
  "error": "string",
  "message": "string",
  "param": "string"
}
```

### Example

```bash
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"
  ]
}'
```

# Fonts

# 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.

### Responses

- `200` OK

```json
{
  "standard": [
    "Helvetica",
    "Helvetica-Bold",
    "Times-Roman",
    "Courier"
  ],
  "google": [
    {
      "name": "Inter",
      "category": "sans-serif",
      "weights": "100..900"
    }
  ],
  "fallback": "Noto Sans"
}
```

- `429` Plan ceiling or rate limit reached; see X-Gw-Reset (from the gateway)

### Example

```bash
curl "https://api.pdfedge.com/v1/fonts" \
  -H "X-API-Key: YOUR_KEY"
```
