Build an automated news briefing agent
Most guides here show how to plug Superhighway into a framework. This one is different: it walks through building a specific, useful thing from scratch — an agent that wakes up each morning, pulls fresh news on the topics you care about, deduplicates the noise, and uses an LLM to write a tight briefing paragraph per topic. The output is a clean Markdown digest you can read over coffee, email to a team, or post to Slack. By the end you'll have a single Python script you can drop onto a cron job or a GitHub Action.
1. What you'll build
A self-contained Python script that:
- Fetches news on multiple topics using Superhighway's
/newsendpoint - Deduplicates articles so the same story doesn't show up twice
- Uses an LLM to write a concise 2–3 sentence briefing for each topic
- Outputs a formatted daily briefing in Markdown
- Runs unattended on a cron job or scheduled GitHub Action
2. Setup
Install the three dependencies:
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. Fetch news on a topic
The core building block is a function that hits /news for a single topic and returns a list of article objects:
import requests, os
from datetime import datetime
from dotenv import load_dotenv
load_dotenv()
SUPERHIGHWAY_KEY = os.getenv("SUPERHIGHWAY_API_KEY")
def fetch_news(topic: str, max_articles: int = 5) -> list[dict]:
r = requests.get(
"https://superhighway.walls.sh/news",
params={"q": topic, "count": max_articles},
headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"}
)
r.raise_for_status()
return r.json().get("articles", [])
Each article comes back with title, url, description, source, and publishedAt. A quick smoke test:
# Each article has: title, url, description, source, publishedAt
articles = fetch_news("artificial intelligence agents")
for a in articles:
print(f"{a['title']} — {a['source']}")
print(f" {a['url']}")
4. Deduplicate articles
News APIs surface the same story from multiple feeds. A simple URL-based pass keeps the first occurrence and drops repeats:
def deduplicate(articles: list[dict]) -> list[dict]:
seen_urls = set()
unique = []
for a in articles:
if a["url"] not in seen_urls:
seen_urls.add(a["url"])
unique.append(a)
return unique
For tighter dedup you could compare normalized titles or domains, but URL matching catches the most common duplicates with zero overhead.
5. Generate a briefing paragraph with an LLM
Feed the deduplicated headlines and descriptions to an LLM and ask for a short, factual briefing. gpt-4o-mini is fast and cheap enough to run across many topics every morning:
from openai import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def write_briefing(topic: str, articles: list[dict]) -> str:
article_text = "\n".join(
f"- {a['title']} ({a.get('source', 'unknown')}): {a.get('description', '')}"
for a in articles[:5]
)
response = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a news editor writing concise briefings."},
{"role": "user", "content": f"Write a 2-3 sentence briefing on \"{topic}\" based on these articles:\n\n{article_text}\n\nBe factual and concise."}
]
)
return response.choices[0].message.content
Passing the article descriptions — not just titles — gives the model enough context to write something accurate rather than generic. Keeping the limit at five articles per topic keeps token use low.
6. Full briefing pipeline
Now wire it all together. Define your topics, then loop: fetch, dedup, summarize, and append a section with linked sources:
TOPICS = [
"AI agents and autonomous systems",
"large language model releases",
"AI regulation and policy",
]
def generate_briefing(topics: list[str]) -> str:
today = datetime.now().strftime("%B %d, %Y")
sections = [f"# AI News Briefing — {today}\n"]
for topic in topics:
articles = fetch_news(topic)
unique = deduplicate(articles)
if not unique:
continue
briefing = write_briefing(topic, unique)
sections.append(f"## {topic.title()}\n\n{briefing}\n")
sections.append("**Sources:**")
for a in unique[:3]:
sections.append(f"- [{a['title']}]({a['url']})")
sections.append("")
return "\n".join(sections)
if __name__ == "__main__":
briefing = generate_briefing(TOPICS)
print(briefing)
# Save to file
with open("briefing.md", "w") as f:
f.write(briefing)
Run python briefing.py and you get a dated Markdown digest printed to the terminal and written to briefing.md — one section per topic, each with a short summary and three linked sources.
7. Running on a schedule
The whole point of a briefing agent is that it runs itself. The simplest option is cron on any always-on machine:
# Run every morning at 7am
0 7 * * * cd /path/to/project && python briefing.py >> briefing.log 2>&1
If you'd rather not keep a machine running, schedule it as a GitHub Action. Commit the script, add your two keys as repository secrets, and drop this in .github/workflows/briefing.yml:
name: Daily News Briefing
on:
schedule:
- cron: '0 7 * * *'
workflow_dispatch:
jobs:
briefing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install openai requests python-dotenv
- run: python briefing.py
env:
SUPERHIGHWAY_API_KEY: ${{ secrets.SUPERHIGHWAY_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
The workflow_dispatch trigger lets you run it manually from the Actions tab to test, while the cron schedule handles the daily run.
8. Extending the agent
The base agent is deliberately small so it's easy to extend. Some natural next steps:
- Use Superhighway's
/searchto deep-dive any article that catches the model's attention - Use
/scrapeto pull the full article text when the description is too thin to summarize well - Filter to fresh stories only with
?time=day— Superhighway's/newssupportstime=day|week|month|year - Send the finished briefing via email with Python's
smtplibor an email API - Post it to Slack through an incoming webhook
- Swap OpenAI for Groq for faster, cheaper LLM summarization
Adding the time=day filter is the single highest-value tweak — it guarantees every briefing is genuinely today's news rather than a mix of recent and stale stories:
params={"q": topic, "count": max_articles, "time": "day"}
9. 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.
From here, the OpenAI function calling guide shows how to let the model decide when to search, and the Groq guide covers swapping in a faster summarization model.