myocr-client 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ # Environment
2
+ .env
3
+ .env.example
4
+ .env.production
5
+ .env.staging
6
+ .env.local
7
+
8
+ # macOS
9
+ .DS_Store
10
+ .AppleDouble
11
+ .LSOverride
12
+
13
+ # Office temp files
14
+ ~$*
15
+
16
+ # Documents & spreadsheets
17
+ *.xlsx
18
+ *.docx
19
+ *.pdf
20
+
21
+ # Google Ads campaigns
22
+ google_ads_campaign/
23
+
24
+ # Python
25
+ __pycache__/
26
+ *.py[cod]
27
+ *.pyo
28
+ *.pyd
29
+ .Python
30
+ venv/
31
+ env/
32
+ .venv/
33
+ *.egg-info/
34
+ dist/
35
+ build/
36
+
37
+ # Logs
38
+ *.log
39
+
40
+ # GeoIP database (8.9MB binary, third-party, upload manually to server)
41
+ config/GeoLite2-Country.mmdb
42
+
43
+ .claude/
44
+
45
+ # Corpus immagini di test (~1.3GB) e script di benchmark scratch — non versionare
46
+ _TEST_DIVERSI/
47
+ bench_*.py
48
+
49
+ # Screenshot del test run (artefatti rigenerabili via Playwright) — non versionare
50
+ notes/img/test/
51
+
52
+ # Cache runtime + file temporanei (creati sul server)
53
+ gemini_chunk_cache/
54
+ upload_files_home/
55
+ processed_files_home/
56
+
57
+ # Indice KB del chatbot (artefatto derivato, ~MB) — rigenerabile con: python -m chatbot.kb_build
58
+ chatbot/kb_index.pkl
59
+ chatbot/kb_index.pkl.tmp
60
+
61
+ # artefatti grezzi test (contengono nomi file reali di clienti) — non versionare
62
+ notes/batch_corpus_results.csv
63
+ notes/BATCH_CORPUS_REPORT.md
64
+ notes/batch_corpus_tracebacks.txt
65
+
66
+ tests/qa/node_modules/
67
+
68
+ # Screenshot di QA add-in (artefatti, non versionati)
69
+ tests/qa/__screens__/
70
+ .publish_tokens
71
+
72
+ # dump DB produzione (dati personali, mai su git)
73
+ dump.sql
74
+ *.dump.sql
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to `myocr-client` will be documented in this file.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Changed (BREAKING)
10
+ - Rinominata l'eccezione `AzureError` -> `OcrEngineError` e il codice errore `AZURE_ERROR` -> `OCR_ERROR`
11
+ (allineamento al motore OCR proprietario myocr; aggiornare gli import e gli `except`).
12
+
13
+ ### Added
14
+ - `client.usage()` — wraps `GET /v1/usage` to fetch current month quota
15
+ programmatically (plan/calls_used/calls_limit/percentage/reset_date).
16
+
17
+ ## [0.1.0] — 2026-05-25 (initial)
18
+
19
+ ### Added
20
+ - `MyOCRClient` class with all v1 endpoints:
21
+ - `convert()` — sync conversion (xlsx / txt / json output)
22
+ - `create_job()` / `get_job()` / `get_job_result()` / `delete_job()` — async lifecycle
23
+ - `batch()` — 1–20 files per call
24
+ - `status()` — public health check
25
+ - `list_keys()` — administrative
26
+ - `Job` model with `wait()` polling (exponential backoff), `download()`, `delete()`.
27
+ - `JobStatus` enum (`pending`, `processing`, `done`, `failed`).
28
+ - `ConversionResult` / `JobResult` / `BatchResult` typed wrappers.
29
+ - 16 typed exceptions mapped 1:1 to API error codes (`QuotaExceeded`, `InvalidApiKey`, `OcrEngineError`, ...).
30
+ - `verify_webhook_signature()` helper for HMAC-SHA256 validation of incoming webhooks.
31
+ - Automatic retry on 429 / 5xx with exponential backoff (honors `Retry-After`).
32
+ - Accepts file path, bytes, or file-like input.
33
+ - Env var fallback `MYOCR_API_KEY`, `MYOCR_BASE_URL`.
34
+ - Full pytest suite with `responses` library mocking.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MAD.AI SRL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,274 @@
1
+ Metadata-Version: 2.5
2
+ Name: myocr-client
3
+ Version: 0.2.0
4
+ Summary: Official Python SDK for myocr.app — convert PDFs and images to structured Excel using myocr's OCR engine.
5
+ Project-URL: Homepage, https://www.myocr.app
6
+ Project-URL: Documentation, https://www.myocr.app/docs/api
7
+ Project-URL: Repository, https://github.com/Selaf688/myocr-3.5
8
+ Project-URL: Bug Tracker, https://github.com/Selaf688/myocr-3.5/issues
9
+ Project-URL: Changelog, https://github.com/Selaf688/myocr-3.5/blob/main/sdk/python/CHANGELOG.md
10
+ Author-email: "MAD.AI SRL" <info@myocr.app>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: api-client,document-extraction,excel,myocr,ocr,pdf
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Topic :: Office/Business :: Office Suites
27
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Requires-Python: >=3.8
30
+ Requires-Dist: requests>=2.28
31
+ Provides-Extra: dev
32
+ Requires-Dist: build>=1.0; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0; extra == 'dev'
34
+ Requires-Dist: responses>=0.23; extra == 'dev'
35
+ Requires-Dist: twine>=4.0; extra == 'dev'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # myocr-client — Python SDK for myocr.app
39
+
40
+ Official Python client for the **[myocr.app](https://www.myocr.app)** API. Convert PDFs and images to structured Excel using myocr's OCR engine (invoice, receipt, bank statement, business card, generic tables, plain text).
41
+
42
+ [![PyPI version](https://img.shields.io/pypi/v/myocr-client.svg)](https://pypi.org/project/myocr-client/)
43
+ [![Python versions](https://img.shields.io/pypi/pyversions/myocr-client.svg)](https://pypi.org/project/myocr-client/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
45
+
46
+ ---
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install myocr-client
52
+ ```
53
+
54
+ ## Quick start
55
+
56
+ Get an API key at [/account/api](https://www.myocr.app/account/api) (signup required), then:
57
+
58
+ ```python
59
+ from myocr_client import MyOCRClient
60
+
61
+ client = MyOCRClient(api_key="sk_live_...")
62
+ # or set MYOCR_API_KEY in env
63
+
64
+ # Synchronous conversion (≤5MB, ≤10 pages, returns immediately)
65
+ result = client.convert("invoice.pdf", model="invoice")
66
+ result.save("invoice.xlsx")
67
+
68
+ print(result.pages_used, result.model, result.request_id)
69
+ ```
70
+
71
+ ## Models
72
+
73
+ | Model | Output | Best for |
74
+ |---|---|---|
75
+ | `tables` | xlsx with generic tables | Any structured table |
76
+ | `text` | plain txt | OCR text extraction |
77
+ | `invoice` | xlsx with Vendor / Customer / Total / Line items | Invoices, bills |
78
+ | `receipt` | xlsx with Merchant / Date / Items / Total | Receipts |
79
+ | `bank_statement` | xlsx with Account / Transactions sheet | Bank statements |
80
+ | `business_card` | xlsx with Contact / Company / Phones / Emails | Business cards |
81
+
82
+ ## Async jobs (files > 5MB or > 10 pages)
83
+
84
+ ```python
85
+ job = client.create_job(
86
+ "annual_report.pdf",
87
+ model="bank_statement",
88
+ webhook_url="https://your.app/webhooks/myocr", # optional
89
+ )
90
+
91
+ # Option 1: polling with exponential backoff
92
+ job.wait(timeout=600)
93
+ job.download("report.xlsx")
94
+
95
+ # Option 2: notified via webhook (preferred for prod) — see "Webhook verification" below
96
+ ```
97
+
98
+ ## Batch (1–20 files in one call)
99
+
100
+ ```python
101
+ result = client.batch(
102
+ ["a.pdf", "b.pdf", "c.pdf"],
103
+ model="invoice",
104
+ webhook_url="https://your.app/webhooks/myocr",
105
+ )
106
+ print(result.jobs_created, "jobs queued;", len(result.errors), "errors")
107
+
108
+ # Wait for all and download
109
+ for job in result.wait_all(timeout=1200):
110
+ if job.is_done:
111
+ job.download(f"{job.request_id}.xlsx")
112
+ ```
113
+
114
+ ## Webhook verification
115
+
116
+ 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:
117
+
118
+ ```python
119
+ from flask import Flask, request
120
+ from myocr_client import verify_webhook_signature
121
+
122
+ app = Flask(__name__)
123
+ SECRET = "your-shared-secret" # same as server WEBHOOK_SIGNING_SECRET
124
+
125
+ @app.route("/webhooks/myocr", methods=["POST"])
126
+ def myocr_webhook():
127
+ body = request.get_data() # raw bytes, NOT request.get_json()
128
+ sig = request.headers.get("X-MyOCR-Signature", "")
129
+ if not verify_webhook_signature(body, sig, SECRET):
130
+ return "invalid signature", 401
131
+
132
+ event = request.get_json() # safe now
133
+ # {"event": "job.completed", "data": {"request_id": "...", "status": "done", ...}}
134
+ return "", 200
135
+ ```
136
+
137
+ Events: `job.completed`, `job.failed`.
138
+ Retry policy: 1m → 5m → 30m → 2h (4 retries beyond the first attempt).
139
+
140
+ ## Error handling
141
+
142
+ Every error code maps to a typed exception:
143
+
144
+ ```python
145
+ from myocr_client import MyOCRClient, QuotaExceeded, InvalidApiKey, OcrEngineError
146
+
147
+ client = MyOCRClient(api_key="sk_live_...")
148
+
149
+ try:
150
+ result = client.convert("doc.pdf", model="invoice")
151
+ except QuotaExceeded as e:
152
+ print(f"Plan {e.current_plan}, used {e.calls_used}/{e.calls_limit}")
153
+ print(f"Upgrade: {e.upgrade_url}")
154
+ print(f"Resets: {e.reset_date}")
155
+ except InvalidApiKey:
156
+ print("Rotate your key from /account/api")
157
+ except OcrEngineError:
158
+ print("OCR engine upstream failure; safe to retry")
159
+ ```
160
+
161
+ | Exception | HTTP | Code |
162
+ |---|---|---|
163
+ | `MissingApiKey` | 401 | `MISSING_API_KEY` |
164
+ | `InvalidApiKey` | 401 | `INVALID_API_KEY` |
165
+ | `UnsupportedModel` | 400 | `UNSUPPORTED_MODEL` |
166
+ | `UnsupportedFileType` | 400 | `UNSUPPORTED_FILE_TYPE` |
167
+ | `MissingFile` | 400 | `MISSING_FILE` |
168
+ | `FileTooLarge` | 413 | `FILE_TOO_LARGE` |
169
+ | `TooManyPages` | 413 | `TOO_MANY_PAGES` |
170
+ | `InvalidWebhookUrl` | 400 | `INVALID_WEBHOOK_URL` |
171
+ | `QuotaExceeded` | 402 | `QUOTA_EXCEEDED` |
172
+ | `NotReady` | 409 | `NOT_READY` |
173
+ | `NotFound` | 404 | `NOT_FOUND` |
174
+ | `OcrEngineError` | 502 | `OCR_ERROR` |
175
+ | `StorageError` | 503 | `STORAGE_ERROR` |
176
+ | `RateLimited` | 429 | — |
177
+ | `ServiceNotReady` | 503 | `SERVICE_NOT_READY` |
178
+ | `InternalError` | 500 | `INTERNAL_ERROR` |
179
+
180
+ 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.
181
+
182
+ ## Input flexibility
183
+
184
+ `client.convert()` and `client.create_job()` accept:
185
+
186
+ - A file path: `client.convert("/path/to/doc.pdf", ...)`
187
+ - Raw bytes: `client.convert(pdf_bytes, filename="doc.pdf", ...)`
188
+ - A file-like object: `with open("doc.pdf", "rb") as f: client.convert(f, ...)`
189
+
190
+ ## Configuration
191
+
192
+ | Argument | Env var | Default |
193
+ |---|---|---|
194
+ | `api_key` | `MYOCR_API_KEY` | — (required) |
195
+ | `base_url` | `MYOCR_BASE_URL` | `https://api.myocr.app` |
196
+ | `timeout` | — | 60s |
197
+ | `retry_attempts` | — | 3 |
198
+ | `session` | — | new `requests.Session()` |
199
+
200
+ For staging:
201
+
202
+ ```python
203
+ client = MyOCRClient(api_key="sk_test_...", base_url="https://beta.myocr.app")
204
+ ```
205
+
206
+ ## Monitor your quota
207
+
208
+ Check current month usage programmatically (e.g. to upgrade before exhaustion):
209
+
210
+ ```python
211
+ usage = client.usage()
212
+ # {
213
+ # "plan": "free", "calls_used": 42, "calls_limit": 100,
214
+ # "percentage": 42.0, "reset_date": "2026-06-01T00:00:00",
215
+ # "year_month": "2026-05", "is_test_key": False
216
+ # }
217
+ if usage["percentage"] and usage["percentage"] > 80:
218
+ # alert ops, upgrade plan, or stop background workers
219
+ ...
220
+ ```
221
+
222
+ ## Status & limits
223
+
224
+ ```python
225
+ status = client.status()
226
+ # {
227
+ # "service": "myocr.app API", "version": "v1",
228
+ # "models_supported": ["bank_statement", "business_card", ...],
229
+ # "features": {"sync_convert": True, "async_jobs": True, "webhook": True, ...},
230
+ # "limits": {"sync_max_bytes": 5242880, "sync_max_pages": 10,
231
+ # "jobs_max_bytes": 52428800, "sync_rate_per_minute": 60,
232
+ # "jobs_rate_per_minute": 120}
233
+ # }
234
+ ```
235
+
236
+ ## Rate limits (server-side)
237
+
238
+ | Endpoint | Limit |
239
+ |---|---|
240
+ | `POST /v1/convert` | 60 / min |
241
+ | `POST /v1/jobs` | 120 / min |
242
+ | `POST /v1/batch` | 30 / min |
243
+
244
+ The SDK handles `429` with automatic retry. If you saturate the quota, upgrade your plan from the dashboard.
245
+
246
+ ## Reference
247
+
248
+ - **Full OpenAPI spec:** [openapi.yaml](https://www.myocr.app/static/openapi.yaml)
249
+ - **Interactive docs:** [/docs/api](https://www.myocr.app/docs/api) (Scalar UI)
250
+ - **Dashboard:** [/account/api](https://www.myocr.app/account/api) — manage keys, view usage, upgrade
251
+ - **Webhook signing secret:** generated when you create a webhook integration; shared via dashboard.
252
+
253
+ ## Development
254
+
255
+ ```bash
256
+ git clone https://github.com/Selaf688/myocr-3.5
257
+ cd myocr-3.5/sdk/python
258
+ pip install -e ".[dev]"
259
+ pytest -v
260
+ ```
261
+
262
+ ## Versioning
263
+
264
+ Semantic versioning. The API itself is `v1` and stable; the SDK can release patch/minor independently.
265
+
266
+ ## License
267
+
268
+ MIT. See [LICENSE](./LICENSE).
269
+
270
+ ## Support
271
+
272
+ - Documentation: <https://www.myocr.app/docs/api>
273
+ - Email: info@myocr.app
274
+ - Issues: <https://github.com/Selaf688/myocr-3.5/issues>
@@ -0,0 +1,237 @@
1
+ # myocr-client — Python SDK for myocr.app
2
+
3
+ Official Python client for the **[myocr.app](https://www.myocr.app)** API. Convert PDFs and images to structured Excel using myocr's OCR engine (invoice, receipt, bank statement, business card, generic tables, plain text).
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/myocr-client.svg)](https://pypi.org/project/myocr-client/)
6
+ [![Python versions](https://img.shields.io/pypi/pyversions/myocr-client.svg)](https://pypi.org/project/myocr-client/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install myocr-client
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ Get an API key at [/account/api](https://www.myocr.app/account/api) (signup required), then:
20
+
21
+ ```python
22
+ from myocr_client import MyOCRClient
23
+
24
+ client = MyOCRClient(api_key="sk_live_...")
25
+ # or set MYOCR_API_KEY in env
26
+
27
+ # Synchronous conversion (≤5MB, ≤10 pages, returns immediately)
28
+ result = client.convert("invoice.pdf", model="invoice")
29
+ result.save("invoice.xlsx")
30
+
31
+ print(result.pages_used, result.model, result.request_id)
32
+ ```
33
+
34
+ ## Models
35
+
36
+ | Model | Output | Best for |
37
+ |---|---|---|
38
+ | `tables` | xlsx with generic tables | Any structured table |
39
+ | `text` | plain txt | OCR text extraction |
40
+ | `invoice` | xlsx with Vendor / Customer / Total / Line items | Invoices, bills |
41
+ | `receipt` | xlsx with Merchant / Date / Items / Total | Receipts |
42
+ | `bank_statement` | xlsx with Account / Transactions sheet | Bank statements |
43
+ | `business_card` | xlsx with Contact / Company / Phones / Emails | Business cards |
44
+
45
+ ## Async jobs (files > 5MB or > 10 pages)
46
+
47
+ ```python
48
+ job = client.create_job(
49
+ "annual_report.pdf",
50
+ model="bank_statement",
51
+ webhook_url="https://your.app/webhooks/myocr", # optional
52
+ )
53
+
54
+ # Option 1: polling with exponential backoff
55
+ job.wait(timeout=600)
56
+ job.download("report.xlsx")
57
+
58
+ # Option 2: notified via webhook (preferred for prod) — see "Webhook verification" below
59
+ ```
60
+
61
+ ## Batch (1–20 files in one call)
62
+
63
+ ```python
64
+ result = client.batch(
65
+ ["a.pdf", "b.pdf", "c.pdf"],
66
+ model="invoice",
67
+ webhook_url="https://your.app/webhooks/myocr",
68
+ )
69
+ print(result.jobs_created, "jobs queued;", len(result.errors), "errors")
70
+
71
+ # Wait for all and download
72
+ for job in result.wait_all(timeout=1200):
73
+ if job.is_done:
74
+ job.download(f"{job.request_id}.xlsx")
75
+ ```
76
+
77
+ ## Webhook verification
78
+
79
+ 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:
80
+
81
+ ```python
82
+ from flask import Flask, request
83
+ from myocr_client import verify_webhook_signature
84
+
85
+ app = Flask(__name__)
86
+ SECRET = "your-shared-secret" # same as server WEBHOOK_SIGNING_SECRET
87
+
88
+ @app.route("/webhooks/myocr", methods=["POST"])
89
+ def myocr_webhook():
90
+ body = request.get_data() # raw bytes, NOT request.get_json()
91
+ sig = request.headers.get("X-MyOCR-Signature", "")
92
+ if not verify_webhook_signature(body, sig, SECRET):
93
+ return "invalid signature", 401
94
+
95
+ event = request.get_json() # safe now
96
+ # {"event": "job.completed", "data": {"request_id": "...", "status": "done", ...}}
97
+ return "", 200
98
+ ```
99
+
100
+ Events: `job.completed`, `job.failed`.
101
+ Retry policy: 1m → 5m → 30m → 2h (4 retries beyond the first attempt).
102
+
103
+ ## Error handling
104
+
105
+ Every error code maps to a typed exception:
106
+
107
+ ```python
108
+ from myocr_client import MyOCRClient, QuotaExceeded, InvalidApiKey, OcrEngineError
109
+
110
+ client = MyOCRClient(api_key="sk_live_...")
111
+
112
+ try:
113
+ result = client.convert("doc.pdf", model="invoice")
114
+ except QuotaExceeded as e:
115
+ print(f"Plan {e.current_plan}, used {e.calls_used}/{e.calls_limit}")
116
+ print(f"Upgrade: {e.upgrade_url}")
117
+ print(f"Resets: {e.reset_date}")
118
+ except InvalidApiKey:
119
+ print("Rotate your key from /account/api")
120
+ except OcrEngineError:
121
+ print("OCR engine upstream failure; safe to retry")
122
+ ```
123
+
124
+ | Exception | HTTP | Code |
125
+ |---|---|---|
126
+ | `MissingApiKey` | 401 | `MISSING_API_KEY` |
127
+ | `InvalidApiKey` | 401 | `INVALID_API_KEY` |
128
+ | `UnsupportedModel` | 400 | `UNSUPPORTED_MODEL` |
129
+ | `UnsupportedFileType` | 400 | `UNSUPPORTED_FILE_TYPE` |
130
+ | `MissingFile` | 400 | `MISSING_FILE` |
131
+ | `FileTooLarge` | 413 | `FILE_TOO_LARGE` |
132
+ | `TooManyPages` | 413 | `TOO_MANY_PAGES` |
133
+ | `InvalidWebhookUrl` | 400 | `INVALID_WEBHOOK_URL` |
134
+ | `QuotaExceeded` | 402 | `QUOTA_EXCEEDED` |
135
+ | `NotReady` | 409 | `NOT_READY` |
136
+ | `NotFound` | 404 | `NOT_FOUND` |
137
+ | `OcrEngineError` | 502 | `OCR_ERROR` |
138
+ | `StorageError` | 503 | `STORAGE_ERROR` |
139
+ | `RateLimited` | 429 | — |
140
+ | `ServiceNotReady` | 503 | `SERVICE_NOT_READY` |
141
+ | `InternalError` | 500 | `INTERNAL_ERROR` |
142
+
143
+ 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.
144
+
145
+ ## Input flexibility
146
+
147
+ `client.convert()` and `client.create_job()` accept:
148
+
149
+ - A file path: `client.convert("/path/to/doc.pdf", ...)`
150
+ - Raw bytes: `client.convert(pdf_bytes, filename="doc.pdf", ...)`
151
+ - A file-like object: `with open("doc.pdf", "rb") as f: client.convert(f, ...)`
152
+
153
+ ## Configuration
154
+
155
+ | Argument | Env var | Default |
156
+ |---|---|---|
157
+ | `api_key` | `MYOCR_API_KEY` | — (required) |
158
+ | `base_url` | `MYOCR_BASE_URL` | `https://api.myocr.app` |
159
+ | `timeout` | — | 60s |
160
+ | `retry_attempts` | — | 3 |
161
+ | `session` | — | new `requests.Session()` |
162
+
163
+ For staging:
164
+
165
+ ```python
166
+ client = MyOCRClient(api_key="sk_test_...", base_url="https://beta.myocr.app")
167
+ ```
168
+
169
+ ## Monitor your quota
170
+
171
+ Check current month usage programmatically (e.g. to upgrade before exhaustion):
172
+
173
+ ```python
174
+ usage = client.usage()
175
+ # {
176
+ # "plan": "free", "calls_used": 42, "calls_limit": 100,
177
+ # "percentage": 42.0, "reset_date": "2026-06-01T00:00:00",
178
+ # "year_month": "2026-05", "is_test_key": False
179
+ # }
180
+ if usage["percentage"] and usage["percentage"] > 80:
181
+ # alert ops, upgrade plan, or stop background workers
182
+ ...
183
+ ```
184
+
185
+ ## Status & limits
186
+
187
+ ```python
188
+ status = client.status()
189
+ # {
190
+ # "service": "myocr.app API", "version": "v1",
191
+ # "models_supported": ["bank_statement", "business_card", ...],
192
+ # "features": {"sync_convert": True, "async_jobs": True, "webhook": True, ...},
193
+ # "limits": {"sync_max_bytes": 5242880, "sync_max_pages": 10,
194
+ # "jobs_max_bytes": 52428800, "sync_rate_per_minute": 60,
195
+ # "jobs_rate_per_minute": 120}
196
+ # }
197
+ ```
198
+
199
+ ## Rate limits (server-side)
200
+
201
+ | Endpoint | Limit |
202
+ |---|---|
203
+ | `POST /v1/convert` | 60 / min |
204
+ | `POST /v1/jobs` | 120 / min |
205
+ | `POST /v1/batch` | 30 / min |
206
+
207
+ The SDK handles `429` with automatic retry. If you saturate the quota, upgrade your plan from the dashboard.
208
+
209
+ ## Reference
210
+
211
+ - **Full OpenAPI spec:** [openapi.yaml](https://www.myocr.app/static/openapi.yaml)
212
+ - **Interactive docs:** [/docs/api](https://www.myocr.app/docs/api) (Scalar UI)
213
+ - **Dashboard:** [/account/api](https://www.myocr.app/account/api) — manage keys, view usage, upgrade
214
+ - **Webhook signing secret:** generated when you create a webhook integration; shared via dashboard.
215
+
216
+ ## Development
217
+
218
+ ```bash
219
+ git clone https://github.com/Selaf688/myocr-3.5
220
+ cd myocr-3.5/sdk/python
221
+ pip install -e ".[dev]"
222
+ pytest -v
223
+ ```
224
+
225
+ ## Versioning
226
+
227
+ Semantic versioning. The API itself is `v1` and stable; the SDK can release patch/minor independently.
228
+
229
+ ## License
230
+
231
+ MIT. See [LICENSE](./LICENSE).
232
+
233
+ ## Support
234
+
235
+ - Documentation: <https://www.myocr.app/docs/api>
236
+ - Email: info@myocr.app
237
+ - Issues: <https://github.com/Selaf688/myocr-3.5/issues>