oxedi
A fast, lossless parser and writer for X12 EDI 835 files (electronic remittance advice). The core is written in Rust and is available from Python and from DuckDB.
- Lossless. Every byte of the file is kept. Nothing is silently dropped, and the file can be written back exactly as it was read.
- Ready-made tables. Payments, claims, service lines and adjustments come out as typed tables that open in Polars, pandas, pyarrow or DuckDB.
- Problems are reported, not raised. Questionable data still parses; each issue is a diagnostic that names the rule, the position and the value.
Install
pip install oxedi # no Python dependencies
pip install "oxedi[polars]" # with Polars
pip install "oxedi[pandas]" # with pandas and pyarrow
pip install "oxedi[pyx12]" # with pyx12 validation
pip install "oxedi[edi-835-parser]" # drop-in layer for edi-835-parser users
Requires Python 3.11 or later. Wheels are available for Linux (x86_64 and aarch64, plus musl on x86_64), macOS (Intel and Apple silicon) and Windows (x86_64).
For DuckDB:
INSTALL oxedi FROM community;
LOAD oxedi;
The community build is not available yet: it waits for the maintainers to merge
duckdb/community-extensions#2942. Until then, build the extension and LOAD the file; see
DuckDB.
Read
import oxedi
result = oxedi.parse_file("remittance.835") # or oxedi.parse(data) from bytes
claims = result.tables["claims"].to_polars() # or .to_pandas()
services = result.tables["services"].to_polars()
print(result.count_claims(), result.sum_payments()) # sum_payments() is a Decimal
print(result.payer["name"], result.payee["name"])
There are five tables: payments, claims, services, adjustments and
provider_adjustments. Child tables point to their parents by row number. Tables also follow
the Arrow PyCapsule interface, so DuckDB and any Arrow-aware library read them directly:
import duckdb
claims = result.tables["claims"]
duckdb.sql("select claim_status, sum(payment_amount) from claims group by 1")
A file that is not an 835 at all raises oxedi.ParseError. Anything else parses, and its
problems come back as diagnostics:
for diagnostic in result.diagnostics:
print(diagnostic)
For large files, stream yields one transaction at a time, so memory stays bounded:
from pathlib import Path
for batch in oxedi.stream(Path("big.835").read_bytes()):
services = batch.tables["services"].to_polars()
To get a parsed file back byte for byte: result.document.write().
Write
oxedi.write turns tables into an 835. Parse, change a value, write:
import datetime
import polars as pl
frames = {name: result.tables[name].to_polars() for name in result.tables.keys()}
frames["payments"] = frames["payments"].with_columns(trace_number=pl.lit("CHK100235"))
envelope = oxedi.Envelope(
sender_id="ACMEPAYER",
receiver_id="SUNRISECLINIC",
date=datetime.date(2024, 1, 10),
time=datetime.time(9, 0),
usage_indicator="T", # "P" (production) is the default
)
data = oxedi.write(frames, envelope, spec=result.spec) # bytes, one interchange
Pass spec=result.spec so the tables are written with the spec that parsed them (4010 or
5010). The writer is strict: if the file would not read back clean, or the money does not
balance, it raises oxedi.WriteError listing every finding, and writes nothing. To get the
file anyway, for example to build invalid test files:
try:
data = oxedi.write(frames, envelope, spec=result.spec)
except oxedi.WriteError as error:
for finding in error.findings:
print(finding.table, finding.row, finding.column, finding)
data, findings = oxedi.write(frames, envelope, spec=result.spec, allow_findings=True)
DuckDB
FROM read_835('remits/*.835', table_name := 'services', filename := true);
-- the findings of the parse, one row each
FROM read_835('remits/*.835', table_name := 'diagnostics', ignore_errors := true);
Write tables back with COPY. This needs extension 0.2.0 or later; until it reaches the
community repository, build the extension. payments, claims and the rest are tables or
views with those names, for example CREATE TEMP TABLE claims AS FROM read_835('remittance.835', table_name := 'claims'), changed in SQL before writing:
COPY (
SELECT {
'payments': (SELECT list(t ORDER BY t."row") FROM payments t),
'claims': (SELECT list(t ORDER BY t."row") FROM claims t),
'services': (SELECT list(t ORDER BY t."row") FROM services t),
'adjustments': (SELECT list(t ORDER BY t."row") FROM adjustments t),
'provider_adjustments': (SELECT list(t ORDER BY t."row") FROM provider_adjustments t)
}
) TO 'out.835' (FORMAT edi835, sender_id 'ACMEPAYER', receiver_id 'SUNRISECLINIC',
date DATE '2024-01-10', time TIME '09:00');
Validate with pyx12
import oxedi
from oxedi.pyx12 import validate
result = oxedi.parse_file("remittance.835")
findings = result.diagnostics + validate("remittance.835") # bytes, a path or a binary file
for d in sorted(findings, key=lambda d: d.level):
print(d.origin, d.code, d) # "oxedi" or "pyx12"
parse never calls pyx12; validate does, on demand, and returns the same Diagnostic type.
Spec versions
parse, parse_file and stream read the version a file declares and use the matching
built-in spec: 5010 (005010X221A1) by default, 4010 (004010X091A1) for 4010 files. Pass
spec=oxedi.Spec.builtin("4010") to choose one. A JSON patch adds segments or table columns
without touching the code; see The spec.
More detail
| Guide | What is in it |
|---|---|
| Reading | Tables in depth, large files, diagnostics, the document |
| Writing | The envelope, findings, balancing rules, writing from your own tables |
| DuckDB | read_835 parameters, COPY options, accepted types, limits |
| The spec | Spec versions and patches |
| Validating with pyx12 | What validate reports and where |
| Migrating from edi-835-parser | Change the import, or move to the native API |
Versioning
The project follows semantic versioning. While the version is 0.x, a minor release may
change the API. See CHANGELOG.md. The DuckDB extension is versioned
separately.
Contributing
See CONTRIBUTING.md.
License
MIT. See LICENSE and THIRD_PARTY_NOTICES.
Metadata
Release files for oxedi 0.5.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 | |
|---|---|---|---|
| oxedi-0.5.0.tar.gz | 343.0 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| oxedi-0.5.0-cp311-abi3-win_amd64.whl | CPython 3.11 | abi3 | Windows x86-64 | Details |
| oxedi-0.5.0-cp311-abi3-musllinux_1_2_x86_64.whl | CPython 3.11 | abi3 | Linux musl 1.2+ x86-64 | Details |
| oxedi-0.5.0-cp311-abi3-manylinux_2_28_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| oxedi-0.5.0-cp311-abi3-manylinux_2_28_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| oxedi-0.5.0-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details |
| oxedi-0.5.0-cp311-abi3-macosx_10_12_x86_64.whl | CPython 3.11 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 8.1 MB
Release files / oxedi-0.5.0.tar.gz
| Download URL | oxedi-0.5.0.tar.gz |
|---|---|
| Size | 343.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d4a99df0d3609c78b274d8cafcee9937e8c05805d70d89e54b968dfc71257e08
|
|
BLAKE2b-256 checksum How to use checksums |
91f572c3d28663d58cf6c719bee7ba7d00bb0ed94454cfe203db33a2d20de543
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-win_amd64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-win_amd64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | CPython 3.11 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
91d9d81585bfa74b2c92c3ea809d9e22b4bde2ac0d23c18db246fc5dcb055a0a
|
|
BLAKE2b-256 checksum How to use checksums |
852f47d03b5e73e64e2e37c8ac895f19fc4a2465da183dc9c8bc50666e56eab3
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-musllinux_1_2_x86_64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.6 MB |
| Tags | CPython 3.11 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
407b489bfd9a291fa2541c4ca2b24126ae55cfa150b2f7b06f3c49ca2bf71c72
|
|
BLAKE2b-256 checksum How to use checksums |
b2581d2f908ebd75379a62c96831b87d2fd009e635fa893a790f6bbdaafea99c
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-manylinux_2_28_x86_64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.11 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d00fcb11ca325351e8a0b16a1ee08999e819efe3f9eea8ed7c43cb21b98c0148
|
|
BLAKE2b-256 checksum How to use checksums |
ce1ff51c37a505f2a38104fb2278c7a283eac6c04620770594f5fdbe12ec555f
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-manylinux_2_28_aarch64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.11 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
fea499c0ecc7291add2a7741acba741343e6d7e4d98242ad397454fb506be50f
|
|
BLAKE2b-256 checksum How to use checksums |
b3aa61e0100902e83b84d8498bc61101f57ad0c74d932382e0aa359509d389de
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
7514960f269ab084ef43833c263d339a5b461c689d31c101b732fec8e65f4282
|
|
BLAKE2b-256 checksum How to use checksums |
3235b2ad5f9991b16c95cb388872be4541bede25b8e1a331e91e5e6504de9db6
|
| 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 12, 2026.
Transparency logRelease files / oxedi-0.5.0-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | oxedi-0.5.0-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
bd347c7833b8bab53b46a133caec7c6f61878b1c401f740a43108c25684af095
|
|
BLAKE2b-256 checksum How to use checksums |
7585adaf663d055c9f0f38b091cc67da86e90333d18cc05814d399ce3ea362d6
|
| 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 12, 2026.
Transparency log