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.
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(orboxes=true) to receiveboxeson 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
usagein 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.
| HTTP | error | Action |
|---|---|---|
| 401 | invalid_api_key | Check Bearer key or create a new one |
| 400 | unknown_tool | Wrong tool; response includes allowed |
| 400 | file_required | Missing file or image_base64 |
| 400 | file_too_large | Over 12 MB |
| 429 | rate_limited | Wait Retry-After seconds |
| 429 | quota_exceeded | Monthly limit — upgrade plan |
| 502 | ocr_failed | Retry later |
| 503 | ocr_unavailable | Service 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;
}