Building a Morning Briefing Agent with the News Search API

Every founder, PM, and analyst wants the same thing at 8am: a clean digest of what changed overnight. With a News Search API, an LLM, and an HTTP-cron platform, you can ship one in an afternoon.
Resumen
- •Architecture: cron → News Search → URL Extract for promising stories → LLM summary → Slack/email post.
- •Tune freshness with <code>start_date</code> = today − 1; tune locale with <code>country_code</code>.
- •Chunk briefings by topic with parallel News Search queries; the LLM merges and de-duplicates.
- •Total cost is ~15 credits per topic per day, plus LLM tokens. A 5-topic briefing is < $0.10/day at list price.
The shape of a useful briefing
A morning briefing agent should answer one question: \"what changed overnight that I need to know about?\" The bar is harder than it sounds. A dump of headlines fails — it's the same as RSS and gets muted within a week. The output that survives is a 5-bullet digest with links, organised by topic, in the user's voice.
That decomposes into a small pipeline:
- Fetch recent news across N topics — News Search API.
- Extract the body of any link the model wants to quote — URL Extract API.
- Summarise + cluster with an LLM — your model of choice.
- Post to Slack / email / Notion — your existing comms channel.
- Schedule with a cron — Vercel Cron, GitHub Actions, n8n, etc.
Step 1: Pick your topics
Keep it to 3–6 topics. Briefings with 12 topics are scrolled past. Examples:
- For a B2B SaaS founder: \"AI funding rounds\", \"OpenAI / Anthropic news\", \"competitor moves\".
- For a financial analyst: \"FOMC and Treasury\", \"semiconductor supply chain\", \"specific tickers\".
- For a healthcare PM: \"FDA approvals\", \"clinical-trial readouts\", \"reimbursement changes\".
Step 2: Fetch the raw signal
Run one News Search call per topic in parallel. Use start_date to cap freshness:
from datetime import date, timedelta
import asyncio, aiohttp
KEY = "pk_yourkey"
TOPICS = [
"AI agent infrastructure funding",
"OpenAI Anthropic Google new releases",
"RAG and LLM tool calling research",
]
async def fetch_topic(session, q):
yesterday = (date.today() - timedelta(days=1)).isoformat()
async with session.post(
"https://www.apipick.com/api/search/news",
headers={"x-api-key": KEY},
json={"query": q, "start_date": yesterday},
) as r:
return q, await r.json()
async def fetch_all():
async with aiohttp.ClientSession() as s:
return await asyncio.gather(*[fetch_topic(s, q) for q in TOPICS])Each call returns up to 5 ranked headlines (max 10 with num_results). For a 3-topic briefing that's 3 × 15 = 45 credits per run. At the $5 / 5,000-credits rate, $0.045/day. Daily cost is dominated by the LLM, not the news API.
Step 3: Selectively extract bodies
You don't need the full content of every story — just the ones the model wants to quote in detail. A simple heuristic: extract the top 2 stories per topic. With API Pick that's a single batch call:
import requests
urls = [r["url"] for topic, payload in results for r in payload["results"][:2]]
extracted = requests.post(
"https://www.apipick.com/api/extract",
headers={"x-api-key": KEY},
json={"urls": urls, "extract_effort": "auto"},
).json()At 2 credits per URL, the extract step adds ~12 credits/day for a 3-topic briefing.
Step 4: The summariser prompt
The prompt is where the briefing's voice lives. A starter:
You are an assistant that writes a morning briefing in <Sarah's> voice:
direct, no fluff, no marketing language.
Input: a JSON list of {topic, headlines, extracted_bodies}.
Output rules:
- 1 short paragraph per topic, max 60 words.
- Each paragraph ends with up to 3 inline source links.
- If a topic has fewer than 2 substantive stories overnight, omit it.
- If the entire briefing has fewer than 2 substantive topics, output the
literal token SKIP and nothing else.
- Never editorialise. Quote facts and figures verbatim from the sources.
Output format: Slack-flavoured markdown.The \"SKIP if low-signal\" instruction is the single highest-leverage rule. It's why your briefing won't get muted.
Step 5: Post and schedule
For a Python cron, the entire script is < 80 lines. The Slack post:
if briefing.strip() == "SKIP":
return
requests.post(
SLACK_WEBHOOK_URL,
json={"text": briefing, "username": "Morning Briefing", "icon_emoji": ":sunrise:"},
)Schedule with Vercel Cron (in vercel.json), GitHub Actions, AWS EventBridge, or n8n. Run at 7:45am in your timezone — the goal is for it to be waiting when you sit down at 8.
Doing it in n8n if you prefer no-code
- Schedule trigger — daily, 7:45 user-tz.
- HTTP Request nodes (one per topic), POST
/api/search/newswithstart_datefrom yesterday. - Merge node combines the topic results.
- HTTP Request node calls
/api/extractwith top URLs. - OpenAI / Anthropic node runs the summariser prompt.
- If node checks the output isn't SKIP.
- Slack node posts the briefing.
What to watch for in production
- Topic drift. \"AI agents\" is too broad after 6 months — the API's index grows; your briefing stays specific. Tighten queries quarterly.
- Repetition across days. Pass the prior day's URLs to the model with \"don't re-cover stories from this list\" if the same story keeps appearing.
- Quiet weekends. The SKIP rule already handles this; just don't pin your daily check-in habit to the briefing.
Iterate, don't ship-and-forget
Read the briefing every morning for a week. Note where the model lied, miscategorised, or missed something. Update the prompt. After a week of iteration the briefing becomes a habit — and you've built a working agent with about 80 lines of code and three API calls.
Preguntas frecuentes
Why not just use Google News or an RSS aggregator?
Both work — and both leave you doing the boring part: clustering, deduplicating, summarising, and writing the actual briefing. An agent does that part with an LLM. RSS gives you headlines; an agent gives you a paragraph that says what matters and links to sources.
How fresh is the News Search API?
The API indexes major outlets in near-real-time. For an 8am briefing covering the prior 24 hours, set start_date to yesterday. For breaking-news bots that run hourly, set start_date to today and re-run the agent with deduplication against your last run.
What if the briefing has nothing new to say?
Build that into the prompt: 'If fewer than 3 substantive stories changed overnight, return SKIP.' Your cron job should respect SKIP and not post a low-signal briefing. Briefing-fatigue is the fastest way to get a Slack channel muted.
Can I run this without n8n?
Yes. The same flow runs as a 30-line Python script on a cron — see the example below. n8n is convenient for visual debugging and Slack integration, but it's not required.
How do I prevent the same story being summarised across topics?
Pass the model the URLs from prior queries in the same run with the instruction: 'do not re-mention any URL already covered.' Or run a single multi-topic search and let the model partition. Both work; the second is cheaper.
APIs usadas en este artículo
Sarah Choy es la CEO de API Pick. Escribe sobre cómo construir APIs listas para producción para agentes de IA y flujos de trabajo con LLMs.