Toolsy

OCR API

Need handwriting, receipts, or forms from your own software? Sign up — you get a key and 40 recognitions per month without a card.

Create accountAPI plansSite widgetOCR solutionsStatusOpenAPIPostman

How to call

One endpoint. Send your key in Authorization: Bearer ….

POST https://toolsy.tools/api/v1/ocr
Authorization: Bearer YOUR_KEY

multipart/form-data:
  file — image or PDF
  tool — recognition type (see below)
  grounding — optional true for evidence-review boxes

or application/json:
  { "tool": "…", "image_base64": "…", "mime": "image/jpeg", "grounding": true }

Limits & formats

  • Files: JPG, PNG, WebP, GIF, PDF. Max 12 MB per request.
  • PDF: first 20 pages processed; longer files get a trim note in the text.
  • Grounding: pass grounding=true (or boxes=true) to receive boxes on a 0–1000 grid for evidence review overlays.
  • Timeout: up to 120 seconds per request.
  • Rate: 60 requests per minute per key. Over limit → 429 and Retry-After: 60.
  • Monthly quota: by plan (see usage in the JSON body and response headers).

Supported tools

OCR from photos and PDF. Word→Markdown and generic image tools stay on the free web pages — not in the API.

  • Handwriting → texttool=handwriting-to-text
  • Handwriting → LaTeXtool=handwriting-to-latex
  • Handwriting → tabletool=handwriting-to-table
  • Table photo → CSVtool=table-photo-to-excel
  • Receipt → CSVtool=receipt-to-excel
  • Packing slip → CSVtool=packing-slip-to-excel
  • Invoice → CSVtool=invoice-to-excel
  • Business card → CSVtool=business-card-to-excel
  • Doctor handwritingtool=doctor-handwriting
  • Historical manuscripttool=historical-manuscript
  • Form → JSONtool=form-to-json
  • Whiteboard → texttool=whiteboard-to-text
  • Screenshot → HTMLtool=screenshot-to-code
  • Screenshot → texttool=screenshot-to-text
  • Meter readingtool=meter-reading
  • Notes → Markdowntool=notes-to-markdown

Response

Success is HTTP 200 with a text field. Table tools return CSV; forms may return JSON as a string.

{
  "text": "recognized text or CSV…",
  "tool": "handwriting-to-text",
  "usage": {
    "used": 12,
    "quota": 500,
    "remaining": 488,
    "plan": "starter",
    "month": "2026-08"
  }
}

Headers: X-RateLimit-Limit, X-RateLimit-Used, X-RateLimit-Remaining.

Error codes

Body is JSON with an error string.

HTTPerrorAction
401invalid_api_keyCheck Bearer key or create a new one
400unknown_toolWrong tool; response includes allowed
400file_requiredMissing file or image_base64
400file_too_largeOver 12 MB
429rate_limitedWait Retry-After seconds
429quota_exceededMonthly limit — upgrade plan
502ocr_failedRetry later
503ocr_unavailableService temporarily down

Code examples

Copy a snippet below. Keys live in your account; do not ship them to browsers.

curl

curl -X POST https://toolsy.tools/api/v1/ocr \
  -H "Authorization: Bearer YOUR_KEY" \
  -F "tool=handwriting-to-text" \
  -F "file=@note.jpg"

PHP

cURL. Keep the key in an environment variable, not in git.

<?php
$key = getenv('TOOLSY_API_KEY');
$ch = curl_init('https://toolsy.tools/api/v1/ocr');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer $key"],
  CURLOPT_POSTFIELDS => [
    'tool' => 'receipt-to-excel',
    'file' => new CURLFile('/path/to/receipt.jpg', 'image/jpeg', 'receipt.jpg'),
  ],
]);
$res = curl_exec($ch);
curl_close($ch);
$data = json_decode($res, true);
echo $data['text'] ?? $data['error'] ?? 'empty';

Python

requests. For table tools the text field is CSV.

import os
import requests

r = requests.post(
    "https://toolsy.tools/api/v1/ocr",
    headers={"Authorization": f"Bearer {os.environ['TOOLSY_API_KEY']}"},
    files={"file": open("receipt.jpg", "rb")},
    data={"tool": "receipt-to-excel"},
    timeout=60,
)
r.raise_for_status()
print(r.json()["text"])
print("remaining:", r.headers.get("X-RateLimit-Remaining"))

Node.js

Built-in fetch (Node 18+) and FormData.

import fs from "node:fs";

const form = new FormData();
form.set("tool", "handwriting-to-text");
form.set("file", new Blob([fs.readFileSync("note.jpg")]), "note.jpg");

const res = await fetch("https://toolsy.tools/api/v1/ocr", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.TOOLSY_API_KEY}` },
  body: form,
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || res.statusText);
console.log(data.text);

Browser JavaScript

Never expose the API key in public front-end code — proxy through your backend.

async function recognize(file) {
  const form = new FormData();
  form.set("tool", "receipt-to-excel");
  form.set("file", file);

  const res = await fetch("https://toolsy.tools/api/v1/ocr", {
    method: "POST",
    headers: { Authorization: "Bearer YOUR_KEY" },
    body: form,
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || "ocr_failed");
  return data.text;
}

Google Apps Script

Handy when your workflow lives in Google Sheets. JSON + base64.

function recognizeDriveFile(fileId) {
  var key = PropertiesService.getScriptProperties().getProperty('TOOLSY_API_KEY');
  var blob = DriveApp.getFileById(fileId).getBlob();
  var res = UrlFetchApp.fetch('https://toolsy.tools/api/v1/ocr', {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + key },
    payload: JSON.stringify({
      tool: 'receipt-to-excel',
      mime: blob.getContentType() || 'image/jpeg',
      filename: blob.getName(),
      image_base64: Utilities.base64Encode(blob.getBytes()),
    }),
    muteHttpExceptions: true,
  });
  var data = JSON.parse(res.getContentText());
  return data.text || data.error;
}
OCR API Documentation — Toolsy