myocr-client
v0.2.0
Published
Official Node.js / TypeScript SDK for myocr.app — convert PDFs and images to structured Excel using myocr's OCR engine.
Maintainers
Readme
myocr-client — Node.js / TypeScript SDK for myocr.app
Official Node.js client for the myocr.app API. Convert PDFs and images to structured Excel using myocr's OCR engine. Zero runtime dependencies (Node 18+ native fetch/FormData/crypto).
Install
npm install myocr-client
# or yarn add myocr-client / pnpm add myocr-clientQuick start
Get an API key at /account/api (free tier: 100 calls/month, no card), then:
import { MyOCRClient } from 'myocr-client';
const client = new MyOCRClient({ apiKey: 'sk_live_...' });
// or set MYOCR_API_KEY env var
// Synchronous conversion (≤5MB, ≤10 pages, returns immediately)
const result = await client.convert('invoice.pdf', { model: 'invoice' });
await result.save('invoice.xlsx');
console.log(result.pagesUsed, result.model, result.requestId);Models
| Model | Output | Best for |
|---|---|---|
| tables | xlsx with generic tables | Any structured table |
| text | plain txt | OCR text extraction |
| invoice | xlsx with Vendor / Customer / Total / Line items | Invoices, bills |
| receipt | xlsx with Merchant / Date / Items / Total | Receipts |
| bank_statement | xlsx with Account / Transactions sheet | Bank statements |
| business_card | xlsx with Contact / Company / Phones / Emails | Business cards |
Async jobs (files > 5MB or > 10 pages)
const job = await client.createJob('annual_report.pdf', {
model: 'bank_statement',
webhookUrl: 'https://your.app/webhooks/myocr', // optional
});
// Option 1: polling with exponential backoff
await job.wait({ timeoutMs: 600_000 });
await job.download('report.xlsx');
// Option 2: notified via webhook — see "Webhook verification" belowBatch (1–20 files in one call)
const batch = await client.batch(
['a.pdf', 'b.pdf', 'c.pdf'],
{
model: 'invoice',
webhookUrl: 'https://your.app/webhooks/myocr',
},
);
console.log(`${batch.jobsCreated} jobs queued; ${batch.errors.length} errors`);
// Wait for all and download
const done = await batch.waitAll({ timeoutMs: 1_200_000 });
for (const job of done) {
if (job.isDone) {
await job.download(`out/${job.requestId}.xlsx`);
}
}Webhook verification
myocr signs every webhook with HMAC-SHA256 (header X-MyOCR-Signature: sha256=<hex>). Always verify before trusting the payload — and use raw bytes, not the parsed JSON:
import express from 'express';
import { verifyWebhookSignature } from 'myocr-client';
const app = express();
const SECRET = 'your-shared-secret'; // same as server WEBHOOK_SIGNING_SECRET
// IMPORTANT: capture raw body. Express's json parser destroys it.
app.use('/webhooks/myocr', express.raw({ type: 'application/json' }));
app.post('/webhooks/myocr', (req, res) => {
const body: Buffer = req.body;
const sig = req.header('X-MyOCR-Signature') || '';
if (!verifyWebhookSignature(body, sig, SECRET)) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(body.toString('utf8'));
// event = { event: 'job.completed', data: { request_id: ..., status: 'done', ... } }
res.status(200).end();
});Events: job.completed, job.failed.
Retry policy: 1m → 5m → 30m → 2h (4 retries beyond the first attempt).
Error handling
Every API error code maps to a typed exception:
import { MyOCRClient, QuotaExceeded, InvalidApiKey, OcrEngineError } from 'myocr-client';
const client = new MyOCRClient({ apiKey: 'sk_live_...' });
try {
const result = await client.convert('doc.pdf', { model: 'invoice' });
} catch (e) {
if (e instanceof QuotaExceeded) {
console.log(`Plan ${e.currentPlan}, used ${e.callsUsed}/${e.callsLimit}`);
console.log(`Upgrade: ${e.upgradeUrl}`);
console.log(`Resets: ${e.resetDate}`);
} else if (e instanceof InvalidApiKey) {
console.log('Rotate your key from /account/api');
} else if (e instanceof OcrEngineError) {
console.log('OCR engine upstream failure; safe to retry');
} else {
throw e;
}
}| Exception | HTTP | Code |
|---|---|---|
| MissingApiKey | 401 | MISSING_API_KEY |
| InvalidApiKey | 401 | INVALID_API_KEY |
| UnsupportedModel | 400 | UNSUPPORTED_MODEL |
| UnsupportedFileType | 400 | UNSUPPORTED_FILE_TYPE |
| MissingFile | 400 | MISSING_FILE |
| FileTooLarge | 413 | FILE_TOO_LARGE |
| TooManyPages | 413 | TOO_MANY_PAGES |
| InvalidWebhookUrl | 400 | INVALID_WEBHOOK_URL |
| QuotaExceeded | 402 | QUOTA_EXCEEDED |
| NotReady | 409 | NOT_READY |
| NotFound | 404 | NOT_FOUND |
| OcrEngineError | 502 | OCR_ERROR |
| StorageError | 503 | STORAGE_ERROR |
| RateLimited | 429 | — |
| ServiceNotReady | 503 | SERVICE_NOT_READY |
| InternalError | 500 | INTERNAL_ERROR |
The SDK automatically retries 429 and 5xx responses up to 3 times with exponential backoff (honoring Retry-After when present).
Input flexibility
convert(), createJob(), and batch() accept:
- A file path:
client.convert('/path/to/doc.pdf', ...) - A Buffer:
client.convert(Buffer.from(...), { filename: 'doc.pdf', ... }) - A
Uint8Array - A
Blob - An object
{ data: Buffer | Blob, filename?: string }
Configuration
| Option | Env var | Default |
|---|---|---|
| apiKey | MYOCR_API_KEY | — (required) |
| baseUrl | MYOCR_BASE_URL | https://api.myocr.app |
| timeoutMs | — | 60000 |
| retryAttempts | — | 3 |
| fetchImpl | — | global fetch |
For staging:
const client = new MyOCRClient({
apiKey: 'sk_test_...',
baseUrl: 'https://beta.myocr.app',
});For custom fetch (e.g. with proxy, undici agent):
import { fetch } from 'undici';
const client = new MyOCRClient({ apiKey: '...', fetchImpl: fetch as typeof globalThis.fetch });Monitor your quota
Check current month usage programmatically (e.g. to upgrade before exhaustion):
const usage = await client.usage();
// {
// plan: 'free', calls_used: 42, calls_limit: 100,
// percentage: 42.0, reset_date: '2026-06-01T00:00:00',
// year_month: '2026-05', is_test_key: false
// }
if (usage.percentage && (usage.percentage as number) > 80) {
// alert ops, upgrade plan, or stop background workers
}Status & limits
const status = await client.status();
// service, version, models_supported, features, limitsRate limits (server-side)
| Endpoint | Limit |
|---|---|
| POST /v1/convert | 60 / min |
| POST /v1/jobs | 120 / min |
| POST /v1/batch | 30 / min |
The SDK handles 429 with automatic retry. If you saturate the quota, upgrade your plan from the dashboard.
TypeScript
The package ships type declarations (.d.ts) — full IntelliSense out of the box. All public surface is strict-typed.
Compatibility
- Node.js 18+ (uses native
fetch,FormData,Blob,node:crypto) - Browser: pass the file as
Blob/File(e.g. from<input type="file">) — no Node-specific APIs are required forconvert()/createJob(). Note: webhook verification (verifyWebhookSignature) requiresnode:cryptoso it runs server-side only. - Serverless: works in AWS Lambda (Node 18+ runtime), Cloud Functions, Vercel, Cloudflare Workers (use Workers' native fetch).
Reference
- Full OpenAPI spec: openapi.yaml
- Interactive docs: /docs/api (Scalar UI)
- Dashboard: /account/api — manage keys, view usage, upgrade
- Python SDK (same surface): pypi.org/project/myocr-client
Development
git clone https://github.com/Selaf688/myocr-3.5
cd myocr-3.5/sdk/node
npm install
npm test # 27 tests
npm run build # tsc → dist/
npm run typecheckVersioning
Semantic versioning. The API itself is v1 and stable; the SDK can release patch/minor independently.
License
MIT. See LICENSE.
Support
- Documentation: https://www.myocr.app/docs/api
- Email: [email protected]
- Issues: https://github.com/Selaf688/myocr-3.5/issues
