▶ Full Post Text
I lost my mind calling useless APIs and buying NLP pipelines that dump raw filing text and let the user figure it out, so I built one. This is what I have so far and what I have learned:
**The data source**
EDGAR's full-text search API (`efts.sec.gov`) is completely free and has zero anti-bot friction. You can pull every 8-K filed by S&P 500 companies within seconds. The actual guidance language almost always lives in Exhibit 99.1 — the earnings press release attached to the 8-K — not the 8-K body itself.
**Sentence splitting**
Nothing fancy:
python
sentences = re.split(r'(?<=[.!?])\s+', exhibit_text)
sentences = [s.strip() for s in sentences if len(s.strip()) > 30]
The 30-character floor filters out headers, table fragments, and other noise that technically ends with a period.
**Guidance sentence identification**
Pure keyword matching against each sentence:
python
GUIDANCE_KEYWORDS = [
"guidance", "outlook", "forecast", "expects", "anticipates",
"revenue of", "earnings per share", "record revenue",
"raised guidance", "lowered guidance", "dividend", "repurchase"
]
def extract_guidance_sentences(sentences, max_results=20):
results = []
for sentence in sentences:
matched = [kw for kw in GUIDANCE_KEYWORDS if kw in sentence.lower()]
if matched:
results.append({"text": sentence, "keywords": matched})
if len(results) >= max_results:
break
return results
No NLP, no transformer model, no dependency beyond the standard library. This runs fast enough that you can process hundreds of filings in a few minutes on a cheap VPS.
**Event type classification**
Same approach — keyword scan across the full filing text to tag what kind of event the 8-K is reporting:
python
EVENT_PATTERNS = {
"earnings_release": ["earnings", "quarterly results", "financial results"],
"guidance_update": ["guidance", "outlook", "forecast"],
"acquisition": ["acqui", "merger", "transaction", "definitive agreement"],
"executive_change": ["appointed", "resigned", "chief executive", "ceo", "cfo"],
"dividend": ["dividend", "repurchase", "buyback"],
"restructuring": ["restructuring", "workforce reduction", "layoff"],
}
def classify_events(text):
text_lower = text.lower()
return [
event for event, patterns in EVENT_PATTERNS.items()
if any(p in text_lower for p in patterns)
]
Multiple types can fire on the same filing — an earnings release that also announces a dividend increase gets both `earnings_release` and `dividend` tags, which is accurate.
**What the output looks like**
Real example from MetLife's 8-K filed June 29, 2026:
json
{
"ticker": "MET",
"filed_date": "2026-06-29",
"event_types": ["earnings_release", "guidance_update"],
"guidance_sentences": [
{
"text": "For the quarter ended June 30, 2026, the Company estimates that its variable investment income will be approximately $220 million to $270 million (pre-tax), which compares to full-year 2026 guidance of approximately $1.6 billion (pre-tax).",
"keywords": ["guidance", "estimates"]
}
]
}
**Known limitations worth being upfront about**
The keyword approach has obvious false positives — "earnings per share" fires on historical reported EPS just as readily as on forward estimates, and "dividend" fires on both declarations and boilerplate forward-looking disclaimers. There's no sentence-position awareness, no section detection (so you can't distinguish the MD&A from the safe-harbor boilerplate), and no semantic understanding of whether a matched sentence is actually forward-looking or backward-looking.
For algo trading use cases where you need high-precision guidance extraction, you'd want to layer in at least: section detection (identify the "Outlook" or "Guidance" section header and prioritize those sentences), tense detection (filter for future-tense verbs), or a small fine-tuned classifier. Happy to discuss any of those approaches in the comments.
I've been running this against S&P 500 8-Ks on a daily cron — it's cheap, fast, and surprisingly useful even with the limitations above. The structured JSON output is what makes it worth building: you can feed it directly into a model, a screener, or a signal pipeline without further parsing.