The API lives at https://uvpdftool.com/api/v1. Requests and answers are JSON; a refused request answers with its status and a body like {"error":{"code":"API_CREDITS_SHORT","message":"тАж","details":{тАж}}}. The examples assume the key in UVPDF_KEY; the quick start shows the calls together.
GET/tools┬╖ List the tools
The tools the API offers now, with what each takes and gives, its limits and its options.
No key needed.
Needs no key. Each toolтАЩs options come as a JSON Schema, the same one the server checks a job against, so a client can validate before sending. The limits reflect the live plan; a test key is held to its own smaller file size, given beside the live one.
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X GET "https://uvpdftool.com/api/v1/tools"Response 200
The tools.
{
"tools": [
{
"id": "compress",
"accepts": [
"application/pdf"
],
"produces": "application/pdf",
"maxFiles": 20,
"maxBytes": {
"live": 2147483648,
"test": 10485760
},
"timeoutMinutes": 10,
"options": {
"type": "object",
"properties": {
"imageQuality": {
"type": "number",
"minimum": 0.3,
"maximum": 0.95
},
"method": {
"type": "string",
"enum": [
"engine",
"ghostscript"
]
}
}
}
}
]
}Errors
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
- 503 API_OFF The API is closed.
POST/jobs┬╖ Create a job
Makes a job and answers with a link to upload each file to.
Key scope jobs:write (not a read-only key).
Declare the tool, its options and each fileтАЩs name, size and content type. The answer carries the job and, for each file, where to send it: a PUT to that address with the headers given, within the next 20 minutes. Nothing is read until you start the job.
A live key spends one API task here, in the same transaction, and gets it back if the job fails, is cancelled or never gets its files. A test key takes one of the dayтАЩs test jobs instead.
Request body
| Name | Type | Description |
|---|---|---|
| toolrequired | string | One of the tool ids from GET /tools. |
| options | object | The toolтАЩs options; see the tool below. Unknown options are refused. |
| filesrequired | object[] | One to 20 files (an empty list for Web page to PDF), each with name (up to 255 characters), size in bytes (over 0) and contentType, which must be one the tool accepts. |
| retentionHours | integer 1тАУ24 | How long the files and results are kept once the job has run. Default 2. |
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X POST "https://uvpdftool.com/api/v1/jobs" \
-H "Authorization: Bearer $UVPDF_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "compress",
"options": {
"method": "ghostscript",
"preset": "ebook"
},
"files": [
{
"name": "report.pdf",
"size": 4821130,
"contentType": "application/pdf"
}
]
}'Response 201
The job, and for each file a link to PUT its bytes to with the headers to send.
{
"job": {
"id": "3f6c2c0e-6d1b-4f1c-9a2f-1d7e2b5a9c10",
"tool": "compress",
"mode": "live",
"state": "created",
"progress": null,
"error": null,
"outputs": [],
"createdAt": "2026-10-28T10:15:00.000Z",
"expiresAt": "2026-10-28T10:35:00.000Z"
},
"uploads": [
{
"name": "report.pdf",
"method": "PUT",
"url": "https://files.example.r2.cloudflarestorage.com/jobs/3f6cтАж/in/тАж?X-Amz-Signature=тАж",
"headers": {
"Content-Type": "application/pdf"
}
}
]
}Errors
- 400 INVALID_REQUEST The body or the options are not valid; the message says what.
- 401 API_KEY_MISSING No key in the Authorization header.
- 401 API_KEY_INVALID The key is unknown, revoked, or its account is closed.
- 403 API_SCOPE A read-only key tried to make, start or cancel a job.
- 402 API_CREDITS_SHORT No API tasks left on the account, with a live key.
- 413 FILE_TOO_LARGE A file is larger than the tool or the key takes; details.max says the limit in bytes.
- 429 API_TEST_LIMIT The 50 test jobs of the day are used.
- 429 API_TOO_MANY_OPEN 10 jobs are unfinished already.
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
- 503 TOOL_UNAVAILABLE The tool is not available for the moment.
- 503 MAINTENANCE The servers are paused for maintenance.
PUT{upload.url}┬╖ Upload a file
Sends one file to the link that POST /jobs gave for it, as it is, unchanged.
No key needed.
A plain PUT of the bytes to the uploadтАЩs url, with exactly the headers it lists (the content type). No API key: the link itself is the permission, and it works for 20 minutes. Send the file whole; a body larger than the size declared is refused when the job starts.
curl -X PUT --upload-file report.pdf \
-H "Content-Type: application/pdf" \
"$UPLOAD_URL"Response 200
An empty body from the storage service.
POST/jobs/{id}/start┬╖ Start a job
Checks that every file arrived and puts the job in line.
Key scope jobs:write (not a read-only key).
Each file is looked up in storage: one missing or empty answers UPLOAD_INCOMPLETE, one larger than declared FILE_TOO_LARGE. Then the job is queued; a server picks it up within seconds, or within half a minute after a quiet spell, when one has to be started first.
| Name | Type | Description |
|---|---|---|
| idrequired | uuid | The jobтАЩs id. |
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X POST "https://uvpdftool.com/api/v1/jobs/$JOB_ID/start" \
-H "Authorization: Bearer $UVPDF_KEY"Response 200
The job, queued.
{
"job": {
"id": "3f6c2c0e-6d1b-4f1c-9a2f-1d7e2b5a9c10",
"tool": "compress",
"mode": "live",
"state": "queued",
"progress": null,
"error": null,
"outputs": [],
"createdAt": "2026-10-28T10:15:00.000Z",
"expiresAt": "2026-10-29T10:15:00.000Z"
}
}Errors
- 400 UPLOAD_INCOMPLETE A file has not finished uploading, or arrived empty.
- 401 API_KEY_MISSING No key in the Authorization header.
- 404 NOT_FOUND No such job for this account.
- 409 ALREADY_STARTED The job was started already.
- 413 FILE_TOO_LARGE A file is larger than the tool or the key takes; details.max says the limit in bytes.
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
GET/jobs/{id}┬╖ Read a job
The job as it stands; once done, each output carries a short-lived download link.
Key scope jobs:read (any key).
Ask every few seconds until state is done, failed or cancelled, or let the webhook tell you. A download link is good for 15 minutes; read the job again for a fresh one. progress is 0 to 1 while the job runs, where the tool reports it.
| Name | Type | Description |
|---|---|---|
| idrequired | uuid | The jobтАЩs id. |
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X GET "https://uvpdftool.com/api/v1/jobs/$JOB_ID" \
-H "Authorization: Bearer $UVPDF_KEY"Response 200
The job.
{
"job": {
"id": "3f6c2c0e-6d1b-4f1c-9a2f-1d7e2b5a9c10",
"tool": "compress",
"mode": "live",
"state": "done",
"progress": 1,
"error": null,
"outputs": [
{
"name": "report.pdf",
"size": 1102337,
"contentType": "application/pdf",
"url": "https://files.example.r2.cloudflarestorage.com/jobs/3f6cтАж/out/тАж?X-Amz-Signature=тАж"
}
],
"createdAt": "2026-10-28T10:15:00.000Z",
"expiresAt": "2026-10-29T10:16:12.000Z"
}
}Errors
- 401 API_KEY_MISSING No key in the Authorization header.
- 401 API_KEY_INVALID The key is unknown, revoked, or its account is closed.
- 404 NOT_FOUND No such job for this account.
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
DELETE/jobs/{id}┬╖ Cancel or delete a job
Stops the job if it is still going and deletes its files and results now.
Key scope jobs:write (not a read-only key).
A live task comes back unless the job already succeeded. The job record stays readable, as cancelled, so a webhook delivery about it can still be matched.
| Name | Type | Description |
|---|---|---|
| idrequired | uuid | The jobтАЩs id. |
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X DELETE "https://uvpdftool.com/api/v1/jobs/$JOB_ID" \
-H "Authorization: Bearer $UVPDF_KEY"Response 200
The job, cancelled.
{
"job": {
"id": "3f6c2c0e-6d1b-4f1c-9a2f-1d7e2b5a9c10",
"tool": "compress",
"mode": "live",
"state": "cancelled",
"progress": null,
"error": null,
"outputs": [],
"createdAt": "2026-10-28T10:15:00.000Z",
"expiresAt": "2026-10-28T10:35:00.000Z"
}
}Errors
- 401 API_KEY_MISSING No key in the Authorization header.
- 403 API_SCOPE A read-only key tried to make, start or cancel a job.
- 404 NOT_FOUND No such job for this account.
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
GET/account┬╖ Read the account
The key in use, the API tasks left, and what is left of todayтАЩs test jobs.
Key scope jobs:read (any key).
A way to check a key and to watch the balance before a batch. The keyтАЩs prefix is the only part of it anyone sees again.
# Once, in your shell: the key never goes into a script.
export UVPDF_KEY="uvp_live_..."
curl -X GET "https://uvpdftool.com/api/v1/account" \
-H "Authorization: Bearer $UVPDF_KEY"Response 200
Usage.
{
"key": {
"name": "Production",
"mode": "live",
"prefix": "uvp_live_k3Jd",
"scopes": [
"jobs:read",
"jobs:write"
]
},
"tasks": 982,
"test": {
"left": 50,
"perDay": 50
}
}Errors
- 401 API_KEY_MISSING No key in the Authorization header.
- 401 API_KEY_INVALID The key is unknown, revoked, or its account is closed.
- 429 TOO_MANY_REQUESTS More than 120 requests in a minute with one key.
GET/openapi.json┬╖ The OpenAPI description
The API described in OpenAPI 3.1, for the tools that read one: code generators, Swagger UI, Postman, request validators. Written from the same definitions the server checks against. No key needed.
curl https://uvpdftool.com/api/v1/openapi.jsonOpen it, or read how to generate a client from it.
GET/postman.json┬╖ The Postman collection
Every call as a Postman collection (v2.1), with the key as a collection variable and the job id carried from one request to the next. No key needed.
curl -o uvpdftool.postman_collection.json https://uvpdftool.com/api/v1/postman.jsonDownload it, then in Postman: Import, and set apiKey in the collectionтАЩs variables. More on the SDKs and tools page.