# Handwriting to Text OCR: Notes, Letters, Forms & PDFs (`bossless/handwriting-to-text`) Actor

Handwriting to text OCR for handwritten notes, letters, forms, whiteboards and historical documents. Photos, scans, PDFs and HEIC iPhone photos in any language, from direct, Google Drive or Dropbox links. Exact text line by line, one record per page. No API key. Pay per page.

- **URL**: https://apify.com/bossless/handwriting-to-text.md
- **Developed by:** [Alexander Daskalov](https://apify.com/bossless) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page transcribeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Handwriting to Text OCR: Notes, Letters, Forms & PDFs

**Turn photos, scans and PDFs of handwriting into text you can copy, search and edit, page by page.** Paste links to your files (JPG, PNG, HEIC iPhone photos, PDF…, or Google Drive and Dropbox file links) and get, for every page:

- ✍️ **Exact transcription with line breaks**: the text is copied exactly as written, line by line, in the original spelling. Nothing is corrected, summarised or translated.
- 🌍 **Any language**: including non-Latin scripts such as Cyrillic, Greek, Arabic, Chinese, Japanese and Hindi, with the detected language as a BCP-47 code.
- ❌ **Crossed-out words dropped, unreadable words marked**: words that were struck through are left out, and words that can't be read are marked `[illegible]` and counted.
- 📄 **PDFs and multi-page TIFFs page by page**: one record per page, with the page number and page count.
- 📱 **HEIC iPhone photos and auto-rotation**: phone photos are turned the right way up from their orientation data before reading.
- 🔢 **Word and line counts** per page, counted by code from the text.

No API key, no account to connect, no setup. It works for notes, notebooks, letters, forms, whiteboards and old documents.

### What can you do with it?

- **Digitise meeting notes and notebooks:** make handwritten notes searchable and paste them into your notes app or task list.
- **Letters and family archives:** transcribe old letters, postcards and diaries so the whole family can read and keep them.
- **Handwritten forms and surveys:** get the answers into text for spreadsheets, CRMs and databases.
- **Whiteboard photos:** keep what was written in a workshop or class as text instead of a blurry photo.
- **Historical and genealogy documents:** a first transcription of registers, minutes and records to proofread and search.
- **Exam answer sheets:** prepare handwritten answers as text for teachers to read and grade faster.
- **AI assistants and RAG:** feed handwritten notes into your AI assistant, search index or knowledge base.

### What files does it accept?

| Input | Example | Supported |
|---|---|---|
| Direct image file link | `https://example.com/scans/letter.jpg` | ✅ JPG, PNG, WebP, HEIC/HEIF (iPhone), TIFF incl. multi-page, GIF, BMP |
| Direct PDF link | `https://example.com/scans/notebook.pdf` | ✅ scanned or photographed pages, rendered at 200 dpi |
| Cloud storage file URL | a public file URL from S3 or another bucket | ✅ if it opens without logging in |
| Google Drive file | `https://drive.google.com/file/d/…/view` | ✅ shared as "Anyone with the link" |
| Dropbox file | `https://www.dropbox.com/scl/fi/…?dl=0` | ✅ |
| Folders, or pages that need a login | | ❌ share each file's own link |

By default up to 20 pages are transcribed per file; raise **Max pages per file** (up to 200) for long PDFs, or lower it to cap the cost of long ones.

### How much does it cost?

Pay per page. No monthly fee.

| Event | Price (FREE/BRONZE) | Lower on paid plans |
|---|---|---|
| Page transcribed (text, language, handwritten flag, counts) | **$0.005** / page | down to $0.004 |
| Actor start | $0.00005 / run | — |

100 pages cost **$0.50**; a 20-page PDF costs **$0.10**. You pay only for pages actually transcribed. Nothing is charged for a page or file that fails.

**Cap your spend:** set a **maximum cost per run**. When the budget runs out, no more pages are transcribed.

**Free plan:** the AI runs on paid infrastructure, so free-plan runs include a 3-page trial per run; upgrade to transcribe more pages.

### How to convert handwriting to text

1. Click **Try for free**. The example input transcribes a sample handwritten note.
2. Paste your own links into **Photo, scan or PDF links**, one per line.
3. Optional: set **Max pages per file** for long PDFs.
4. Run, then open the **Pages** or **Text** view, or download JSON, CSV or Excel.

#### Input example

```json
{
  "fileUrls": [
    "https://example.com/scans/letter.jpg",
    "https://example.com/scans/notebook.pdf"
  ],
  "maxPagesPerFile": 50
}
```

### Output

One record per page. A page of meeting notes:

```json
{
 "url": "https://example.com/scans/notes.pdf",
 "source": "direct",
 "page": 1,
 "pageCount": 2,
 "text": "Team sync - Tuesday 14 Oct\n- Launch moved to Nov 3 (waiting on legal)\n- Maria owns the pricing page copy\n- Budget: $12,400 left for Q4 ads\n- Ask Tom about the Berlin office lease\nNext call: Friday 9:30 am",
 "language": "en",
 "handwritten": true,
 "illegibleWords": 0,
 "lineCount": 6,
 "wordCount": 42,
 "processedAt": "2026-10-15T08:00:00+00:00"
}
```

`text` keeps the line breaks of the page. `handwritten` is true when most of the text on the page is handwritten. `illegibleWords`, `lineCount` and `wordCount` are computed by code from the text, not guessed by the AI.

### How accurate is it?

Measured with Google's Gemini 3.1 Flash-Lite model on:

- **4 synthetic handwritten notes** (made with handwriting fonts, with jitter, rotation and blur): every character right on all 4.
- **12 real phone photos of handwritten notes** from a public CC0 dataset: 11 word-for-word exact; the 12th differed only in capitalisation ("ocr" vs "OCR") after crossed-out words were left out.
- **A 1916 Danish handwritten minutes page** (old cursive, Aarhus City Archives, CC BY 4.0): about 84% of characters right.

Tips: use clear, well-lit photos, and one page per photo. For old scripts, expect to proofread.

### How fast is it?

Pages are transcribed 4 at a time. Measured on Apify at the default 1 GB: a single photo in about 8 seconds including start-up, and a 20-page PDF in 14 seconds.

### Use it with AI agents (MCP)

```
https://mcp.apify.com?tools=bossless/handwriting-to-text
```

Ask your assistant "what does this handwritten note say?", and it runs this actor and reads back the text.

### Use the API from code

```bash
curl -X POST "https://api.apify.com/v2/acts/bossless~handwriting-to-text/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileUrls": ["https://example.com/scans/letter.jpg"]}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("bossless/handwriting-to-text").call(run_input={
    "fileUrls": ["https://example.com/scans/notebook.pdf"],
    "maxPagesPerFile": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"--- page {item['page']} of {item['pageCount']} ({item['wordCount']} words)")
    print(item["text"])
```

### FAQ

**Do I need a Google, OpenAI or other API key?** No. Everything is included in the price.

**Which languages work?** Most languages and scripts, including Cyrillic, Greek, Arabic, Chinese, Japanese and Hindi. The text stays in the language it was written in, and `language` tells you which one was detected. Quality varies with the handwriting.

**Does it read printed text too?** Yes. Printed text on the page is transcribed as well; `handwritten` tells you whether handwriting or print dominates the page.

**What about tables and forms?** They come out as lines of text in reading order. Cell structure is not kept.

**What happens to crossed-out words?** They are left out, so the text reads the way the writer meant it.

**What if a word can't be read?** It's marked `[illegible]` in the text and counted in `illegibleWords`.

**What happens with a private or broken link?** It's skipped with a clear message and not charged.

**Is my data stored or used for training?** Files are downloaded only for the run and deleted when it ends. Results are saved in your own Apify dataset. The AI model runs on Google Cloud (Vertex AI). Google's terms say it won't use this data to train or fine-tune its models; it may cache requests for up to 24 hours and log them for abuse monitoring.

**How are pages counted?** One photo or image is one page; a PDF or multi-page TIFF counts each page transcribed, up to **Max pages per file**.

**Is this an official product of any provider?** No. This is an independent tool and is not affiliated with or endorsed by Google, Apple, Microsoft or any OCR provider.

### Related tools

- [Speaker Diarization & Transcription](https://apify.com/bossless/speaker-diarization): who spoke when in any audio or video, with a transcript with speaker names, SRT/VTT subtitles and RTTM.
- [Call Recording Transcription & AI Call Analysis](https://apify.com/bossless/call-transcription-analyzer): speaker-labelled call transcripts with an AI summary, sentiment, action items and a QA scorecard.

# Actor input Schema

## `fileUrls` (type: `array`):

Links to photos, scans or PDFs of handwriting: direct files (JPG, PNG, WebP, HEIC/HEIF iPhone photos, TIFF incl. multi-page, GIF, BMP, PDF) from your website or cloud storage, or Google Drive / Dropbox file links shared as 'Anyone with the link'. One file per line. Folders and pages that need a login don't work.

## `maxPagesPerFile` (type: `integer`):

PDFs and multi-page TIFFs are transcribed up to this many pages from the start. Caps the cost of an unexpectedly long file.

## Actor input object example

```json
{
  "fileUrls": [
    "https://api.apify.com/v2/key-value-stores/T5HtoeQYut1ufwkKC/records/sample-handwritten-note.jpg"
  ],
  "maxPagesPerFile": 20
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "fileUrls": [
        "https://api.apify.com/v2/key-value-stores/T5HtoeQYut1ufwkKC/records/sample-handwritten-note.jpg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bossless/handwriting-to-text").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "fileUrls": ["https://api.apify.com/v2/key-value-stores/T5HtoeQYut1ufwkKC/records/sample-handwritten-note.jpg"] }

# Run the Actor and wait for it to finish
run = client.actor("bossless/handwriting-to-text").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "fileUrls": [
    "https://api.apify.com/v2/key-value-stores/T5HtoeQYut1ufwkKC/records/sample-handwritten-note.jpg"
  ]
}' |
apify call bossless/handwriting-to-text --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bossless/handwriting-to-text"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/qj68OmAwc2aJuQQDc/builds/zuyeSN3J58tlNPXk3/openapi.json
