DEV Community

Cristian Lungu
Cristian Lungu

Posted on

Give your AI agent real SEC data (financials + insider signals) in 5 minutes

Ask an LLM "What was Apple's revenue last quarter?" or "Are insiders buying NVIDIA right now?" and you'll often get a confident, wrong answer. Financial numbers are exactly the kind of thing models hallucinate - and the ground truth (SEC EDGAR) is not something you can just paste into a prompt.

If you've tried, you know the pain:

  • A single 10-K is megabytes of XBRL and HTML. It blows your context window instantly.
  • Parsing XBRL by hand is a second job.
  • Insider trades live in Form 4 XML that nobody enjoys scraping.

So your agent needs structured SEC data, not raw filings. Here's how to wire that up in about five minutes.

What we'll build

An agent tool that, given a ticker, returns clean JSON: company profile, XBRL financials (revenue, net income, EPS), recent filings, and parsed insider trades with a 0-100 conviction score. Then we'll hand that JSON to an LLM and ask it a real question.

We'll use the SEC Intel API - it wraps official SEC EDGAR data (US public domain) and returns exactly the shape an LLM can reason over. There's a free tier, no credit card.

Disclosure: I built this API. But the technique below works with any structured-data source - the point is don't feed raw filings to your model.

Step 1 - one call, everything

import requests

HOST = "sec-intel-filings-financials-insider-trades.p.rapidapi.com"
HEADERS = {"X-RapidAPI-Key": "YOUR_KEY", "X-RapidAPI-Host": HOST}

def sec_company(query: str) -> dict:
    r = requests.get(
        f"https://{HOST}/company",
        params={"query": query},   # ticker, CIK, or company name
        headers=HEADERS,
        timeout=30,
    )
    r.raise_for_status()
    return r.json()

data = sec_company("AAPL")
print(data["profile"]["name"])          # Apple Inc.
print(data["financials"]["revenue"])    # latest revenue from XBRL
Enter fullscreen mode Exit fullscreen mode

/company bundles profile + financials + insider summary + recent filings in one response, so your agent doesn't have to orchestrate four calls. Want just one slice? There are focused endpoints too: /financials, /filings, /insider, /search, /latest.

(No key yet? You can hit the live interactive docs and try every endpoint in the browser first.)

Step 2 - the part most APIs don't give you: insider conviction

Ten insiders each filed a Form 4 last week. Which company are they actually betting on? Raw filings won't tell you - you'd have to weigh how many insiders bought, whether the CFO/CEO were among them, and how buys compare to sells.

/insider does that math for you and returns a single conviction_score (0-100):

def insider_conviction(query: str) -> dict:
    r = requests.get(
        f"https://{HOST}/insider",
        params={"query": query},
        headers=HEADERS, timeout=30,
    )
    r.raise_for_status()
    return r.json()

nvda = insider_conviction("NVDA")
print(nvda["conviction_score"], nvda.get("signal"))
# e.g. 82 strong_accumulation
Enter fullscreen mode Exit fullscreen mode

Now your agent reads intent, not XML.

Step 3 - hand it to the model

The whole point: put clean JSON in the context, then let the LLM reason. Here's the pattern with Claude (works the same with the OpenAI SDK):

import anthropic, json

client = anthropic.Anthropic()  # ANTHROPIC_API_KEY in env

facts = sec_company("NVDA") | {"insider": insider_conviction("NVDA")}

msg = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=400,
    messages=[{
        "role": "user",
        "content": (
            "Using ONLY this SEC data, summarize NVIDIA's latest "
            "fundamentals and whether insiders are accumulating.\n\n"
            + json.dumps(facts, indent=2)
        ),
    }],
)
print(msg.content[0].text)
Enter fullscreen mode Exit fullscreen mode

Because the model is grounded in real, structured numbers, it stops guessing. This is the same idea as RAG - just with a financial data source instead of a vector DB.

Turn it into a tool the agent can call itself

If you're using function/tool calling, expose it as a tool and let the agent decide when to pull data:

tools = [{
    "name": "get_sec_company",
    "description": "Get SEC financials + insider conviction for a ticker.",
    "input_schema": {
        "type": "object",
        "properties": {"ticker": {"type": "string"}},
        "required": ["ticker"],
    },
}]
# when the model calls it, run sec_company(ticker) and return the JSON
Enter fullscreen mode Exit fullscreen mode

Why not just parse EDGAR yourself?

You can - EDGAR is free and public domain. But you'll spend your week on XBRL taxonomies, Form 4 XML quirks, CIK/ticker resolution, and rate-limit etiquette before you write a line of agent logic. A structured wrapper turns all of that into one HTTP call, and the data stays resale-safe because it's public domain.

Try it

If you build something with it - an earnings summarizer, an insider-buying screener, a due-diligence agent - I'd love to hear about it in the comments. What SEC data would make your agent smarter?

Top comments (0)