Every item on the feed is available as JSON, built to be polled by a script rather than read by a
human. No API key, no account. Responses carry an ETag; send back
If-None-Match and a poll that found nothing new costs a 304 with no body.
| param | meaning |
|---|---|
mode | new (default: the / tape in its order, 50 rows unless limit), top (the top 24h tab's rows, newest first) or all |
since | an item id or an ISO 8601 timestamp — only items newer than that |
min_score | only items scoring at least this |
limit | cap the number of rows returned (max 500) |
q | case-insensitive substring match on title or summary |
watch | comma-separated provider slugs (up to 8) —
matches a row from that provider's own channel, or naming one of its models, e.g. watch=anthropic,openai |
id | look up one item by id, any age, ignores every other param |
explain | explain=1 adds a explain score breakdown (tier/category weight, diff and burst adjustments, velocity, decay) to every item — the same data /i/<id> renders |
An unrecognized parameter returns 400 with a JSON error body instead of being silently
ignored.
curl 'https://aisquawk.io/api/items.json' curl 'https://aisquawk.io/api/items.json?mode=all&limit=20' curl 'https://aisquawk.io/api/items.json?min_score=70' curl 'https://aisquawk.io/api/items.json?q=anthropic' curl 'https://aisquawk.io/api/items.json?watch=anthropic,openai' curl 'https://aisquawk.io/api/items.json?since=2026-09-16T00:00:00Z' curl 'https://aisquawk.io/api/items.json?id=1' curl 'https://aisquawk.io/api/items.json?id=1&explain=1'
Polling for new items every minute: keep the highest id you've seen
and pass it back as since — every response is only rows you don't already have, so
there's nothing to dedup:
SINCE=0 while true; do BATCH=$(curl -s "https://aisquawk.io/api/items.json?mode=all&since=$SINCE&limit=200") echo "$BATCH" | jq -c '.[]' SINCE=$(echo "$BATCH" | jq '([.[].id] + ['"$SINCE"']) | max') sleep 60 done
Each item: id, title, url (the original source),
permalink (this site's own stable link), source (feed name),
publisher (registrable domain), tier, category,
summary, score, scoreAtPublish, publishedAt (ISO 8601), dateOnly, sourceDate, alsoSources,
relevant, models (up to 2 model slugs this item names),
provider (a provider slug), changes — an
array, empty except on a value-diff PRICING row, of
{provider, model, field, old, new, pct, unit} so a script can react to one price moving
without parsing the prose summary — incident, null
except on an OUTAGE row: {state: "open"|"resolved", openedAt, resolvedAt,
durationMin}, resolvedAt/durationMin null until it resolves —
and deadline, null except on a shutdown-reminder DEPRECATION row
or a model-list “removed” row whose id sits in the provider's deprecated table:
{provider, model, deprecatedAt, shutdownAt, replacement, daysLeft, sourceUrl}, daysLeft
recomputed on every request so it never goes stale — and breaking, null
except on a breaking library release (DEPRECATION from GitHub releases): up to 3 short
strings, verbatim from the notes' own breaking section, of what was removed or changed.
Every response also carries Access-Control-Allow-Origin: * so it's fetchable straight
from a browser page on another domain, and Cache-Control: max-age=20.
Also see RSS (per-category with ?category=, or add
?mode=top/?min_score= — same vocabulary as above, silently ignored
if invalid rather than a 400, since a feed reader can't fix its own URL) and
push webhooks for the same feed delivered to you instead of polled. All
/api/* routes share one per-IP limit: 60 requests/minute, 429 with
Retry-After past that — a poller checking every 20–60s never gets close.
No polling, no client library: curl -N against /tail prints the
last 20 rows, then every new row as it lands, one line each. A reload ends the stream, so leave it
in a loop if you want it to survive a restart:
while true; do curl -N 'https://aisquawk.io/tail'; sleep 1; done
Filters match /api/items.json's vocabulary: min_score,
category, provider (a provider slug), and
q. Add ?format=ndjson for one JSON object per line instead of the plain
HH:MMZ CATEGORY score title — source url text format — ndjson lines also carry
the same changes/incident fields /api/items.json does. An
OUTAGE row that resolves after you've already seen it open prints a second line
(HH:MMZ OUTAGE RESOLVED after Nm ... in text, {"event": "resolved", ...} in
ndjson) rather than staying silent until you re-poll:
curl -N 'https://aisquawk.io/tail?category=OUTAGE' curl -N 'https://aisquawk.io/tail?format=ndjson&min_score=70' | jq -c .
No accounts: the URL you register is the subscription. POST /api/webhooks with a JSON
body registers one, DELETE /api/webhooks with the same {"url": ...} body
unsubscribes it. We POST new items that clear the top-mode score bar (and every filter you set) as
they publish, capped at 10 items/delivery, 10s timeout. An open OUTAGE you were sent
fires one more time when it resolves — same filters, payload marked "event":
"resolved" (a ✅ Resolved prefix on discord/slack) — so you're not stuck
polling to find out an incident ended. A webhook failing 5 cycles in a row, or
failing for 24h straight, is paused — check /stats for the delivery log.
| field | meaning |
|---|---|
url | required; https/http only, no localhost or private-network hosts |
format | required; generic, discord, or slack |
categories | optional array, e.g. ["PRICING", "OUTAGE"] — omit for every category |
providers | optional array of slugs from /providers, e.g. ["anthropic", "openai"] |
sources | optional array of exact source names, e.g. ["Anthropic Blog"] |
min_score | optional integer 0-100 — raises the delivery bar above the default 50 |
models | optional array of model ids, same grammar as /check (explicit, suffix-stripped, never fuzzy) — delivers only rows concerning those ids: a price change, retirement, delisting, or an incident on one of their providers |
Every given filter must pass (an AND, not an OR) — "Anthropic pricing moves or an OpenAI incident
opens" is two webhooks (providers: ["anthropic"], categories: ["PRICING"] and
providers: ["openai"], categories: ["OUTAGE"]), not one.
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint", "format": "generic"}'
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://discord.com/api/webhooks/...", "format": "discord", "categories": ["RELEASE"]}'
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://hooks.slack.com/services/...", "format": "slack", "providers": ["anthropic"], "categories": ["PRICING"]}'
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint", "format": "generic", "min_score": 80}'
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint", "format": "generic", "sources": ["OpenAI Blog"]}'
curl -X POST 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint", "format": "generic", "models": ["gpt-4o-mini (2024-07-18)"]}'
POST /api/webhooks/test sends the most recent item that matches an already-registered
webhook's own filters, so you see a real delivery without waiting for one to publish:
curl -X POST 'https://aisquawk.io/api/webhooks/test' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint"}'
curl -X DELETE 'https://aisquawk.io/api/webhooks' -H 'Content-Type: application/json' \
-d '{"url": "https://your-endpoint"}'
Payload shapes: generic sends {"items": [...]} matching the JSON API row
shape, changes array included; discord sends native embeds (title/url/
description/color-by-category/footer/timestamp), a value-diff row appends one line per change to the
description; slack sends Block Kit (blocks) for a Slack incoming webhook
— linked title, one-line summary (a value-diff row appends the same per-change lines), and a
context line with a score chip, category, and source.
An MCP (Model Context Protocol) server at https://aisquawk.io/mcp so an agent living in Claude
Code, Cursor, or your own tooling can query the feed, pricing, outages, deprecations, models and
calendar directly — no API key, stateless JSON-RPC over plain HTTP (no SSE, no session id).
Tools: latest_items, search_items, pricing,
pricing_changes, deprecations, outages, models,
calendar.
claude mcp add --transport http aisquawk https://aisquawk.io/mcp
mcp.json{
"mcpServers": {
"aisquawk": { "url": "https://aisquawk.io/mcp" }
}
}
curl -X POST 'https://aisquawk.io/mcp' -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"outages","arguments":{}}}'