Skip to main content

xbrlkit

License: MIT

Work with XBRL filings above Arelle: fetch a filing, parse it once into a neutral typed model, and project that model into whichever portable representation you need.

EDGAR ──▶ Arelle ──▶ XbrlModel ──┬──▶ holon.jsonld   (RDF / JSON-LD)
                                 ├──▶ Tavi           (compiled model)
                                 ├──▶ xBRL-JSON      (OIM)
                                 └──▶ …

Arelle stays the parser — nobody should reimplement DTS resolution. What it does not give you is anything ergonomic to hold: ModelXbrl is a large mutable object graph tied to a controller you have to close. XbrlModel is the answer to that — stateless, single-filing, lossless, and the waist every projection hangs off.

The one architectural rule: everything goes through XbrlModel. A feature that reaches into Arelle's ModelXbrl directly is bypassing the waist, and that is the change that turns a kit into a junk drawer.

Projections

Target Status Notes
holon (.holon.jsonld) shipped RDF/JSON-LD, renders in the Holon Viewer
Tavi (.tavi.json) shipped Project Tavi compiled model, PWD-2026-09-01
OIM (.oim.json) shipped xBRL-JSON, checked fact-for-fact against Arelle's own writer
property graph (.lbug, parquet) shipped the RoboSystems sec graph's tables, ids and DDL, as one LadybugDB file per filing; row-identical to the platform's own processor on a 26-filing corpus

The OIM projection is the one with a released reference implementation to check against: Arelle's saveLoadableOIM writes the same document from the same filing. A second writer is redundant as a feature — its value is that every difference is a fidelity bug in the parse or the model, and those same bugs are otherwise silent in the holon output, which has nothing to check it. Current parity is every fact on 3M FY2024 (3,150) and Boeing FY2024 (2,688), and all but one on Microsoft FY2024 (1,855 of 1,856); footnotes are the one construct the model does not carry.

Tavi is a public working draft and its name is explicitly a working title, so treat that projection as tracking a moving target. It has been diffed, object class by object class, against the compiled model Arelle's unreleased XbrlModel plugin (Arelle PR #2418) writes for 3M FY2024; the two agree on every fact outside that plugin's own defects and on every cube. Where the draft left a choice open, the choice and its reason are recorded in SPEC_AMBIGUITIES and carried in the .tavi.gaps.json sidecar --format tavi writes alongside the document — the sidecar also records what the filing carries that the model has nowhere to put, and that file is the point of the projection, not a by-product of it.

Property graph

xbrlkit build --format lpg (with the lpg extra: pip install "xbrlkit[lpg]") writes the filing as a single-file LadybugDB database with the tables the RoboSystems sec graph is built from — the same node labels, relationship types, columns and ids, declared once in xbrlkit.schema — so Cypher written against the shared graph runs on the file and a fact in either is the same row. What the platform adds after projection is not in the file: text blocks stay inline in Fact.value, and the enrichment columns and tables (canonical_concept, canonical_type, FactSet, Classification) are empty. The projection is checked row for row against the platform's own processor on the Filing Ladder's 26-filing corpus; the two explained differences are association ids (random on the platform, derived from the arc here) and exact duplicate arcs inside Arelle's aggregate XBRL-dimensions network, which the derived ids collapse.

from xbrlkit.serialize import to_graph_tables, write_parquet, build_lbug

tables = to_graph_tables(model)          # node and relationship rows, schema order
write_parquet(tables, Path("out/mmm"))   # nodes/*.parquet, relationships/*.parquet
build_lbug(tables, Path("out/mmm.lbug")) # CREATE TABLE … + COPY FROM, one file

Text

xbrlkit.text reads the filing's primary HTML document — no Arelle, no network — and returns its text as sections:

Parser Sections Notes
iXBRLParser every inline-XBRL text block (notes, policies, tables), with the XBRL element names it contains ix:continuation chains resolved; nested continuations and nested text blocks included
NarrativeExtractor the 10-K / 10-Q Items — Business, Risk Factors, Cybersecurity, Properties, MD&A, Market Risk table-of-contents rows and cross-references rejected; a 10-Q's Part I and Part II Items kept apart

