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/public

Authentication

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.

StatusCodeMeaning
400tool_size_and_type_requiredA required field is missing or invalid.
400invalid_webhook_urlThe webhook URL is not HTTPS, or points to a private network.
401invalid_api_keyMissing, unknown or revoked key.
402insufficient_balanceYour balance does not cover one minute of the tool's processing.
403tool_not_availableThe tool is not open to the API, or closed to your account.
404—No such job for this key.
429rate_limitedMore 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.

ToolPrice per secondAccepts

List the tools

GET/tools

The 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

GET/balance

Your 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

POST/jobs

Declares 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

  • toolstringrequired

    A tool id from /tools.

  • sizeintegerrequired

    File size, in bytes.

  • typestringrequired

    Media 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.docx

Response 201

{
  "id": "6f1c…",
  "upload": {
    "url": "https://…signed…",
    "method": "PUT",
    "headers": { "content-type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }
  }
}

Start a job

POST/jobs/{id}/start

Starts the job once its file is uploaded. Options are checked against the tool's options; missing ones take their default.

Path parameters

  • iduuidrequired

    The job id.

Body

  • optionsobject

    The 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

GET/jobs/{id}

The job's status: upload, queued (with its position), processing, succeeded, failed (with an error code) or expired.

Path parameters

  • iduuidrequired

    The 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

POST/jobs/{id}/result

Returns 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

  • iduuidrequired

    The 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
}