doc-cheap
The Python client for doc.cheap: it sends a photo or scan of a passport, national ID card or driving licence to the doc.cheap API and returns the printed fields – name, date of birth, document number, expiry, the machine-readable zone – as typed dictionaries.
One recognised document costs one credit, $0.01. Registering gives 100 free documents every month. An unreadable image, an empty frame or an unsupported document type costs nothing, and every answer says whether it was billed.
The package needs Python 3.10 or later and depends only on httpx.
Installation
pip install doc-cheap
Quick start
from doc_cheap import SANDBOX_KEY, Client
with Client(SANDBOX_KEY) as client:
scan = client.recognize(path="passport.jpg")
print("status:", scan["meta"]["status"], "billed:", scan["meta"]["billed"])
if scan["holder"] and scan["holder"]["full_name"]:
print("holder:", scan["holder"]["full_name"])
if scan["document"] and scan["document"]["expiry_date"]:
print("expires:", scan["document"]["expiry_date"])
API keys
- Live key –
sk_live_…, created in the doc.cheap cabinet. - Sandbox key –
sk_sandbox_public(thedoc_cheap.SANDBOX_KEYconstant) works without an account. It gives 10 free recognised documents per address in all, and at most 10 requests per address an hour, whatever their answer.
Client() without a key reads it from the DOC_CHEAP_API_KEY environment
variable, which keeps a live key out of source code. The key is sent as a
Bearer token.
Methods
| Method | Request | What it does |
|---|---|---|
recognize |
POST /v1/scans |
Reads one JPEG or PNG image, from a file (path), raw bytes (image) or base64 (image_base64), with an optional reference label, idempotency_key, expect_country, return_portrait and retain_hours. |
get_scan |
GET /v1/scans/{id} |
Returns a stored result. |
list_scans |
GET /v1/scans |
Returns one page of stored results; pass next_cursor back as cursor for the next page. |
delete_scan |
DELETE /v1/scans/{id} |
Deletes a stored result. |
usage |
GET /v1/usage |
Returns this month's free credits, the paid credits, the credits spent and the scan counters. |
An image that holds no readable document is not an error: the scan comes back
with a status such as unreadable or no_document_found and billed set to
False.
Stored results. A result is stored only when the scan was made with a live
key under a non-zero retention window, and only until that window ends. Under
the sandbox key nothing is stored, so get_scan and delete_scan raise
NotFoundError. None of get_scan, delete_scan or usage charges a credit.
Errors
Every exception the package raises is a doc_cheap.DocCheapError. When the API
answers with an error status, it is an APIError carrying the HTTP status, the
API's error code and its message, narrowed to a subclass you can catch:
from doc_cheap import APIError, NotFoundError, RateLimitError
try:
scan = client.get_scan(scan_id)
except NotFoundError:
scan = None
except RateLimitError as err:
print("try again in", err.retry_after, "seconds")
except APIError as err:
print(err.status_code, err.code, err.message, err.request_id)
| Exception | When |
|---|---|
InvalidRequestError |
The request was malformed or failed validation (400, 413, 415, 422). |
AuthenticationError |
The key is missing, unknown or revoked (401). |
PermissionDeniedError |
The key may not do this (403). |
InsufficientCreditsError |
The account has no credits left (402). |
NotFoundError |
No such scan (404). |
ConflictError |
An idempotency key is reused or still in progress (409). |
RateLimitError |
Too many requests; retry_after says how long to wait (429). |
ServiceUnavailableError |
The service is briefly unavailable (503). |
InternalServerError |
Any other server error (5xx). |
APIConnectionError |
No answer at all: a network failure or a timeout. |
An argument the API would refuse anyway – no image, an image over 25 MiB, a
reference over 128 characters – raises InvalidArgumentError (also a
ValueError) before any request is sent.
Options
import httpx
from doc_cheap import Client
client = Client(
timeout=30.0,
user_agent="my-app/1.0",
)
# Or bring your own httpx.Client for a proxy or a shared connection pool;
# the doc.cheap client leaves it open when it closes.
client = Client(http_client=httpx.Client(proxy="http://proxy.internal:3128"))
base_url (or the DOC_CHEAP_API_BASE environment variable) points the client
at another address, for example a test server.
Resources
- doc.cheap documentation
- Your first recognition
- From the sandbox to a live key
- API reference
- Handling errors
License
Metadata
Release files for doc-cheap 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| doc_cheap-0.1.0.tar.gz | 15.0 kB | With provenance | Details for doc_cheap-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| doc_cheap-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for doc_cheap-0.1.0-py3-none-any.whl |
Total release size: 28.3 kB
Release files / doc_cheap-0.1.0.tar.gz
| Download URL | doc_cheap-0.1.0.tar.gz |
|---|---|
| Size | 15.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bde4be0941126077f5a86135dace1c76afd9f259e25d7bfecac4d6cbb2e6ae37
|
|
BLAKE2b-256 checksum How to use checksums |
e309fd803b3f01a5424f6f5e7ef75db53d43ed3a75ec4d6f51736372ee07fb73
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / doc_cheap-0.1.0-py3-none-any.whl
| Download URL | doc_cheap-0.1.0-py3-none-any.whl |
|---|---|
| Size | 13.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c11ae5208759297d30f947c3092d7d5fe14af2aa3c2574b86628251daf767e74
|
|
BLAKE2b-256 checksum How to use checksums |
76a576c7385e7d9f9202819ab18ae004b258dbaab095a585f5758e1e9ca28600
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logSecurity for doc-cheap 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for doc_cheap-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.