docfig
Check bank statement transaction tables for reconciliation errors, offline and free. Convert statement PDFs to checked JSON, Excel and CSV with the DocFig API.
pip install docfig # add [excel] to read .xlsx files: pip install "docfig[excel]"
Free reconciliation check
Scraped or exported bank statements often have corrupted tables: a debit in the credit column, a dropped row, a row copied twice, a misread amount. docfig check finds them. No API key, no network, no cost.
docfig check statement.csv
docfig check statement.csv --opening 2841.17 --closing 11023.40
docfig check statement.csv --json # for scripts and CI
docfig check: statement.csv
27 rows, 26 balance checks. Passed: 24, flagged: 3.
Opening 2841.17 (inferred), closing 11023.40 (inferred). Money in 11501.94, money out 3434.66.
3 problems:
- [row 9] sign_error: Row 9: 150.88 is in the wrong column; the balance says it is a debit.
Fix: Move 150.88 to the debit column.
- [row 15] duplicate: Row 15 repeats row 14: same date, description, amount and balance.
Fix: Delete row 15 if it was copied or exported twice.
- [row 20] amount_typo: Row 20: the balance says this amount should be -180.80, not -18.08 (a zero looks dropped or added).
Fix: Amount 18.08 likely 180.80.
Starting from PDFs? docfig.com/api
What it checks, row by row:
- Running balance: every balance must equal the previous balance plus the rows in between (in integer cents, so 0.1 + 0.2 never fails). Oldest-first or newest-first order is detected from the balances.
- Swapped debit/credit columns for the whole file, and sign errors or figures in the wrong column on single rows.
- Mistyped amounts: "amount 125.00 likely 1,250.00" (a dropped zero, a misplaced decimal point, two swapped digits, one misread digit).
- Missing rows: a gap in the balance chain, with the amount that is missing and where.
- Duplicated rows, dates out of order (including day and month swapped), totals against
--openingand--closing.
It reads what real exports look like: headers such as Date / Narration / Withdrawal Amt. / Deposit Amt. / Closing Balance, a signed Amount column or separate Debit and Credit columns, a Dr/Cr column, CR/DR suffixes, (1,234.56) negatives, £-12.50, decimal commas (1.234,56), Indian lakh grouping (1,23,456.78), ; or tab delimiters, preamble lines above the header, balance brought forward lines, and dates as 2026-03-31, 31/03/2026, 03/31/2026, 31.03.2026, 31-Mar-26 or Mar 31, 2026. A credit card table, where the balance is the amount owed, takes --type card.
Exit codes: 0 clean, 1 problems found, 2 the file could not be read as a transactions table. The same logic powers the checks on docfig.com, and the Node package (npm install docfig-api) gives identical results.
From Python:
import docfig
report = docfig.check_file("statement.csv", opening="2841.17")
print(report["status"], report["flagged"])
for p in report["problems"]:
print(p["line"], p["kind"], p["message"], p["fix"])
print(docfig.format_report(report))
Convert statement PDFs (DocFig API)
Get a free key at docfig.com/api (email code, no card): 100 pages a month. Text PDFs, scanned PDFs, password-protected PDFs and photos (JPG, PNG, HEIC).
import docfig
client = docfig.Client() # reads DOCFIG_API_KEY
st = client.convert("statement.pdf") # upload, wait, return the result
print(st.bank, st.account["last4"], st.period)
print(st.checks["status"], st.checks["rows_flagged"])
for t in st.transactions:
print(t["date"], t["description"], t["amount"], t["balance"], t["flags"])
st.download("statement.xlsx") # the same workbook as the website, or .csv
locked = client.convert("locked.pdf", password="1234")
combined = client.combine(["jan.pdf", "feb.pdf", "apr.pdf"])
for p in combined.problems:
print(p["kind"], p["text"]) # "Missing statement: 1 Mar 2026 to 31 Mar 2026 ..."
combined.download("all-statements.xlsx")
print(client.usage()) # pages used and left this month
client.upload(file) returns at once; client.get(id), client.wait(id), client.download(id, "csv") and client.delete(id) work with the id. Results are deleted from DocFig an hour after upload.
From the shell: docfig convert statement.pdf -o statement.xlsx (or .csv, .json).
Errors
All errors derive from docfig.DocFigError and carry .code and .status:
| Exception | When |
|---|---|
AuthenticationError |
missing, invalid or revoked key |
PasswordRequiredError, WrongPasswordError |
locked PDF (.attempts_left) |
InvalidFileError |
empty, too large, too many pages, unsupported, unreadable, or refused by the virus scan |
LimitReachedError |
the month's pages are used (.upgrade_url) |
RateLimitError |
too many requests (.retry_after); retried automatically first |
ConversionFailedError |
the statement could not be read |
ServiceUnavailableError |
daily capacity reached or busy; retried first |
NotFoundError, DocFigTimeoutError, APIConnectionError |
unknown or expired id, waited too long, network |
Options: docfig.Client(api_key=None, base_url="https://docfig.com", timeout=60, max_retries=3, poll_interval=2.0).
MCP server for AI assistants
docfig mcp runs a Model Context Protocol server on stdio, so Claude Desktop, Cursor, VS Code and other MCP apps can use DocFig directly:
check_transactions: check a transactions table (CSV, TSV or rows) for balance errors. Free, offline, no key. Same result asdocfig check --json.convert_statement: a statement PDF or photo (local path, https URL or base64) to checked transactions, with Excel and CSV links valid for one hour. NeedsDOCFIG_API_KEY.combine_statements: several statements to one set of accounts, with missing months and duplicated rows found.usage: pages left this month.
{
"mcpServers": {
"docfig": {
"command": "uvx",
"args": ["docfig", "mcp"],
"env": { "DOCFIG_API_KEY": "dfk_..." }
}
}
}
With pip instead of uv: pip install docfig, then use "command": "docfig", "args": ["mcp"]. No install at all: the hosted server is at https://mcp.docfig.com/mcp (Streamable HTTP; send the key as Authorization: Bearer dfk_...). Setup for each app: docfig.com/api#mcp.
Privacy
The offline check never sends anything anywhere. For the API: files are virus scanned, read in memory, account numbers are masked before the AI step, and results are deleted after an hour. See docfig.com/security.
License
MIT
Metadata
Release files for docfig 0.2.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 |
|---|---|---|---|---|
| docfig-0.2.0.tar.gz | 53.3 kB | Without provenance | Details for docfig-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| docfig-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for docfig-0.2.0-py3-none-any.whl |
Total release size: 93.6 kB
Release files / docfig-0.2.0.tar.gz
| Download URL | docfig-0.2.0.tar.gz |
|---|---|
| Size | 53.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e13d24ed5152f6756e7971aa5e2db863ea2e75f822ed1183edd1536f26916a55
|
|
BLAKE2b-256 checksum How to use checksums |
0f59159de316e9c348689a7d3ce668b8c993097d4e4fb6a081d676d6af1d40f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / docfig-0.2.0-py3-none-any.whl
| Download URL | docfig-0.2.0-py3-none-any.whl |
|---|---|
| Size | 40.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
40a877f350d9df157581138df34289417936ec7aa4cc2f33732d238fc48b6006
|
|
BLAKE2b-256 checksum How to use checksums |
cc785be76bd1680351ccc816c5bc41459d82678a45e82d61b1cc33dd298206f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for docfig 0.2.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.