hexread
hexread is the official Python client for HexRead - convert PDFs and
images to Markdown. It is a thin API client: it talks to the HexRead API at
https://api.hexread.com/v1 and never processes documents locally.
API access requires a paid HexRead plan. The free trial is web only, so no API key can be issued for it. Keys are created and revoked in your HexRead dashboard.
Install
pip install hexread
Python 3.10+. The only runtime dependency is httpx.
Quick start
import hexread
res = hexread.convert("report.pdf") # -> ConvertResult
print(res) # str(res) is res.markdown
Use a client when you want metadata, several files, or a custom timeout:
from hexread import HexRead
client = HexRead() # key from the argument, the environment, or the CLI credential
res = client.convert("report.pdf", progress=True)
print(res.model, res.route_reason, res.pages_remaining)
for page in res.pages:
print(page.index, page.markdown[:80])
for res in client.convert_many(["a.pdf", "b.png"], max_workers=2):
print(res) # str(res) is res.markdown; a failed file yields the exception
print(client.usage().pages_remaining)
AsyncHexRead has the same surface with await:
import asyncio
from hexread import AsyncHexRead
async def main():
async with AsyncHexRead() as client:
res = await client.convert("report.pdf")
print(res.markdown)
asyncio.run(main())
Options
| Argument | On | Meaning |
|---|---|---|
api_key |
HexRead(...) |
key for this client; wins over the environment and the CLI credential |
base_url |
HexRead(...) |
API base URL (default https://api.hexread.com/v1) |
timeout |
HexRead(...) |
request timeout in seconds |
max_retries |
HexRead(...) |
retries for the failures a repeat can fix (default 4) |
model |
client and convert() |
parser to request; auto (the default) lets the server route |
lang |
client and convert() |
OCR language hint, passed through to the parser |
prefer |
convert() |
"sync" or "async"; by default the client decides |
name |
convert() |
the file name to report when you pass bytes or an open file |
progress |
convert(), convert_many() |
print a progress line while the conversion runs |
on_progress |
convert() |
callback receiving Progress(phase, pages_done, pages_total, source) |
max_workers |
convert_many() |
files converted in parallel (default 2) |
raise_on_error |
convert_many() |
True aborts the batch on the first failure instead of returning it in place |
Naming a model requires a plan that allows it; auto is always allowed.
client.usage().available_models lists the ones you may request.
The result object
convert() returns a ConvertResult:
| Field | Type | What |
|---|---|---|
markdown |
str |
the whole document as Markdown |
pages |
list[Page] |
one entry per page, in order |
pages[i].index |
int |
zero-based page index, as returned by the API |
pages[i].number |
int |
the 1-based page number |
pages[i].markdown |
str |
that page's Markdown |
model |
str |
the parser that produced this result |
route_reason |
str |
why auto picked that parser; empty when you named a model |
page_count |
int |
pages converted |
pages_remaining |
int | None |
pages left in the current allowance window |
source |
str |
the path or name you passed in |
job_id |
str |
set when the conversion took the async path, otherwise empty |
meta |
Meta |
pages, sha256, timing_ms, backend, model, route_reason |
str(result) |
str |
the same string as .markdown |
Account meters come from client.usage():
u = client.usage()
u.tier # your plan
u.pages_used # pages converted in the current window
u.pages_remaining # pages left before the cap
u.pages_allowance # the window's allowance
u.concurrency_limit # conversions this plan may run at once
u.api_access # True on a plan that may call the API
u.available_models # models you may request explicitly
u.resets_at # when the window rolls over
Credentials
Resolved in this order; the first one found wins.
| Order | Source |
|---|---|
| 1 | api_key= passed to HexRead() or AsyncHexRead() |
| 2 | HEXREAD_API_KEY |
| 3 | the credential file written by hexread login (hexread/credential in your OS config directory) |
| Environment variable | Meaning |
|---|---|
HEXREAD_API_KEY |
API key for this process (nothing is written to disk) |
HEXREAD_BASE_URL |
API base URL (default https://api.hexread.com/v1; HEXREAD_API is a legacy alias) |
Errors
Every exception subclasses HexReadError and carries .message, .status, .type, .code,
.request_id and .retry_after. The class is keyed on the API's error type, not the HTTP status.
| Exception | Raised when |
|---|---|
MissingAPIKeyError |
no credential was found in any of the three sources above |
AuthenticationError |
the key is malformed, revoked, or rejected |
PermissionError |
authenticated but not allowed: plan gate, scope, or a model outside the plan |
QuotaError |
the page allowance for the current period is used up |
RateLimitError |
too many requests; .retry_after holds the wait in seconds |
ValidationError |
the request or the file was rejected |
NotFoundError |
unknown job or endpoint |
ConflictError |
the job is in a conflicting state |
GoneError |
the result is gone (results are not retained) |
PayloadTooLargeError |
the upload exceeds the size limit |
CapacityError |
transient: no capacity right now, retry later |
CanceledError |
the job was canceled |
ServerError |
the API failed |
MissingAPIKeyError subclasses AuthenticationError, so one except covers both.
hexread.PermissionError shadows the builtin inside from hexread import *; import the module or
alias it if that matters to you.
One more class covers a rare case: ResultDeliveryError means a result was fetched but could not
be handed back (a disk write failed, say). It carries the text on .markdown, because a result is
delivered once and the API cannot serve it again.
Related packages
| Package | Import | Purpose |
|---|---|---|
llama-index-readers-hexread |
llama_index.readers.hexread |
LlamaIndex reader |
langchain-hexread |
langchain_hexread |
LangChain document loader |
License
Licensed under the MIT License, © HexWorld Solutions GmbH.
The HexRead service and its documentation live at hexread.com; this repository contains the open-source client only.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hexread-0.1.0.tar.gz.
File metadata
- Download URL: hexread-0.1.0.tar.gz
- Upload date:
- Size: 22.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7544ae7da51fb3c3825d09795f5128d9c6a4438765ab615a5a55ecc7e0ea6b99
|
|
| MD5 |
d760e0cf845117b56947f84b5964ff36
|
|
| BLAKE2b-256 |
ae1d4721713b12026bd52c04378e26a4ea3c717c801d2913fd54f8e782e9e1f1
|
Provenance
The following attestation bundles were made for hexread-0.1.0.tar.gz:
Publisher:
release.yml on HexWorldEU/hexread-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hexread-0.1.0.tar.gz -
Subject digest:
7544ae7da51fb3c3825d09795f5128d9c6a4438765ab615a5a55ecc7e0ea6b99 - Sigstore transparency entry: 2344907893
- Sigstore integration time:
-
Permalink:
HexWorldEU/hexread-python@7872f282d78c73b123e9c3db2fddcdd07436fc09 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/HexWorldEU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7872f282d78c73b123e9c3db2fddcdd07436fc09 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hexread-0.1.0-py3-none-any.whl.
File metadata
- Download URL: hexread-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8af174af89d53965a488a4a5ab43ecc739efe7c4a13f482dc0cbeeef56682b67
|
|
| MD5 |
3f510ed52255b1c92d5b9a7da83fce0f
|
|
| BLAKE2b-256 |
b88a0748b061d3809fc6efe05e133ed55f86a8d7eec5ab9740329ac4e2194441
|
Provenance
The following attestation bundles were made for hexread-0.1.0-py3-none-any.whl:
Publisher:
release.yml on HexWorldEU/hexread-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hexread-0.1.0-py3-none-any.whl -
Subject digest:
8af174af89d53965a488a4a5ab43ecc739efe7c4a13f482dc0cbeeef56682b67 - Sigstore transparency entry: 2344907943
- Sigstore integration time:
-
Permalink:
HexWorldEU/hexread-python@7872f282d78c73b123e9c3db2fddcdd07436fc09 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/HexWorldEU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7872f282d78c73b123e9c3db2fddcdd07436fc09 -
Trigger Event:
push
-
Statement type: