Build an M&A research agent
M&A intelligence — comparable transactions, deal multiples, the buyer universe, regulatory exposure — is some of the most expensive data in finance. PitchBook, CapIQ, and Refinitiv charge five and six figures a year for it. But much of the underlying signal is public: SEC 8-K deal announcements, press releases, league tables, investment-bank research, and news coverage. This guide builds a Python agent that mines those public sources and assembles a structured M&A brief. It chains all four Superhighway endpoints — /research for the deal landscape and buyer universe, /search for comparable transactions and SEC filings, /scrape for specific deal terms and rationale, and /news for recent announced deals and rumored targets — then uses an LLM to emit a structured M&A intelligence brief as JSON.
1. What you'll build
A Python agent that takes a company, sector, or deal type and produces a structured M&A intelligence brief:
- Synthesizes the M&A landscape: deal history, typical deal structures, buyer universe, regulatory environment
- Finds comparable transactions, SEC 8-K deal announcements, and investment-bank research
- Scrapes press releases and filings for specific deal terms and strategic rationale
- Pulls recent M&A news: new deals, rumored targets, strategic buyer moves, PE portfolio activity
- Uses an LLM to generate a structured M&A brief — comparable transactions, typical multiples, potential acquirers, regulatory considerations, and due-diligence flags as JSON
2. Setup
pip install openai requests python-dotenv
Create a .env file with your two keys:
SUPERHIGHWAY_API_KEY=your_key_here
OPENAI_API_KEY=your_key_here
3. Research the M&A landscape
Start with /research, which pulls multi-source background into one synthesis — the deal history for this company or sector, how transactions are typically structured, who the active buyers are, and what valuation multiples apply. This is the grounding context for every later step.
import requests, os, json
SUPERHIGHWAY_KEY = os.getenv("SUPERHIGHWAY_API_KEY")
BASE = "https://superhighway.walls.sh"
def research_ma_landscape(company_or_sector: str) -> str:
"""Deep synthesis: deal history, structures, buyer universe, multiples."""
r = requests.get(
f"{BASE}/research",
params={
"q": f"{company_or_sector} M&A acquisition merger deal history valuation multiples",
"pages": 6
},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
data = r.json()
return data.get("synthesis", data.get("markdown", ""))[:3000]
4. Find comparable transactions and filings
Two /search calls do the legwork. The first hunts for recent deal announcements and SEC 8-K filings; the second goes after valuation comps — EV/EBITDA, EV/Revenue, and the strategic buyers behind comparable deals. Together they surface the candidate pages the agent will scrape for hard terms.
def find_comparable_deals(company_or_sector: str) -> list[dict]:
"""Find recent deal announcements and SEC 8-K filings."""
r = requests.get(
f"{BASE}/search",
params={
"q": f"{company_or_sector} acquisition deal announcement SEC 8-K merger agreement 2024 2025",
"limit": 6
},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
return r.json().get("results", [])
def find_valuation_comps(company_or_sector: str) -> list[dict]:
"""Find comp analysis: multiples, strategic acquirers, buyer universe."""
r = requests.get(
f"{BASE}/search",
params={
"q": f"{company_or_sector} strategic acquirer buyer M&A valuation EV/EBITDA revenue multiple comparable",
"limit": 6
},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
return r.json().get("results", [])
5. Scrape deal pages and filings
/scrape turns each candidate URL into clean, LLM-ready markdown. This is where the agent pulls actual deal terms — purchase price, consideration mix, stated rationale — out of press releases, 8-K filings, and company IR pages.
def scrape_deal_page(url: str) -> dict:
"""Scrape a press release, filing, or IR page for deal terms and rationale."""
r = requests.get(
f"{BASE}/scrape",
params={"url": url},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
data = r.json()
return {
"url": url,
"title": data.get("title", ""),
"content": data.get("markdown", "")[:2500],
}
6. Get recent M&A news
/news surfaces what just happened — newly announced deals, rumored takeover targets, strategic buyer activity, PE portfolio moves, and antitrust rulings. This is the time-sensitive layer that a static comp set misses.
def get_ma_news(company_or_sector: str) -> list[dict]:
"""Get recent announced deals, rumors, and strategic buyer activity."""
r = requests.get(
f"{BASE}/news",
params={
"q": f"{company_or_sector} merger acquisition deal rumor takeover strategic buyer",
"count": 6
},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
return r.json().get("articles", [])
7. Generate the M&A brief with an LLM
Now hand everything to the LLM. The system prompt forbids inventing deal terms or multiples and bars any buy/sell or invest/divest recommendation — the agent summarizes what's in the sources, it does not advise. The output is structured JSON so it slots straight into a deal memo, a screening tracker, or a dashboard.
from openai import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
DISCLAIMER = (
"This summary is for informational purposes only and does not constitute "
"financial or investment advice. M&A transactions involve significant "
"complexity and risk. Always conduct proper due diligence and consult "
"qualified financial advisors and legal counsel before making investment "
"or acquisition decisions."
)
def generate_ma_brief(
subject: str,
landscape: str,
deal_pages: list[dict],
news: list[dict],
context: dict
) -> dict | None:
"""Generate a structured M&A intelligence brief."""
deal_text = "\n".join(
f"- {p['title']}: {p['content'][:400]}"
for p in deal_pages[:5]
if p.get("content")
)
news_text = "\n".join(
f"- {n.get('title', '')} ({n.get('source', '')})"
for n in news[:6]
)
subject_type = context.get("subject_type", "company")
deal_size = context.get("deal_size", "mid-market")
geography = context.get("geography", "global")
response = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": f"""You are an M&A analyst preparing an intelligence brief.
The subject is a {subject_type}; focus on {deal_size} deals in the {geography} market.
Be specific and factual. Only use information from the provided sources.
Do not invent deal terms or valuation multiples — if a figure isn't in the sources,
say 'not found in sources.' Do not make invest, divest, buy, or sell recommendations."""
},
{
"role": "user",
"content": f"""Write an M&A intelligence brief for: {subject}
M&A Landscape:
{landscape[:2000]}
Comparable Deals & Filings:
{deal_text}
Recent M&A News:
{news_text}
Return JSON with:
- subject: string (the company, sector, or deal type researched)
- deal_type: "strategic-acquisition" | "private-equity-buyout" | "merger-of-equals" | "carve-out" | "growth-investment" | "mixed"
- strategic_rationale: string (why buyers typically pursue this target/sector — scale, capabilities, market access, synergies)
- comparable_transactions: list of 3-5 strings (relevant precedent deals found, with rough terms if public)
- typical_multiples: string (what valuation metrics apply: EV/EBITDA, EV/Revenue, P/E — and typical ranges for this sector from the sources)
- potential_acquirers: list of 3-5 strings (who would logically buy — strategic buyers and PE firms)
- regulatory_considerations: string (antitrust risk, jurisdiction issues, approvals typically required)
- recent_deal_activity: list of 3 strings (from the news — recently announced deals, rumors, PE portfolio moves)
- due_diligence_flags: list of 3-4 strings (key things a buyer would examine: IP, customer concentration, tech debt, litigation, retention)
- data_quality: "high" | "medium" | "low" (how much source material was found)"""
}
],
response_format={"type": "json_object"}
)
try:
brief = json.loads(response.choices[0].message.content)
brief["disclaimer"] = DISCLAIMER
return brief
except (json.JSONDecodeError, KeyError):
return None
8. The full research pipeline
Wire the steps together: research the landscape, find comparable deals and valuation comps, scrape the top pages, pull news, then generate the brief. The context dict tells the agent whether you're researching a company, a sector, or a deal type — and what deal size and geography to slant toward.
def research_ma(
subject: str,
context: dict | None = None,
max_pages: int = 5
) -> dict | None:
"""
Run the full M&A research pipeline.
context: {
"subject_type": "company" | "sector" | "deal-type",
"deal_size": "small-cap" | "mid-market" | "large-cap",
"geography": "US" | "EU" | "global"
}
"""
if context is None:
context = {"subject_type": "company", "deal_size": "mid-market", "geography": "global"}
print(f"Researching M&A: {subject}")
# Step 1: Landscape synthesis
print("Synthesizing M&A landscape...")
landscape = research_ma_landscape(subject)
# Step 2: Find comparable deals and valuation comps
print("Finding comparable transactions and filings...")
results = find_comparable_deals(subject) + find_valuation_comps(subject)
# Step 3: Scrape the top deal pages and filings
print(f"Scraping {min(len(results), max_pages)} deal pages...")
deal_pages = []
seen = set()
for result in results:
url = result.get("url")
if not url or url in seen:
continue
seen.add(url)
page = scrape_deal_page(url)
if page["content"]:
deal_pages.append(page)
if len(deal_pages) >= max_pages:
break
# Step 4: Recent M&A news
print("Pulling recent M&A news...")
news = get_ma_news(subject)
# Step 5: Generate the brief
print("Generating M&A brief...")
return generate_ma_brief(subject, landscape, deal_pages, news, context)
def print_brief(brief: dict):
if not brief:
print("Could not generate brief.")
return
print(f"\n{'='*60}")
print(f"M&A Brief: {brief.get('subject', 'Subject')}")
print(f"Deal type: {brief.get('deal_type', '?')}")
print(f"Data quality: {brief.get('data_quality', '?').upper()}")
print(f"{'='*60}")
print(f"\nStrategic Rationale:\n{brief.get('strategic_rationale', '')}")
comps = brief.get("comparable_transactions", [])
if comps:
print("\nComparable Transactions:")
for c in comps:
print(f" * {c}")
print(f"\nTypical Multiples:\n{brief.get('typical_multiples', '')}")
acquirers = brief.get("potential_acquirers", [])
if acquirers:
print("\nPotential Acquirers:")
for a in acquirers:
print(f" > {a}")
print(f"\nRegulatory Considerations:\n{brief.get('regulatory_considerations', '')}")
activity = brief.get("recent_deal_activity", [])
if activity:
print("\nRecent Deal Activity:")
for d in activity:
print(f" ! {d}")
flags = brief.get("due_diligence_flags", [])
if flags:
print("\nDue Diligence Flags:")
for f in flags:
print(f" ? {f}")
print(f"\n{brief.get('disclaimer', '')}")
if __name__ == "__main__":
import sys
# Usage: python agent.py "cybersecurity" sector
subject = sys.argv[1] if len(sys.argv) > 1 else "cybersecurity M&A multiples"
subject_type = sys.argv[2] if len(sys.argv) > 2 else "sector"
CONTEXT = {
"subject_type": subject_type,
"deal_size": "mid-market",
"geography": "US",
}
brief = research_ma(subject, CONTEXT, max_pages=5)
if brief:
print_brief(brief)
9. Common research topics
- Target screening — "SaaS companies under $50M ARR", "healthcare IT acquisition targets", "fintech payment companies".
- Sector comps — "cybersecurity M&A multiples", "software PE buyout comps", "biotech acquisition premiums".
- Specific companies — "Notion acquisition potential", "Figma acquisition history" (after the blocked Adobe deal).
- Deal types — "carve-out transactions technology", "add-on acquisitions private equity software".
- Strategic dynamics — "Microsoft acquisition strategy AI", "PE roll-up strategy dental practices".
10. Use cases
- Corporate development — screen a sector for acquisition targets before engaging bankers.
- PE analyst — assemble a quick comp set for a new portfolio company or deal evaluation.
- Investment banker — prep for a first meeting with a strategic client exploring M&A.
- Founder — understand who the natural acquirers of your company are, and at what multiples.
- VC — evaluate exit paths and comp multiples before making an investment.
11. Extending the agent
- Deal monitoring — schedule a weekly
/news?q={sector}+acquisition+merger+announcedcall to track new deals as they break. - Target screening — batch-run for a list of companies in a sector and compare
potential_acquirersandtypical_multiplesside by side. - Competitive intelligence — watch
recent_deal_activityfor strategic buyer signals in adjacent markets. - Board prep — combine with the financial research agent to build a complete strategic-review package.
12. Disclaimer
This agent retrieves publicly available M&A information from news sources, SEC filings, and public research. It is not financial advice and does not replace professional M&A advisory services — for informational purposes only. M&A transactions involve significant complexity. Always conduct proper due diligence and consult qualified financial advisors and legal counsel before making any investment or acquisition decisions.
13. Getting your API key
Grab a free Superhighway key at /pricing (1,000 calls/month, no credit card). For an agent that provisions its own access, skip the key entirely with x402: it pays $0.002 per call in USDC on Base — no signup, no key management. See the x402 pay-per-call guide for the wallet setup.
For related builds, the financial research agent uses the same four-endpoint pattern on companies and markets, and the competitor analysis agent applies it to your market rivals.