Build an automated news briefing agent

Superhighway guides

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:

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:

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.