Developers
Toolty API reference
Convert Word, Excel and PowerPoint to PDF, PDF to Word, and make scanned PDFs searchable from your own code. Open to every account: billed per second of processing, from a prepaid balance topped up from $5.
Introduction
The API is REST over HTTPS, with JSON bodies. Files never go through the API itself: you upload to, and download from, short-lived signed URLs. A job goes through four calls: open, upload, start, fetch the result.
Base URL
https://toolty.io/api/v1/publicAuthentication
Send your key in the Authorization header. Create keys in your account, under API: a key is shown once, only its fingerprint is kept, and you can revoke it at any time.
Authorization: Bearer tk_live_…Errors
Errors return an HTTP status and a JSON body with an error code.
| Status | Code | Meaning |
|---|---|---|
| 400 | tool_size_and_type_required | A required field is missing or invalid. |
| 400 | invalid_webhook_url | The webhook URL is not HTTPS, or points to a private network. |
| 401 | invalid_api_key | Missing, unknown or revoked key. |
| 402 | insufficient_balance | Your balance does not cover one minute of the tool's processing. |
| 403 | tool_not_available | The tool is not open to the API, or closed to your account. |
| 404 | — | No such job for this key. |
| 429 | rate_limited | More than 60 calls in a minute with this key. |
Rate limits and files
60 calls per minute per key, two jobs running at once. Uploaded files and results are deleted at most one hour after processing.
Webhooks
Pass a webhook HTTPS URL when you start a job. When it ends, we POST the job to it, signed with your key's webhook secret (shown in your account). We retry after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h until we get a 2xx answer. With Zapier, Make or n8n, use their generic HTTP modules and a “Catch hook” trigger.
POST · job.completed
{
"id": "1d7e…",
"type": "job.completed",
"createdAt": "2026-11-05T14:42:10.000Z",
"data": {
"job": { "id": "6f1c…", "tool": "word-to-pdf", "status": "succeeded", "result": { "size": 51022, "expiresAt": "2026-11-05T15:42:00.000Z" } }
}
}Verifying the signature
import crypto from "node:crypto"
// header = req.headers["toolty-signature"] → "t=1762351200,v1=5f2c…"
function verify(header, rawBody, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")))
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300
return fresh && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}Tools and prices
Every tool the API will cover. Each delivered result is billed per second of processing, from the moment a worker starts the job to its end, rounded up to the next second, at the tool's price per second. Failed jobs are not billed. Opening or starting a job requires a balance covering one minute of the tool's processing. A tool marked Soon is documented but not callable yet, and a job opened with it is refused with tool_not_available.
| Tool | Price per second | Accepts |
|---|
List the tools
/toolsThe tools open to the API, their price per result, accepted formats, size limit and options.
curl https://toolty.io/api/v1/public/tools \
-H "Authorization: Bearer tk_live_…"Response 200
{
"tools": [
{
"id": "word-to-pdf",
"title": "Word to PDF",
"pricePerSecondUsd": 0.001714,
"accepts": ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
"maxSizeMb": 50,
"options": {}
}
]
}Read the balance
/balanceYour prepaid balance, in US dollars.
curl https://toolty.io/api/v1/public/balance \
-H "Authorization: Bearer tk_live_…"Response 200
{
"balanceUsd": 12.5
}Open a job
/jobsDeclares the file to process and returns the signed URL where to upload it. The balance must cover one result. One open upload per key at a time: a new job closes the previous one if its file was never uploaded.
Body
toolstringrequiredA tool id from
/tools.sizeintegerrequiredFile size, in bytes.
typestringrequiredMedia type of the file, among the tool's
accepts.
curl -X POST https://toolty.io/api/v1/public/jobs \
-H "Authorization: Bearer tk_live_…" \
-H "Content-Type: application/json" \
-d '{"tool":"word-to-pdf","size":48213,"type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}'
# Then upload the file to the signed URL
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
--data-binary @report.docxResponse 201
{
"id": "6f1c…",
"upload": {
"url": "https://…signed…",
"method": "PUT",
"headers": { "content-type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }
}
}Start a job
/jobs/{id}/startStarts the job once its file is uploaded. Options are checked against the tool's options; missing ones take their default.
Path parameters
iduuidrequiredThe job id.
Body
optionsobjectThe tool's options, by id.
webhookstring (https)Called when the job ends. See Webhooks.
curl -X POST https://toolty.io/api/v1/public/jobs/$JOB_ID/start \
-H "Authorization: Bearer tk_live_…" \
-H "Content-Type: application/json" \
-d '{"webhook":"https://example.com/hooks/toolty"}'Response 200
{
"id": "6f1c…",
"tool": "word-to-pdf",
"status": "queued"
}Read a job
/jobs/{id}The job's status: upload, queued (with its position), processing, succeeded, failed (with an error code) or expired.
Path parameters
iduuidrequiredThe job id.
curl https://toolty.io/api/v1/public/jobs/$JOB_ID \
-H "Authorization: Bearer tk_live_…"Response 200
{
"id": "6f1c…",
"tool": "word-to-pdf",
"status": "succeeded",
"result": { "size": 51022, "expiresAt": "2026-11-05T15:42:00.000Z" }
}Fetch the result
/jobs/{id}/resultReturns a short-lived download URL. The first call charges the job's processing seconds to your balance and says how many (billedSeconds, chargedUsd); later calls return a fresh URL at no cost. The file is deleted at most one hour after processing.
Path parameters
iduuidrequiredThe job id.
curl -X POST https://toolty.io/api/v1/public/jobs/$JOB_ID/result \
-H "Authorization: Bearer tk_live_…"Response 200
{
"url": "https://…signed…",
"size": 51022,
"expiresAt": "2026-11-05T15:42:00.000Z",
"billedSeconds": 4,
"chargedUsd": 0.006856
}