Skip to main content

myocr-client — Python SDK for myocr.app

Official Python client for the myocr.app API. Convert PDFs, scans and photos to structured Excel or JSON: bank statements (with a balance check), invoices, receipts, business cards, generic tables, plain text, or the columns you choose.

Source, issues and runnable examples: github.com/Selaf688/myocr-sdk.

PyPI version Python versions License: MIT


Install

pip install myocr-client

Quick start

Create an account, then get a key from the API dashboard. A sandbox key (sk_test_...) needs no card and includes 5 pages on the synchronous endpoint, enough to try everything below. Then:

from myocr_client import MyOCRClient

client = MyOCRClient(api_key="sk_live_...")
# or set MYOCR_API_KEY in env

# Synchronous conversion (≤5MB, ≤10 pages, returns immediately)
result = client.convert("invoice.pdf", model="invoice")
result.save("invoice.xlsx")

print(result.pages_used, result.model, result.request_id)

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
fields xlsx with the columns you name in fields Forms, delivery notes, any document where you know what you want

Check that a bank statement balances

With output="json", a bank statement comes back with a reconciliation block: opening balance + credits - debits, compared with the closing balance printed on the statement.

result = client.convert("statement.pdf", model="bank_statement", output="json")
check = result.data["reconciliation"]
# {'ok': True, 'opening': 2450.0, 'credits': 2845.75, 'debits': 1247.07,
#  'computed_closing': 4048.68, 'closing': 4048.68, 'delta': 0.0, 'n_transactions': 10, ...}
if not check["ok"]:
    print(f"Off by {check['delta']:.2f}: check the statement before importing")

The xlsx output carries the same check in its balance check sheet.

Only some pages

page_range (PDF only) converts just the pages you list: "3", "3-5", "1,3-5", "2-" (to the end). Pages outside the range are not billed. It works on convert(), create_job() and batch() (where it applies to every file).

result = client.convert("invoice_with_attachments.pdf", model="invoice", page_range="1")

Your own columns

result = client.convert(
    "delivery_notes.pdf",
    model="fields",
    fields=["Date", "Delivery note number", "Customer", "Total"],  # max 60
    fields_mode="page",  # one row per page; "list" = one row per printed item
)
result.save("delivery_notes.xlsx")

Async jobs (files > 5MB or > 10 pages)

job = client.create_job(
    "annual_report.pdf",
    model="bank_statement",
    webhook_url="https://your.app/webhooks/myocr",  # optional
)

# Option 1: polling with exponential backoff
job.wait(timeout=600)
job.download("report.xlsx")

# Option 2: notified via webhook (preferred for prod) — see "Webhook verification" below

Batch (1–20 files in one call)

result = client.batch(
    ["a.pdf", "b.pdf", "c.pdf"],
    model="invoice",
    webhook_url="https://your.app/webhooks/myocr",
)
print(result.jobs_created, "jobs queued;", len(result.errors), "errors")

# Wait for all and download
for job in result.wait_all(timeout=1200):
    if job.is_done:
        job.download(f"{job.request_id}.xlsx")

Webhooks

Pass webhook_url= to create_job() or batch() and myocr POSTs a JSON notification when each job ends:

{"event": "job.completed", "data": {"request_id": "...", "status": "done", "model": "bank_statement", "pages_used": 3, "result_url": "https://..."}}
{"event": "job.failed", "data": {"request_id": "...", "status": "failed", "model": "tables", "error": "..."}}

result_url is a temporary download link (24 hours). Failed deliveries are retried after 1m, 5m, 30m and 2h.

Every delivery is signed: X-MyOCR-Signature: sha256=<hex>, an HMAC-SHA256 of the raw body made with your account's webhook signing secret. Copy it from the API dashboard, where you can also replace it. Verify on the raw bytes, before parsing the JSON:

import os
from flask import Flask, request
from myocr_client import verify_webhook_signature

app = Flask(__name__)
SECRET = os.environ["MYOCR_WEBHOOK_SECRET"]   # whsec_... from the dashboard

