openextract reads documents, images, audio, and video with the LLM of your choice and hands back a typed Pydantic object. Not a string. Not a dict. The model you asked for, already validated.
Installs every provider SDK. For just one, use its extra, like [anthropic]. Set its API key and call extract.
pip install 'openextract[all]'
0000 25 50 44 46 2d 31 2e 35 %PDF-1.5 0008 0a 25 8f 0a 31 30 30 20 .%..100 0010 30 20 6f 62 6a 0a 3c 3c 0 obj.<< 0018 20 2f 46 69 6c 74 65 72 /Filter 0020 20 2f 46 6c 61 74 65 44 /FlateD 0028 65 63 6f 64 65 20 2f 4c ecode /L
INVOICE No. 2291 Halvorsen Print Co. Kanalgata 14, 5003 Bergen, Norway Issued 12 Sep 2026 Due 12 Oct 2026 Letterpress run, 500 cards ........... 860.00 Cotton stock, 600 gsm ................ 380.00 Total due ...................... 1,240.00 EUR
Halvorsen Print Co.1
Kanalgata 14, 5003 Bergen, Norway
| Letterpress run, 500 cards | 860.00 |
| Cotton stock, 600 gsm | 380.00 |
| Total due | 1,240.002 EUR3 |
from pydantic import BaseModel
from openextract import extract
class Invoice(BaseModel):
vendor: str
total: float
currency: str
invoice = extract(
Invoice,
"openai:gpt-5",
source="./invoice.pdf", # path to the file
)
extract(schema, model, source, instructions=None, *, media_type=None)
It takes the first answer it finds: the media_type you passed, the file extension, the URL's Content-Type header, then the file's leading bytes.
Every error subclasses ExtractionError and keeps the original exception as its cause.
Any model pydantic-ai supports. Install the matching extra and set its API key.
pip install 'openextract[anthropic]' # openai, google, bedrock, groq, mistral,
# cohere, huggingface, openrouter, xai, all
Yes. It takes the same arguments. Await it.
invoice = await extract_async(Invoice, "openai:gpt-5", source="./invoice.pdf")