Offering Activity Feed
Build a capital raise activity feed that surfaces new offerings, warrant exercises, and shelf registrations as they happen.
Most traders find out about offerings after the price has already moved. By the time a headline hits your screener, the damage is done. An offering activity feed pulls structured data from SEC filings as they're processed and gives you a filterable, context-rich stream of capital raise events — before the rest of the market reacts.
This example shows you how to build a feed that monitors offerings across the market or a specific watchlist, enriches each event with historical context, and flags the ones that matter most.
What you're building
A feed that surfaces events like:
- New public offerings and private placements (PIPEs)
- ATM program usage and shelf registration filings
- Warrant exercises and convertible note activity
- SEC filings related to capital raises (S-1, S-3, 8-K, etc.)
Each event includes context: how many times the company has raised capital recently, who the underwriters and investors are, and how the offering size compares to the company's float.
The data sources
| Endpoint | What it gives you |
|---|---|
/v1/offerings | Offering events with type, share price, size, and filing details |
/v1/offerings-advanced | Same as above, plus investor names and underwriter/bank info |
/v1/registrations | Shelf registrations, ATM programs, effective dates, and remaining capacity |
/v1/news | SEC filings and news, filterable by tags and form type |
Step 1: Pull recent offerings
Start with a date-range query to get all offerings filed in the last few days. Omit the ticker parameter to get market-wide results:
import requests
from datetime import date, timedelta
API_KEY = "your_api_key"
BASE_URL = "https://api.askedgar.com"
HEADERS = {"API-KEY": API_KEY}
def get_recent_offerings(days_back=3, ticker=None):
date_from = (date.today() - timedelta(days=days_back)).isoformat()
params = {
"date_from": date_from,
"limit": 50,
}
if ticker:
params["ticker"] = ticker
resp = requests.get(
f"{BASE_URL}/v1/offerings-advanced",
params=params,
headers=HEADERS,
)
resp.raise_for_status()
return resp.json()["results"]The advanced endpoint gives you the full picture — including who underwrote the deal and which funds participated:
{
"ticker": "MULN",
"headline": "Mullen Automotive Announces $25M Registered Direct Offering",
"filed_at": "2025-03-10",
"form_type": "8-K",
"offering_type": "Registered Direct",
"shares_amount": 50000000,
"warrants_amount": 50000000,
"share_price": 0.50,
"offering_amount": 25000000,
"investors": "Hudson Bay Capital Management",
"bank": "H.C. Wainwright & Co.",
"askedgar_url": "https://app.askedgar.io/filing?ticker=MULN&..."
}Step 2: Pull new registrations
Shelf registrations going effective are a leading indicator — they give the company the ability to sell shares, even if they haven't done it yet.
def get_recent_registrations(days_back=7, ticker=None):
date_from = (date.today() - timedelta(days=days_back)).isoformat()
params = {
"effective_date_from": date_from,
"limit": 50,
}
if ticker:
params["ticker"] = ticker
resp = requests.get(
f"{BASE_URL}/v1/registrations",
params=params,
headers=HEADERS,
)
resp.raise_for_status()
return resp.json()["results"]Key fields to surface: offering_amount, is_atm, amount_remaining_atm, expiration_date, and over_baby_shelf.
Step 3: Pull related SEC filings
The news endpoint captures SEC filings that may not show up as structured offerings yet — early S-1 filings, prospectus supplements, and 8-Ks announcing capital raises.
def get_offering_filings(days_back=3, ticker=None):
date_from = (date.today() - timedelta(days=days_back)).isoformat()
params = {
"date_from": date_from,
"hide_news": True, # Only show SEC filings, not news articles
"limit": 50,
}
if ticker:
params["ticker"] = ticker
resp = requests.get(
f"{BASE_URL}/v1/news",
params=params,
headers=HEADERS,
)
resp.raise_for_status()
# Filter to offering-related form types
offering_forms = {"S-1", "S-3", "F-1", "F-3", "424B", "EFFECT", "8-K"}
return [
r for r in resp.json()["results"]
if any(r.get("form_type", "").startswith(f) for f in offering_forms)
]Step 4: Add historical context
A single offering is useful. Knowing it's the company's third offering in six months, all through the same underwriter, is a pattern. Pull the full offering history for each ticker that appears in your feed:
def get_offering_history(ticker, months_back=6):
date_from = (date.today() - timedelta(days=months_back * 30)).isoformat()
resp = requests.get(
f"{BASE_URL}/v1/offerings",
params={"ticker": ticker, "date_from": date_from, "limit": 50},
headers=HEADERS,
)
resp.raise_for_status()
return resp.json()["results"]
def enrich_with_context(offering):
ticker = offering["ticker"]
history = get_offering_history(ticker)
total_raised = sum(
o.get("offering_amount", 0) or 0
for o in history
)
offering_types = [o.get("offering_type", "Unknown") for o in history]
return {
**offering,
"context": {
"offerings_last_6_months": len(history),
"total_raised_last_6_months": total_raised,
"offering_types_used": list(set(offering_types)),
"repeat_bank": (
sum(1 for o in history if o.get("bank") == offering.get("bank"))
if offering.get("bank") else None
),
},
}Putting it together
A complete feed builder that merges all three event types into a single timeline:
from datetime import datetime
def build_offering_feed(days_back=3, watchlist=None):
events = []
# Gather offerings
offerings = get_recent_offerings(days_back, ticker=",".join(watchlist) if watchlist else None)
for o in offerings:
enriched = enrich_with_context(o)
events.append({
"type": "offering",
"date": o.get("filed_at"),
"ticker": o["ticker"],
"headline": o.get("headline"),
"amount": o.get("offering_amount"),
"bank": o.get("bank"),
"investors": o.get("investors"),
"context": enriched["context"],
})
# Gather new registrations
registrations = get_recent_registrations(days_back, ticker=",".join(watchlist) if watchlist else None)
for r in registrations:
events.append({
"type": "registration",
"date": r.get("effective_date") or r.get("filed_at"),
"ticker": r["ticker"],
"headline": r.get("headline"),
"amount": r.get("offering_amount"),
"is_atm": r.get("is_atm"),
"atm_remaining": r.get("amount_remaining_atm"),
"bank": r.get("bank"),
})
# Gather SEC filings
if watchlist:
for ticker in watchlist:
filings = get_offering_filings(days_back, ticker=ticker)
for f in filings:
events.append({
"type": "filing",
"date": f.get("filed_at"),
"ticker": f["ticker"],
"headline": f.get("title") or f.get("summary"),
"form_type": f.get("form_type"),
"url": f.get("url"),
})
# Sort by date, most recent first
events.sort(key=lambda e: e.get("date", ""), reverse=True)
return eventsRunning it
# Market-wide feed
feed = build_offering_feed(days_back=3)
# Or filter to a watchlist
feed = build_offering_feed(days_back=3, watchlist=["MULN", "FFIE", "NVAX", "SNDL"])
for event in feed:
prefix = {"offering": "💰", "registration": "📋", "filing": "📄"}.get(event["type"], "•")
context_str = ""
if event.get("context"):
ctx = event["context"]
context_str = f" (#{ctx['offerings_last_6_months']} in 6mo, ${ctx['total_raised_last_6_months']:,.0f} raised)"
print(f"{prefix} [{event['date']}] {event['ticker']}: {event.get('headline', 'N/A')}{context_str}")Example output:
💰 [2025-03-10] MULN: Mullen Automotive Announces $25M Registered Direct Offering (#3 in 6mo, $75,000,000 raised)
📋 [2025-03-08] FFIE: S-3 Shelf Registration Statement ($50,000,000)
📄 [2025-03-07] NVAX: Prospectus Supplement Filed
💰 [2025-03-06] SNDL: SNDL Announces $100M ATM Program (#2 in 6mo, $200,000,000 raised)
What to watch for
Some patterns in the feed that are worth flagging or alerting on:
- Repeat underwriters — the same bank showing up across multiple offerings for a ticker (e.g., H.C. Wainwright appearing on 3 consecutive deals) often indicates an ongoing relationship with more deals likely
- Rapid-fire offerings — 3+ offerings in 6 months signals a company that's relying on the market for cash
- ATM programs going effective — unlike priced offerings, ATMs let the company sell shares at market price over time with no announcement per sale
- Large offering relative to market cap — a $25M offering on a $30M market cap company is a very different event than the same offering on a $2B company
- Prospectus supplements (424B filings) — these often appear right before shares hit the market from an existing shelf
Cost considerations
The enrichment step (pulling offering history for context) adds API calls per event. For a market-wide feed, consider caching the history per ticker and only refreshing it when a new event appears. For a 20-ticker watchlist polled daily, you're looking at roughly 100-150 calls per run — use the cost estimate endpoint to project costs at your scale.
Next steps
Updated about 3 hours ago