Parseur Python Client API¶
This library provides a Python client and CLI to interact with the Parseur.com API.
Installation¶
pip install parseur-py
Quick Start¶
import parseur
parseur.api_key = "YOUR_API_KEY"
for mailbox in parseur.Mailbox.list():
print(mailbox["name"])
Configuration¶
The CLI can save and load configuration from a file:
By default:
~/.parseur.conf- Fields:
api_key: Your Parseur API token
api_base: (Optional) Override the Parseur API base URL
Example:
parseur init --api-key YOUR_API_TOKEN
Overview¶
API Key¶
Set a global API key once:
import parseur
parseur.api_key = "YOUR_API_KEY"
Every API method also accepts an api_key keyword argument that takes
priority over the global one for that single call. This is handy for
multi-account or multi-tenant usage:
parseur.Mailbox.list(api_key="sk_account_a")
parseur.Document.upload_file(123, "invoice.pdf", api_key="sk_account_b")
parseur.ParserField.add(123, "Total", parseur.FieldFormat.NUMBER, api_key="sk_account_b")
Enums¶
ParseurEvent¶
Supported webhook events:
from parseur import ParseurEvent
ParseurEvent.DOCUMENT_PROCESSED
ParseurEvent.TABLE_PROCESSED
DocumentOrderKey¶
Used to sort documents:
from parseur import DocumentOrderKey
DocumentOrderKey.NAME
DocumentOrderKey.CREATED
DocumentOrderKey.PROCESSED
DocumentOrderKey.STATUS
MailboxOrderKey¶
Used to sort mailboxes:
from parseur import MailboxOrderKey
MailboxOrderKey.NAME
MailboxOrderKey.DOCUMENT_COUNT
MailboxOrderKey.TEMPLATE_COUNT
MailboxOrderKey.PARSEDOK_COUNT
DocumentStatus¶
Document processing status:
from parseur import DocumentStatus
DocumentStatus.INCOMING
DocumentStatus.PARSEDOK
DocumentStatus.EXPORTKO
AIEngine¶
AI engine accepted when creating/updating a mailbox:
from parseur import AIEngine
AIEngine.DISABLED # Disabled (template-based parsing only)
AIEngine.GCP_AI_2_5 # AI Text engine v2.5
AIEngine.GCP_AI_3_TXT # AI Text engine v3
AIEngine.GCP_AI_2 # AI Vision engine v3
FieldFormat¶
Format of a parser field:
from parseur import FieldFormat
FieldFormat.TEXT
FieldFormat.NUMBER
FieldFormat.DATE
FieldFormat.TABLE
ExportType¶
Type of an export configuration:
from parseur import ExportType
ExportType.PARSER # document-level export
ExportType.PARSER_FIELD # a table field's rows
EmailProcessing¶
How a mailbox processes incoming emails and attachments:
from parseur import EmailProcessing
EmailProcessing.EMAILS_AND_ATTACHMENTS # process both
EmailProcessing.EMAILS_ONLY # skip attachments
EmailProcessing.ATTACHMENTS_ONLY # skip the email body
DateFormat¶
How to read ambiguous dates in documents:
from parseur import DateFormat
DateFormat.MONTH_FIRST # mm/dd/yyyy
DateFormat.DAY_FIRST # dd/mm/yyyy
DecimalSeparator¶
Decimal separator for numbers in documents:
from parseur import DecimalSeparator
DecimalSeparator.DOT # 123.45
DecimalSeparator.COMMA # 123,45
SenderFilter¶
How a mailbox filters incoming senders:
from parseur import SenderFilter
SenderFilter.ALLOWLIST # only the listed emails/domains are accepted
SenderFilter.BLOCKLIST # the listed emails/domains are rejected
Metadata¶
Per-document metadata columns a mailbox can expose. An IntFlag, so columns
compose with | (see Mailbox.set_metadata()):
from parseur import Metadata
Metadata.SUBJECT | Metadata.SENDER | Metadata.RECEIVED
# other members: ATTACHMENTS, CC, BCC, TO, DOCUMENT_URL, PAGE_COUNT, ... (37 total)
Methods¶
Mailboxes¶
List all mailboxes, with optional search and sorting.
search: filter by mailbox name or email prefix
order_by: MailboxOrderKey
ascending: bool
mailboxes = parseur.Mailbox.list(search="Invoices", order_by=MailboxOrderKey.NAME) for m in mailboxes: print(m)
Get details of a mailbox.
mailbox = parseur.Mailbox.retrieve(mailbox_id=123) print(mailbox)
Get the schema of a mailbox.
schema = parseur.Mailbox.schema(mailbox_id=123) print(schema)
Download every parsed result of the mailbox as a single file (one row per processed document). Returns the file content as bytes.
csv_bytes = parseur.Mailbox.download(123) # CSV (default) json_bytes = parseur.Mailbox.download(123, "json") # JSON xlsx_bytes = parseur.Mailbox.download(123, "xlsx") # XLSX
Create a mailbox. All fields are optional (Parseur generates a name and email address when omitted). The request body is validated and serialized with
MailboxCreateSchema; invalid values or unknown/read-only fields raisemarshmallow.ValidationErrorbefore any request is sent. By default the AI Vision engine (GCP_AI_2) is used, and — when no fields are predefined — identificationREQUESTEDso Parseur auto-detects the fields.mailbox = parseur.Mailbox.create( name="Invoices", ai_engine="GCP_AI_2", retention_policy=30, ) print(mailbox)
Update a mailbox. Only the fields you pass are changed. The body is validated and serialized with
MailboxUpdateSchema.mailbox = parseur.Mailbox.update(123, name="Renamed", ai_engine="GCP_AI_2_5") print(mailbox)
Beyond the generic update, each mailbox setting has a dedicated,
self-validating helper (a thin wrapper over update that targets one
setting). They all return the updated mailbox.
Rename a mailbox.
parseur.Mailbox.rename(123, "Invoices EU")
Change the AI engine used to extract data.
from parseur import AIEngine parseur.Mailbox.set_ai_engine(123, AIEngine.GCP_AI_2_5)
Set (or clear) the natural-language extraction instructions.
parseur.Mailbox.set_ai_instructions(123, "Extract the grand total") parseur.Mailbox.set_ai_instructions(123, None) # clear
Set the input formats — timezone, date format, and decimal separator. All three are nullable: pass
Noneto reset to auto / the account default.from parseur import DateFormat, DecimalSeparator parseur.Mailbox.set_timezone(123, "Europe/Paris") parseur.Mailbox.set_date_format(123, DateFormat.DAY_FIRST) parseur.Mailbox.set_decimal_separator(123, DecimalSeparator.COMMA)
Choose how incoming emails are processed.
from parseur import EmailProcessing parseur.Mailbox.set_email_processing(123, EmailProcessing.ATTACHMENTS_ONLY)
Filter incoming senders by an allow- or block-list (pass
[]to clear).from parseur import SenderFilter parseur.Mailbox.set_sender_filter(123, SenderFilter.ALLOWLIST, ["acme.com"])
Restrict which file types (“Files to process”) are accepted. Each value is validated against
SUPPORTED_FILE_EXTENSIONS; passNoneto accept every supported type again.parseur.Mailbox.set_allowed_extensions(123, ["pdf", "docx"])
Choose the per-document metadata columns.
Metadatais anIntFlag, so columns are enabled/disabled in parallel with|; columns not listed are left unchanged.from parseur import Metadata parseur.Mailbox.set_metadata( 123, enable=Metadata.SUBJECT | Metadata.SENDER, disable=Metadata.TO )
Restrict which pages are processed — by page range, or to odd/even pages. Pass
enabled=Falseto clear a restriction.parseur.Mailbox.process_page_range(123, [{"start_index": 1, "end_index": 5}]) parseur.Mailbox.process_odd_pages(123) parseur.Mailbox.process_even_pages(123, enabled=False)
Configure document splitting (the split itself runs per document via
Document.split()). Passenabled=Falseto turn a method off.parseur.Mailbox.split_by_ai(123, "one invoice per page") parseur.Mailbox.split_by_page(123, 2) parseur.Mailbox.split_by_page_range(123, [{"start_index": 1, "end_index": 5}]) parseur.Mailbox.split_by_keywords(123, [{"keyword": "Invoice", "is_before": True}]) parseur.Mailbox.split_by_ai(123, enabled=False)
Note
The advanced toggles force_ocr, expand_result,
disable_document_links, disable_deskew, extract_xml_from_comment
and input_date_format_autodetection are intentionally not exposed.
Delete a mailbox.
parseur.Mailbox.delete(mailbox_id=123) print("Deleted!")
Parser Fields¶
Parseur has no per-field endpoint: a mailbox’s fields live in its
parser_object_set and are written back through the mailbox.
ParserField reads the current fields, applies your change, and persists it.
Every write is validated and serialized through ParserFieldWriteSchema, so
an invalid format or an unknown property raises
marshmallow.ValidationError before any request is sent.
List the fields of a mailbox.
fields = parseur.ParserField.list(mailbox_id=123) for field in fields: print(field["id"], field["name"], field["format"])
Add a field (keeping the existing ones).
from parseur import FieldFormat parseur.ParserField.add( 123, name="Total", field_format=FieldFormat.NUMBER, query="the grand total of the invoice", )
Update a single field by id (only the provided properties change).
parseur.ParserField.update(123, "PF12345", name="Grand Total")
Delete a field by id.
parseur.ParserField.delete(123, "PF12345")
Download a table field’s rows as a single file (one row per line item). Returns the file content as bytes.
csv_bytes = parseur.ParserField.download(123, "PF12345") # CSV xlsx_bytes = parseur.ParserField.download(123, "PF12345", "xlsx") # XLSX
Documents¶
List all documents in a mailbox with optional filtering.
search: Searches document id, name, template, email addresses, metadata
order_by: DocumentOrderKey
received_after / received_before: datetime.date
with_result: bool
from datetime import datetime documents = parseur.Document.list( mailbox_id=123, search="invoice", order_by=DocumentOrderKey.PROCESSED, ascending=False, received_after=datetime(2024, 1, 1), with_result=True ) for doc in documents: print(doc)
Get document details.
document = parseur.Document.retrieve(document_id="abcd-1234") print(document)
Reprocess a document. Asynchronous: returns a notification_set (not the document); poll with
retrieve/waitto see the new result.notifications = parseur.Document.reprocess(document_id="abcd-1234") print(notifications)
Skip a document. Returns the updated document.
document = parseur.Document.skip(document_id="abcd-1234") print(document)
Copy document to another mailbox.
result = parseur.Document.copy(document_id="abcd-1234", target_mailbox_id=456) print(result)
Split a multi-page document (or undo a split). Both are asynchronous and return a notification_set.
parseur.Document.split(document_id="abcd-1234") parseur.Document.reverse_split(document_id="abcd-1234")
Retrieve logs for a document.
logs = parseur.Document.logs(document_id="abcd-1234") for log in logs: print(log)
Delete a document.
parseur.Document.delete(document_id="abcd-1234") print("Deleted!")
Uploads¶
Upload a local file.
result = parseur.Document.upload_file(mailbox_id=123, file_path="/path/to/file.pdf") print(result)
Upload text/email content.
result = parseur.Document.upload_text( recipient="inbox@parseur.net", subject="Invoice 123", sender="billing@example.com", body_html="<p>Here is your invoice</p>" ) print(result)
Upload and wait for processing (synchronous). Polls until the document reaches a final status (
PARSEDOK,PARSEDKO,EXPORTKO, …). The cadence is fixed: a check every 5 seconds, for up to 10 minutes (parseur.document.POLL_INTERVAL/MAX_WAIT); aTimeoutErroris raised if it is still processing after that.document = parseur.Document.upload_file_and_wait(123, "/path/to/file.pdf") print(document["status"], document["result"]) # Same for email/text content: document = parseur.Document.upload_text_and_wait( recipient="inbox@parseur.net", subject="Invoice 123", body_plain="Total: 42" ) # Or poll an already-uploaded document: document = parseur.Document.wait("abcd-1234") # Render progress with the on_poll callback: parseur.Document.upload_file_and_wait( 123, "/path/to/file.pdf", on_poll=lambda elapsed, status: print(status, f"{elapsed:.0f}s"), )
On the CLI, add
--waitto show a live progress bar with an ETA:parseur upload-file 123 invoice.pdf --wait
Webhooks¶
Create a new custom webhook for documents or tables.
result = parseur.Webhook.create( event=ParseurEvent.DOCUMENT_PROCESSED, target_url="https://example.com/webhook", mailbox_id=123, headers={"X-Custom-Header": "value"}, name="My Webhook" ) print(result)
Get webhook details.
webhook = parseur.Webhook.retrieve(webhook_id=789) print(webhook)
Delete an existing webhook by its ID.
parseur.Webhook.delete(webhook_id=789) print("Webhook deleted.")
Enable a webhook for a specific mailbox.
mailbox = parseur.Webhook.enable(mailbox_id=123, webhook_id=789) print(mailbox)
Pause (disable) a webhook for a specific mailbox.
mailbox = parseur.Webhook.pause(mailbox_id=123, webhook_id=789) print(mailbox)
Retrieve a list of all registered webhooks.
webhooks = parseur.Webhook.list() for webhook in webhooks: print(webhook)
Exports¶
There are three ways to get a mailbox’s results out as a file:
the whole mailbox, one row per document —
Mailbox.download();a single table field, one row per line item —
ParserField.download();a custom column selection — an export configuration, described below.
An export configuration selects which columns to export for a mailbox
(PARSER) or one of its table fields (PARSER_FIELD), and exposes
csv_download / xls_download URLs for the resulting file.
Discover the columns you can export.
groups = parseur.ExportConfig.available_fields(mailbox_id=123) for group in groups: print(group["type"], group["items"])
Create, list, retrieve, update and delete export configurations.
config = parseur.ExportConfig.create( 123, name="Invoices CSV", items=["InvoiceNumber", "TotalDue"] ) parseur.ExportConfig.list(123) parseur.ExportConfig.retrieve(123, config["id"]) parseur.ExportConfig.update(123, config["id"], name="Renamed") parseur.ExportConfig.delete(123, config["id"])
For a table field, pass
export_type=ExportType.PARSER_FIELDand theparser_field_id.Download the configured export (bytes).
csv_bytes = parseur.ExportConfig.download(123, config["id"]) # CSV xlsx_bytes = parseur.ExportConfig.download(123, config["id"], "xlsx") # XLSX
Command Line Interface¶
The CLI covers reading mailboxes and their fields, document operations (including synchronous uploads and downloading results), exports, and webhooks. Mailbox creation/update/deletion and parser-field management (add/update/ delete) are available through the Python API and the MCP server, but not the CLI.
parseur init --api-key YOUR_TOKEN
parseur list-mailboxes
parseur get-mailbox 123
parseur get-mailbox-schema 123
parseur list-parser-fields 123
parseur list-documents 456 --search invoice --order-by status --descending
# Synchronous upload: block until the document is parsed (live progress bar).
parseur upload-file 123 /path/to/file.pdf --wait
parseur upload-text --recipient inbox@parseur.net --subject "Invoice" --body-plain "Total: 42" --wait
# Download results as a file (stdout by default, or --output FILE).
parseur download-mailbox 123 --format csv -o results.csv # whole mailbox
parseur download-field 123 PF951 --format xlsx -o lines.xlsx # a table field
parseur list-export-configs 123
parseur download-export 123 10 -o custom.csv # a custom export
parseur create-webhook --event document.processed --target-url https://example.com/webhook
MCP Server¶
parseur-py includes a Model Context Protocol
server that exposes the client as tools for AI assistants (Claude Desktop,
Cursor, Claude Code, …). It speaks MCP over stdio and reads your API key
from ~/.parseur.conf or the PARSEUR_API_KEY environment variable.
Install the extra:
pip install "parseur-py[mcp]"
Run the server:
parseur mcp
# or: parseur-mcp
# or: python -m parseur.mcp_server
Example Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"parseur": {
"command": "parseur-mcp",
"env": {
"PARSEUR_API_KEY": "YOUR_PARSEUR_API_KEY"
}
}
}
}
Every tool carries a title, a description, per-argument descriptions,
behavioral annotations (read-only / destructive / idempotent / open-world
hints) and a structured JSON output schema, so the assistant knows exactly what
each tool does and what it returns. Read-only tools (list_* / get_*) are
safe to call freely; destructive tools (delete_*) are flagged so clients can
ask for confirmation first.
Tools¶
The server exposes the full client surface as 54 tools:
Mailboxes:
list_mailboxes,get_mailbox,get_mailbox_schema,create_mailbox,delete_mailboxMailbox settings (one tool per setting, instead of a generic update):
rename_mailbox,set_ai_engine,set_ai_instructions,set_email_processing,set_metadata,set_timezone,set_date_format,set_decimal_separator,set_allowed_extensions,set_sender_filter,split_by_ai,split_by_page,split_by_page_range,split_by_keywords,process_page_range,process_odd_pages,process_even_pagesParser fields:
list_parser_fields,add_parser_field,update_parser_field,delete_parser_fieldDocuments:
list_documents,get_document,get_document_logs,reprocess_document,skip_document,copy_document,split_document,reverse_split_document,delete_documentUploads:
upload_file,upload_text(asynchronous) andupload_file_and_wait,upload_text_and_wait,wait_for_document(block until the document is parsed)Webhooks:
list_webhooks,get_webhook,create_webhook,delete_webhook,enable_webhook,pause_webhookExports:
get_mailbox_export,get_table_export,list_export_fields,list_export_configs,get_export_config,create_export_config,update_export_config,delete_export_config
Workflow¶
The tools are designed around the lifecycle of a Parseur mailbox. The server’s own instructions describe the same flow so the assistant can follow it unassisted.
Create a mailbox with just a title (
create_mailbox). Do not define fields up front: Parseur auto-detects them from the first documents during its identification phase. Adjust them afterwards withadd_parser_field/update_parser_field/delete_parser_field.Send documents to parse with
upload_file(a path on the server’s machine) orupload_text(email/HTML content).Wait for the result. Parsing is asynchronous: a document is pending while its status is
INCOMING/ANALYZING/PROGRESSand finished atPARSEDOK(orPARSEDKO/EXPORTKO). The extracted data lives in the document’sresultfield, populated only once it reachesPARSEDOK. To get the result in a single call, preferupload_file_and_wait/upload_text_and_wait; to wait on an already-uploaded document usewait_for_document.Get the data out as a file — three kinds of export:
the whole mailbox, one row per document —
get_mailbox_export;one table field, one row per line item —
get_table_export;a custom column selection —
list_export_fieldsthencreate_export_config.
Each returns ready-to-use, self-authenticating
csv/json/xlsxdownload links. Alternatively,create_webhookpushes each parsed document to a URL in real time.
IDs follow a simple convention: mailboxes and webhooks use an integer id,
documents use a string id, parser/table fields use a PF... string id, and
export configs use an integer id.
Publishing to the MCP Registry¶
The server is described by server.json at the repository root and can be
published to the official MCP Registry
under the io.github.parseur/parseur-py name. Ownership is verified through
the GitHub parseur organization and the mcp-name marker shipped in the
PyPI README.
# 1. Install the publisher CLI
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
# 2. Authenticate (you must be a member of the `parseur` GitHub org)
./mcp-publisher login github
# 3. Publish the version described in server.json
./mcp-publisher publish
Keep the version in server.json in sync with the parseur-py release
on PyPI (whose README must contain the mcp-name marker). A GitHub Actions
workflow publishes automatically on each GitHub release. Clients then run the
server with:
uvx --from "parseur-py[mcp]" parseur-py