How to read a 10-K with an AI agent, using the SEC EDGAR API

Updated on

Educational only, not investment advice.

How to read a 10-K with AI, using the SEC EDGAR API: what a 10-K is, which sections to read first, and how to find the latest 10-K with the SEC EDGAR API and pull Item 1A and Item 7 so an AI agent can cite the section and accession number for every claim.

What a 10-K is

Most U.S. public companies must file a 10-K each year with the SEC. SEC rules require a set order of topics. The company writes the 10-K, and the SEC neither writes it nor vouches for its accuracy. The CEO and CFO must certify its accuracy (Sarbanes-Oxley). Non-U.S. public companies usually file annual reports on different forms. All 10-Ks are public on EDGAR (Investor Bulletin: How to Read a 10-K, SEC Office of Investor Education and Advocacy).

The sections that matter most

ItemNameWhat it contains (from SEC)
Item 1 Business The company's business, main products and services, subsidiaries and markets. It may cover recent events, competition, regulation, labor issues and seasonal factors. SEC calls it a good place to start.
Item 1A Risk Factors The most significant risks to the company or its securities, generally listed in order of importance. It focuses on the risks, not how the company addresses them. Smaller reporting companies are not required to provide this item (Form 10-K and General Instructions).
Item 1C Cybersecurity Information required by Item 106 of Regulation S-K (Form 10-K and General Instructions). Not in the older bulletin.
Item 7 Management's Discussion and Analysis (MD&A) Management's perspective on the past financial year: operations and results, liquidity and capital resources, known trends or uncertainties, material changes from the prior period, and critical accounting judgments.
Item 7A Quantitative and Qualitative Disclosures About Market Risk Exposure to market risks such as interest rate, foreign currency, commodity or equity price risk.
Item 8 Financial Statements and Supplementary Data The audited financial statements (income statement, balance sheets, cash flows, stockholders' equity) with notes. The auditor's report is a key part.
Item 9A Controls and Procedures Disclosure controls and internal control over financial reporting.

Item names follow the current Form 10-K (Form 10-K and General Instructions). SEC's investor bulletin (Investor Bulletin: How to Read a 10-K) is older: for example it still describes Item 6 as Selected Financial Data, which the current form lists as [Reserved].

Part III items (10–14, directors, executive pay, ownership, related transactions, accountant fees) are often incorporated by reference from the proxy statement, which is typically filed a month or two after the 10-K (Investor Bulletin: How to Read a 10-K).

Non-GAAP measures must be shown with how they differ from the most comparable GAAP measure (Investor Bulletin: How to Read a 10-K).

Step by step: pull Risk Factors and MD&A with Python

This builds on the SEC EDGAR API guide. Use the same User-Agent rule and the same limit of no more than 10 requests per second (Developer Resources, Accessing EDGAR Data). You need Python 3 and the requests library. Each numbered step below is one part of the script. The full script and a run from 2026-10-20 follow.

1. Find the latest 10-K and its accession number (submissions API)

The script starts with a User-Agent that states your name and email, and a helper that pauses 0.2 seconds after each request so you stay under the limit of 10 requests per second. It then reads the submissions JSON for a 10-digit CIK. The first "10-K" in filings.recent.form is the latest annual report, in parallel with accessionNumber, filingDate and primaryDocument. The document URL uses the CIK without leading zeros, the accession number without dashes, and the primary document file name.

The block below is the start of the script: imports and the request helper, then the submissions call marked "# 1.".

import html
import re
import time
import requests

# SEC asks every automated request to declare who you are.
# Replace with your own name and email before running.
HEADERS = {"User-Agent": "Your Name your-email@example.com"}
CIK = "0000320193"  # example filer; any 10-digit, zero-padded CIK works


def get(url):
    r = requests.get(url, headers=HEADERS, timeout=60)
    r.raise_for_status()
    time.sleep(0.2)  # stays well under SEC's 10 requests/second limit
    return r

# 1. Find the latest 10-K in the filing history
recent = get(f"https://data.sec.gov/submissions/CIK{CIK}.json").json()["filings"]["recent"]
i = recent["form"].index("10-K")
accession = recent["accessionNumber"][i]
doc_url = (f"https://www.sec.gov/Archives/edgar/data/{int(CIK)}/"
           f"{accession.replace('-', '')}/{recent['primaryDocument'][i]}")
print("10-K", recent["filingDate"][i], accession)
print(doc_url)

2. Download the filing document and convert it to plain text

Download that document with the same helper. Remove script and style blocks, strip the remaining tags, turn HTML entities back into characters, and collapse runs of spaces. The section() function in this part of the script finds the text from one Item heading to the next. The next step calls it.

# 2. Download the filing and turn the HTML into plain text
raw = get(doc_url).text
text = re.sub(r"(?is)<(script|style).*?</\1>", " ", raw)
text = re.sub(r"(?s)<[^>]+>", " ", text)
text = html.unescape(text).replace("\xa0", " ")
text = re.sub(r"[ \t\r\f\v]+", " ", text)


def section(text, start, end):
    """Text from one Item heading to the next. Match the heading title too,
    so cross-references ("see Item 1A of this Form 10-K") are skipped. The
    table of contents repeats the headings, so keep the longest match."""
    pattern = rf"(?is)\bitem\s+{start}.*?(?=\bitem\s+{end})"
    matches = re.findall(pattern, text)
    return max(matches, key=len).strip() if matches else ""

3. Cut out Item 1A and Item 7 by their headings

Item 1A is the Risk Factors heading through the Item 1B heading. Item 7 is the Management heading through the Item 7A heading. The pattern requires the heading title, so a cross-reference such as "see Item 1A of this Form 10-K" does not match. If the heading appears more than once, the function keeps the longest match.

# 3. Pull Risk Factors (Item 1A) and MD&A (Item 7)
parts = {
    "Item 1A": section(text, r"1A\.?\s*Risk Factors", r"1B\.?\s*Unresolved"),
    "Item 7": section(text, r"7\.?\s*Management", r"7A\.?\s*Quantitative"),
}
for name, body in parts.items():
    print(f"\n{name}: {len(body.split())} words")
    print(" ".join(body.split()[:15]), "...")

4. Hand the sections to your agent with the source attached

Give the agent the extracted text plus the company name, the form, the filing date, the accession number and the document URL. The next section is a prompt that requires the section and the accession number after every claim.

Full script

The comments number the three data steps. The imports and request helper come first and are not numbered. Replace the placeholder User-Agent before you run it.

import html
import re
import time
import requests

# SEC asks every automated request to declare who you are.
# Replace with your own name and email before running.
HEADERS = {"User-Agent": "Your Name your-email@example.com"}
CIK = "0000320193"  # example filer; any 10-digit, zero-padded CIK works


def get(url):
    r = requests.get(url, headers=HEADERS, timeout=60)
    r.raise_for_status()
    time.sleep(0.2)  # stays well under SEC's 10 requests/second limit
    return r


# 1. Find the latest 10-K in the filing history
recent = get(f"https://data.sec.gov/submissions/CIK{CIK}.json").json()["filings"]["recent"]
i = recent["form"].index("10-K")
accession = recent["accessionNumber"][i]
doc_url = (f"https://www.sec.gov/Archives/edgar/data/{int(CIK)}/"
           f"{accession.replace('-', '')}/{recent['primaryDocument'][i]}")
print("10-K", recent["filingDate"][i], accession)
print(doc_url)

# 2. Download the filing and turn the HTML into plain text
raw = get(doc_url).text
text = re.sub(r"(?is)<(script|style).*?</\1>", " ", raw)
text = re.sub(r"(?s)<[^>]+>", " ", text)
text = html.unescape(text).replace("\xa0", " ")
text = re.sub(r"[ \t\r\f\v]+", " ", text)


def section(text, start, end):
    """Text from one Item heading to the next. Match the heading title too,
    so cross-references ("see Item 1A of this Form 10-K") are skipped. The
    table of contents repeats the headings, so keep the longest match."""
    pattern = rf"(?is)\bitem\s+{start}.*?(?=\bitem\s+{end})"
    matches = re.findall(pattern, text)
    return max(matches, key=len).strip() if matches else ""


# 3. Pull Risk Factors (Item 1A) and MD&A (Item 7)
parts = {
    "Item 1A": section(text, r"1A\.?\s*Risk Factors", r"1B\.?\s*Unresolved"),
    "Item 7": section(text, r"7\.?\s*Management", r"7A\.?\s*Quantitative"),
}
for name, body in parts.items():
    print(f"\n{name}: {len(body.split())} words")
    print(" ".join(body.split()[:15]), "...")

Output from a run on 2026-10-20

10-K 2025-10-31 0000320193-25-000079
https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/aapl-20250927.htm

Item 1A: 9784 words
Item 1A. Risk Factors The following summarizes factors that could have a material adverse effect ...

Item 7: 2805 words
Item 7. Management’s Discussion and Analysis of Financial Condition and Results of Operations The following ...

We ran this script against the live EDGAR system on 2026-10-20 with our own User-Agent. Apple Inc. (CIK 0000320193) is only a well-known example filer; nothing here is a view on the company. Word counts depend on the filing and on how the HTML is converted.

In this filing the table of contents repeats each heading, which is why the script keeps the longest match. There were two matches for each heading.

Headings vary between filers: punctuation, capitalization, and "ITEM 1A." versus "Item 1A:". Some filings put a heading inside a table or split it across tags, and some text is incorporated by reference from exhibits or the proxy. Always check that the extracted text starts and ends where you expect.

A prompt that keeps every claim sourced

Paste the sections into a prompt like this. The angle brackets are placeholders.

You are reading one SEC filing. Use only the text I give you.

Filing: <company name>, Form 10-K, filed <filing date>, accession <accession number>
Source URL: <document URL>

Sections:
[Item 1A - Risk Factors]
<pasted text>

[Item 7 - MD&A]
<pasted text>

Task: <your question, e.g. list the five risks the company describes first and summarize what MD&A says about liquidity>.

Rules:
- After every claim, cite the section and accession number, e.g. (Item 1A, <accession number>).
- Quote the exact sentence for any number you mention.
- If the text does not answer something, write "not stated in these sections". Do not use outside knowledge or estimate.
- Do not give investment advice or a view on the stock.

Check a sample of the quotes against the filing. A long section may exceed an agent's context window, so send one section at a time, and keep the accession number in your notes.

If you later grade an agent's research, see how we plan to grade an AI analyst's calls and how we plan to compare two agents on the same research job. The rules for those jobs are on how we test.

Troubleshooting

403 from www.sec.gov/Archives without a User-Agent

In our run on 2026-10-20, the archive document returned 403 when the request had no User-Agent. Set a User-Agent with your name and email (Accessing EDGAR Data).

No Item 1A found

Smaller reporting companies are not required to provide Item 1A (Form 10-K and General Instructions). The heading may also be formatted differently. Print the text around "Risk Factors" and adjust the pattern.

Section is far too long or too short

Usually the pattern matched a cross-reference or the table of contents instead of the heading. Check the first and last lines of the extract.

The section says see the proxy statement

Part III items are often incorporated by reference from the proxy statement (Investor Bulletin: How to Read a 10-K).

FAQ

Which parts of a 10-K should I read first?

SEC's investor bulletin calls Item 1, Business, a good place to start. Item 1A covers risk factors, Item 7 is management's discussion and analysis, and Item 8 holds the audited financial statements.

Can an AI agent read a 10-K for me?

It can extract and summarize sections you give it, but it can misread or invent details. Give it the filing text, require a section and accession number for every claim, and check quotes against the filing.

Is this investment advice?

No. This page is educational only.

Related on bests.one

Sources: Investor Bulletin: How to Read a 10-K, Form 10-K and General Instructions, Accessing EDGAR Data, Developer Resources. Checked on 2026-10-20.

Follow the tests