Blog · 2026-07-09

SEC EDGAR API: convert XBRL to JSON in one call (2026)

Raw XBRL-shaped SEC JSON, nested five levels deep, reshaped into flat named statement fields
The SEC hands you JSON. It just isn't the shape you wanted.

You went looking for an XBRL-to-JSON converter. Fair enough. XBRL is XML, JSON is nicer, you want the second one.

Here's the twist most guides skip: the SEC already gives you JSON. Point at data.sec.gov and you get JSON back, no XML in sight. So why does "convert XBRL to JSON" have real search demand and a handful of paid products behind it?

Because the JSON the SEC returns is XBRL-shaped. It's nested five levels deep, indexed by cryptic concept names, and every value the company ever filed is jammed into one array. It's JSON that still thinks like XBRL.

Converting SEC XBRL to JSON is mostly a reshape, not a format change. The SEC's companyfacts endpoint returns JSON already, but in raw XBRL form: facts to us-gaap to a concept like Revenues to units to an array of every fact ever filed. The real work is turning that into flat named fields, one value per period, which the SEC EDGAR API does not do for you.

Key takeaway: The SEC EDGAR API already returns XBRL as JSON, but XBRL-shaped: five levels deep, concept-indexed, one array of every value ever filed. "Converting XBRL to JSON" really means reshaping that into named fields per period. You can do it four ways (DIY, open-source, a converter API, or a normalized statements API). The one-call version is GET /v1/fundamentals/{ticker}.

Does the SEC EDGAR API already give you JSON?

Yes. The SEC's companyfacts endpoint returns pure JSON, free, with no API key, capped at 10 requests per second. So this is not a case where you download XML and run a parser. The bytes are already JSON.

The confusion is understandable. XBRL is filed as XML (and Inline XBRL inside the filing document), so "XBRL" and "XML" feel synonymous.

But the SEC also exposes that same tagged data through its XBRL APIs as JSON. The format was never the hard part. The shape is.

What does the raw companyfacts JSON look like?

It's one big object, nested five levels deep, that holds every XBRL fact a company ever tagged. The path to a single revenue number runs facts to us-gaap to the concept (like Revenues) to units to a USD array, and each entry carries an end date, a value, a fiscal year, and the form it came from. Apple's fiscal 2023 revenue shows up in there as 383285000000, in raw dollars.

The five-level path from the companyfacts root through facts, us-gaap, concept, units, to a fact array holding one revenue value
Five levels down, and you still have to pick the right value out of the array.

Nothing about that is malformed. It's faithful to how XBRL models data, where a "fact" is a value plus its context.

It's just not what you asked for. You wanted revenue: 383285000000. You got a five-level treasure hunt that ends in an unsorted list.

Why is XBRL-shaped JSON hard to use?

Because a single concept's array mixes everything together: annual and quarterly values, restatements, and trailing-twelve-month windows, in no particular order. To get one clean annual revenue figure you filter that array by form and period, then take the latest-filed value. Do that for revenue, net income, assets, and a dozen more fields and you've written a normalization engine.

One concept array mixing annual, quarterly, restated, and trailing-twelve-month values, reshaped into one clean value per period
The conversion isn't XML-to-JSON. It's choosing the right value out of a noisy array.

Then the tag itself moves. The US GAAP taxonomy has more than 15,000 elements, and companies pick different ones for the same line. Apple tags revenue as RevenueFromContractWithCustomerExcludingAssessedTax; plenty of others use plain Revenues. Hard-code one tag and you get blanks for half your tickers.

And the quarterly data hides a trap: a raw 10-Q array carries both true three-month figures and trailing-twelve-month ones, so if you don't filter them your Q3 revenue is quietly wrong. I went deeper on all of this in why parsing SEC EDGAR XBRL is harder than it looks.

How do you convert SEC XBRL to JSON?

There are four routes, and they trade faithfulness for convenience. You can parse companyfacts yourself, run an open-source XBRL processor, call a converter API that returns a filing's every tag, or call a normalized statements API that returns named fields. Here is how they compare.

RouteYou give itYou get backCostBest when
DIY parsecompanyfacts JSONwhatever you codeFree (your hours)Full control, you want every tag, you'll own the tag map
Open-source (Arelle, edgartools)a filing / CIKfaithful XBRL as JSONFree (self-host)Python, standards-grade, happy to maintain it
Converter API (sec-api.io)one filingevery tag in it, as JSONFrom ~$49/moYou need all tags from specific filings, hosted
Statements API (Edgrapi)a tickernamed fields per periodFree tier, from $10/moYou want clean statements without mapping tags
Four conversion routes from DIY parsing through open-source and converter APIs to a normalized statements API
Left is most faithful to the raw filing; right is most convenient to use.

The DIY route in Python looks small until step three:

import requests
UA = {"User-Agent": "you@example.com"}

cik = "0000320193"  # Apple, zero-padded to 10 digits
facts = requests.get(
    f"https://data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json", headers=UA).json()

gaap = facts["facts"]["us-gaap"]
tag = "RevenueFromContractWithCustomerExcludingAssessedTax"  # Apple's tag; others use "Revenues"
rows = gaap[tag]["units"]["USD"]