Both render HTML tables as markdown pipe tables and split a long section into balanced parts at paragraph boundaries (part, part_count, and a label like "MD&A (2/6)") instead of truncating it. Measured on a 26-filing corpus of 2024–2025 10-Ks and 10-Qs: every text block's full text is carried, where a map of outermost continuations alone lost 15–29% of the note text on nine of the filings, and every target Item starts at its body heading.

from xbrlkit.text import iXBRLParser, NarrativeExtractor

html = open("mmm-20241231.htm").read()
for s in iXBRLParser().parse(html):
  print(s.section_id, s.label, s.word_count, s.xbrl_elements[:3])
for s in NarrativeExtractor().extract(html, form_type="10-K"):
  print(s.section_id, s.label, s.word_count)

Install

As a package

pip install xbrlkit

Exposes the xbrlkit CLI (xbrlkit build …, xbrlkit fetch …, xbrlkit query …) and the library — use this to consume it from another project. Set your SEC User-Agent via the environment (see SEC User-Agent).

From source (development)

# Install the toolchain
brew install uv just

# Install dependencies and provision .env from the template
just install

just install creates .env from .env.example on first run — then set your SEC User-Agent in it.

SEC User-Agent

SEC EDGAR requires a descriptive User-Agent on every request, or it throttles you (empty responses / HTTP 429). just install already created your .env — set your details there:

# .env
SEC_GOV_USER_AGENT="Your Name your@email.com"

.env is loaded automatically by every command. Outside the just workflow, export SEC_GOV_USER_AGENT="Your Name your@email.com" or pass --user-agent.

Usage

# Build a holon.jsonld from a specific filing (-> ./output/)
xbrlkit build --cik 320193 --accno 0000320193-23-000106

# Fetch the latest filing for a ticker (-> ./output/)
xbrlkit fetch --ticker NVDA

# Query consolidated facts in a built holon (in-memory SPARQL)
xbrlkit query --in output/0000320193-23-000106.holon.jsonld --element us-gaap:Assets

From a source checkout, just wraps the same CLI as a shorthand: just build 320193 0000320193-23-000106 and just fetch NVDA.

View & explore

Built holons render in the RoboSystems Holon Viewer — a browser-based reader that renders the financial statements and lets you ask questions of the report with AI:

The viewer reads a holon entirely client-side, so a single holon.jsonld is a complete, portable, self-describing report.

License

This project is licensed under the MIT License - see the LICENSE file for details.

MIT © 2026 RFS LLC

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xbrlkit-0.3.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xbrlkit-0.3.0-py3-none-any.whl (1.9 MB view details)

Uploaded Python 3

File details

Details for the file xbrlkit-0.3.0.tar.gz.

File metadata

  • Download URL: xbrlkit-0.3.0.tar.gz
  • Upload date:
  • Size: 1.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for xbrlkit-0.3.0.tar.gz
Algorithm Hash digest
SHA256 df941f5b454bdbc8d5cfd0a380dac39b7996f543d57c63fa11f09439074d156f
MD5 b9bf7d4f871795b456e06df705bf2927
BLAKE2b-256 2aac6bdef5adb6ab263037cce05c2956e85dabdd3517536152a1e99d94a73142

See more details on using hashes here.

Provenance

The following attestation bundles were made for xbrlkit-0.3.0.tar.gz:

Publisher: publish.yml on RoboFinSystems/xbrlkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file xbrlkit-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: xbrlkit-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 1.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for xbrlkit-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 facd800c4b248ead0da7772aa7a5d711c412908ff2629f5ecb697f599249f77a
MD5 1e0b72306df60d12876686e713eb13c8
BLAKE2b-256 611f64093d3ae019c41e2ab0d622f785152dd79f900d2ab2921b92635bf1d0cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for xbrlkit-0.3.0-py3-none-any.whl:

Publisher: publish.yml on RoboFinSystems/xbrlkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

This release

0.3.0 This release

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page