@app.post("/webhooks/myocr")
def myocr_webhook():
    body = request.get_data()                 # raw bytes, NOT request.get_json()
    if not verify_webhook_signature(body, request.headers.get("X-MyOCR-Signature", ""), SECRET):
        return "invalid signature", 401
    event = request.get_json()                # safe now
    ...
    return "", 204

Error handling

Every error code maps to a typed exception:

from myocr_client import MyOCRClient, QuotaExceeded, InvalidApiKey, OcrEngineError

client = MyOCRClient(api_key="sk_live_...")

try:
    result = client.convert("doc.pdf", model="invoice")
except QuotaExceeded as e:
    print(f"Plan {e.current_plan}, used {e.calls_used}/{e.calls_limit}")
    print(f"Upgrade: {e.upgrade_url}")
    print(f"Resets: {e.reset_date}")
except InvalidApiKey:
    print("Rotate your key from /account/api")
except OcrEngineError:
    print("OCR engine upstream failure; safe to retry")
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). After retries exhausted the exception is raised.

Input flexibility

client.convert() and client.create_job() accept:

  • A file path: client.convert("/path/to/doc.pdf", ...)
  • Raw bytes: client.convert(pdf_bytes, filename="doc.pdf", ...)
  • A file-like object: with open("doc.pdf", "rb") as f: client.convert(f, ...)

Configuration

Argument Env var Default
api_key MYOCR_API_KEY — (required)
base_url MYOCR_BASE_URL https://api.myocr.app
timeout — 60s
retry_attempts — 3
session — new requests.Session()

For staging:

client = MyOCRClient(api_key="sk_test_...", base_url="https://beta.myocr.app")

Monitor your quota

Check current month usage programmatically (e.g. to upgrade before exhaustion):

usage = client.usage()
# {
#   "plan": "api_starter", "calls_used": 420, "calls_limit": 2500,
#   "percentage": 16.8, "reset_date": "2026-11-01T00:00:00",
#   "year_month": "2026-10", "is_test_key": False
# }
if usage["percentage"] and usage["percentage"] > 80:
    # alert ops, upgrade plan, or stop background workers
    ...

Status & limits

status = client.status()
# {
#   "service": "myocr.app API", "version": "v1",
#   "models_supported": ["bank_statement", "business_card", ...],
#   "features": {"sync_convert": True, "async_jobs": True, "webhook": True, ...},
#   "limits": {"sync_max_bytes": 5242880, "sync_max_pages": 10,
#              "jobs_max_bytes": 52428800, "sync_rate_per_minute": 60,
#              "jobs_rate_per_minute": 120}
# }

Rate 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.

Reference

  • Full OpenAPI spec: openapi.json (import it into Postman or Insomnia)
  • Interactive docs: /docs/api (Scalar UI)
  • Dashboard: /account/api — manage keys, view usage, upgrade
  • Webhook signing secret: generated when you create a webhook integration; shared via dashboard.

Development

git clone https://github.com/Selaf688/myocr-sdk
cd myocr-sdk/python
pip install -e ".[dev]"
pytest -v

Runnable examples, with a fictitious sample statement, are in examples/.

Versioning

Semantic versioning. The API itself is v1 and stable; the SDK can release patch/minor independently.

License

MIT. See LICENSE.

Support

Release files for myocr-client 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for myocr-client 0.3.0
File Size Uploaded
myocr_client-0.3.0.tar.gz 19.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for myocr-client 0.3.0
File Interpreter ABI Platform
myocr_client-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 37.5 kB

Release files / myocr_client-0.3.0.tar.gz

Download URL myocr_client-0.3.0.tar.gz
Size 19.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5d72712948d6ea684c78bf342603e4827b619a46181a2107c81c891e8e905614
BLAKE2b-256 checksum
How to use checksums
cef690d6a7780eafc4dd3f440701ef02134f2f21b756addc6acc7406cbd81c12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release files / myocr_client-0.3.0-py3-none-any.whl

Download URL myocr_client-0.3.0-py3-none-any.whl
Size 18.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b8bb95146cb90313a32f897c28f20b494a43827a28ebdaa7507bc75aa09752de
BLAKE2b-256 checksum
How to use checksums
a56ca08371aafdc5679493854435a17a4d461461cabaa29269a42401b9f50f12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page