# filter the array to one annual value, newest 10-K wins, drop the TTM rows:
fy = [r for r in rows if r.get("fp") == "FY" and r.get("form") == "10-K"]
print(fy[-1]["end"], fy[-1]["val"])   # 2023-09-30 383285000000

That works for Apple. For the next company you handle a different tag, and the quarterly path needs its own period logic. Multiply that across every field and it stops being a snippet.

Converter API or statements API: which do you actually need?

They solve different problems, and "XBRL to JSON" gets used for both. A faithful converter takes one filing and returns every tag in it, which you want when completeness matters: a specific 10-K, custom tags, an audit trail. A statements API takes a ticker and returns clean named fields across periods, which you want when usability matters: revenue and net income across years, in any language, without tag archaeology.

Faithful converter (one filing, every tag) versus statements API (a ticker, named fields per period)
Two different jobs. Pick by whether you need completeness or usability.

Most people searching for an XBRL-to-JSON converter actually want the second one. They don't need every custom tag from one filing; they need revenue as a number they can chart. If that's you, the converter is more machinery than the job requires.

How do you get clean statement JSON in one call?

You call a normalized endpoint with a ticker and read named fields off the response. With Edgrapi the reshaping is already done: GET /v1/fundamentals/{ticker} returns the income statement, balance sheet, and cash flow as flat fields, one row per period, with the tag mapping and the TTM filtering handled.

import requests

r = requests.get(
    "https://api.edgrapi.com/v1/fundamentals/AAPL",
    headers={"X-API-Key": "edgr_your_key"},
    params={"period": "annual"},
)
for row in r.json()["income_statement"]:
    print(row["period"], row["revenue"], row["net_income"])
# 2023 383285000000 96995000000
# 2022 394328000000 99803000000

Same SEC data as the raw companyfacts call. The difference is that the five-level treasure hunt and the tag guessing already happened, so you read revenue instead of reconstructing it. The financial statements post covers the full response shape, and the alternatives roundup lines up every option side by side.

Can an AI agent get XBRL financials as JSON?

Yes, through MCP, the Model Context Protocol. Edgrapi runs a hosted MCP server at https://api.edgrapi.com/mcp with a get_fundamentals tool, so an AI client pulls the already-reshaped statements in a conversation instead of parsing companyfacts itself.

That matters more than it sounds. An agent handed raw companyfacts has to guess tags and dedupe arrays, which is exactly where it hallucinates a number. Handed clean fields, it reads the value.

Stop converting, start reading

If you only need clean statements, you don't need an XBRL-to-JSON converter at all. You need the numbers already reshaped.

Grab a free key, call /v1/fundamentals on a ticker you know, and read revenue straight off the JSON. The free tier is 100 credits a month, no card, enough to convert your whole watchlist without writing a single line of tag-mapping code. Point it at https://api.edgrapi.com and skip the treasure hunt.

Frequently asked questions

Does the SEC EDGAR API return XBRL as JSON?

Yes, but XBRL-shaped JSON, not usable statement JSON. The companyfacts endpoint at data.sec.gov returns a large JSON object nested five levels deep: facts, then taxonomy (us-gaap), then concept (Revenues), then units (USD), then an array of every value ever filed. It is JSON, but you still have to reshape it into named fields per period.

How do I convert SEC XBRL to JSON?

Four ways. Parse the companyfacts JSON yourself; run an open-source processor like Arelle or the edgartools library; call a converter API that returns a filing's full XBRL as JSON; or call a normalized statements API that returns named fields per period. The first two are free but you own the work; the last is one call, like GET /v1/fundamentals/AAPL on Edgrapi.

Is there a free XBRL to JSON converter?

Yes. The SEC's own companyfacts API is free and already returns XBRL as JSON, just in its raw concept-indexed shape. Open-source options like Arelle and the edgartools Python library convert it faithfully for free if you self-host. Hosted wrappers add free tiers on top: Edgrapi returns clean statement JSON with 100 free credits a month, no card.

What is the difference between an XBRL-to-JSON converter and a financial statements API?

A converter takes one filing and returns every XBRL tag in it as JSON, faithful but unmapped. A statements API takes a ticker and returns clean named fields (revenue, net_income) across periods. Use a converter when you need completeness from a specific filing; use a statements API when you want usable numbers without mapping 15,000+ tags yourself.

How do I get a company's revenue from SEC XBRL as JSON?

In the raw companyfacts JSON, revenue sits at facts.us-gaap.[tag].units.USD as an array you filter down to one annual value. The catch: the tag varies by company (Apple uses RevenueFromContractWithCustomerExcludingAssessedTax, others use Revenues). A normalized API hides that: GET /v1/fundamentals/AAPL returns revenue as a named field directly.

Can I convert a specific 10-K filing's XBRL to JSON?

Yes, but that is the faithful-converter job, not the statements-API job. Tools like sec-api.io's XBRL-to-JSON converter or the open-source Arelle processor take one filing and return all its tags as JSON. Edgrapi instead returns normalized statements across a company's filings by ticker, which is the right tool when you want clean numbers rather than one exact filing's every tag.

Get a free API key