no key · no human
HomeNews API for AI agentsAgent recipes

Recipes

Patterns that work, with the reasoning behind each choice. All of them run without a key.

Monitor a topic without repeating yourself

The trap is sorting by publication date: publishers backdate, so a newly crawled article can appear behind your cursor and be missed forever. Sort by crawled, which only moves forward.

python
import requests, time

BASE = "https://freenewsapi.ai/v1/search"
seen = set()

def poll(topic, minutes=15):
    r = requests.get(BASE, params={
        "q": topic, "sort": "crawled", "from": f"now-{minutes}m", "size": 100,
    }, timeout=20).json()
    fresh = [a for a in r["results"] if a["id"] not in seen]
    seen.update(a["id"] for a in fresh)
    return fresh

while True:
    for a in poll("central bank rate decision"):
        print(a["published_at"], a["host"], a["title"])
    time.sleep(900)          # new batches land hourly; 15 min is plenty

Detect breaking news by corroboration

One publisher is a claim. Fifteen publishers within an hour is an event. Count independent hosts rather than articles — wire republication inflates the article count without adding a single source.

python
def is_breaking(topic, min_publishers=8):
    r = requests.get("https://freenewsapi.ai/v1/stats", params={
        "q": topic, "date": "24h", "top": 100,
    }, timeout=30).json()
    publishers = len(r["hosts"])
    today = list(r["by_day"].values())[0] if r["by_day"] else 0
    return publishers >= min_publishers and today > 20, publishers, today

Morning digest, deduplicated

python
from collections import defaultdict

def digest(country="US", lang="en", top=10):
    r = requests.get("https://freenewsapi.ai/v1/search", params={
        "country": country, "lang": lang, "date": "24h",
        "sort": "date", "size": 100, "strict_country": "true",
    }, timeout=20).json()

    # group by the first six significant words of the headline
    groups = defaultdict(list)
    for a in r["results"]:
        key = " ".join(w.lower() for w in a["title"].split() if len(w) > 3)[:60]
        groups[key].append(a)

    ranked = sorted(groups.values(), key=len, reverse=True)[:top]
    for g in ranked:
        print(f"{len(g)} sources | {g[0]['title']}")
        for a in g[:3]:
            print(f"    {a['host']}  {a['url']}")

Note strict_country=true: without it a US digest fills with Indian publishers whose templates declare en-US.

Compare how countries cover the same event

The most interesting question a news API can answer, and one nobody makes easy.

python
def by_country(topic, countries=("US", "GB", "DE", "FR", "IN", "UA", "BR")):
    for cc in countries:
        r = requests.get("https://freenewsapi.ai/v1/search", params={
            "q": topic, "country": cc, "date": "7d",
            "sort": "relevance", "size": 5, "strict_country": "true",
        }, timeout=20).json()
        print(f"\n=== {cc}: {r['total']} articles")
        for a in r["results"]:
            print(f"  {a['host']:<28} {a['title'][:70]}")

Feed the result to a model and ask what differs in framing between the slices. The answer is usually more interesting than the articles.

Track a story from its first appearance

python
def timeline(topic):
    r = requests.get("https://freenewsapi.ai/v1/stats",
                     params={"q": topic, "date": "30d"}, timeout=30).json()
    for day, n in sorted(r["by_day"].items()):
        print(f"{day}  {'#' * min(n // 5, 60)} {n}")

    first = requests.get("https://freenewsapi.ai/v1/search", params={
        "q": topic, "date": "30d", "sort": "date_asc", "size": 3,
    }, timeout=20).json()
    print("\nEarliest coverage:")
    for a in first["results"]:
        print(f"  {a['published_at']}  {a['host']}  {a['url']}")

Watch one publisher

bash
curl "https://freenewsapi.ai/v1/search?host=www.reuters.com&sort=crawled&size=50"

Find the exact hostname first — publishers differ on the www. prefix, and host matches literally:

bash
curl "https://freenewsapi.ai/v1/stats?q=reuters&top=20" | jq .hosts

Build a training or evaluation set

Walk hour by hour, keep the bodies, respect the throttle. The full loop is in pagination. Two rules: sort by date_asc so the window is stable while you page it, and use id for deduplication — it is the MD5 of the URL and never changes.

Planning a full-corpus export? Tell us first. There is a better way to hand you 100,000 articles than 100,000 HTTP requests, and we would rather give you the better way.