One GET endpoint. One bearer token. Cyber-security stories from 50+ outlets, rewritten into plain-language briefs and tagged by topic, as JSON.
Send your key as a bearer token:
Authorization: Bearer cisoai_...
X-API-Key: cisoai_... is accepted as an alternative, because clients reach for
it. Bearer is the documented form.
Keys belong on your server, never in a browser
Anything in client-side JavaScript is readable by every visitor. A key in a web page is a key anyone can take, and the usage bills to you. Call this API from your backend and serve the result to your own front end — there is a worked example of exactly that below.
GET https://cisoai.au/api/news
That is the whole API. There is no second endpoint, no SDK, and no OAuth flow.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 |
How many stories to return. Clamped to your plan's ceiling — asking for more is not an error, you simply receive the ceiling. |
category | string | — | Exact match on a story's category. See the list below. |
source | string | — | Exact match on the publishing outlet, e.g. Dark Reading. |
distinct | 1 | — | Collapse duplicate coverage so one event does not fill the page with nine versions of itself. Useful for a feed; leave it off for analysis. |
Threat Intelligence · Security News ·
Cybersecurity Research · Government Advisory ·
Industry News
The Hacker News, Infosecurity Magazine, CrowdStrike, Cisco Talos, Unit 42, Project Zero,
Krebs on Security, Dark Reading, CISA, ASD's ACSC, and a financial wire whose per-story publisher is
used as the source name. The live list is in every response under sources, so
you never have to hard-code it.
{
"articles": [ /* see fields below */ ],
"count": 20,
"plan": "pro",
"limit": 20,
"fullBody": true
}
| Field | Type | Description |
|---|---|---|
headline | string | Rewritten headline. |
summary | string | One sentence. |
body | string | The full rewritten article, paragraphs separated by \n\n. Paid plans only. |
keyTakeaway | string | One sentence of practical advice. |
tags | string[] | Topic tags assigned at ingest. |
category | string | One of the categories above. |
source | string | Publishing outlet. |
originalUrl | string | The original article. Link to this when you republish. |
originalTitle | string | The headline as published by the source. |
publishedAt | ISO 8601 | When the source published it. Never in the future. |
createdAt | ISO 8601 | When we ingested it. |
slug | string | Stable identifier. cisoai.au/brief/<slug> is the human-readable page. |
id | string | Internal identifier. Prefer slug. |
imageUrl | string | Source image where one exists, otherwise empty. Roughly two thirds of stories have one. |
Fields are additive. Nothing already published gets renamed or removed without notice, so parsing defensively for new keys is safe and parsing strictly is not.
| Free | Paid | |
|---|---|---|
| Stories per request | 20 | 200 |
body | Not included | Full text |
| Filters | Yes | Yes |
| Key required | No | Yes |
| Endpoint | api.cisoai.au/api/news | cisoai.au/api/news |
The free tier needs no key and no signing up — use it to decide whether the data is worth paying for. Every field name you will eventually depend on is already in its response, so moving to a paid key is a header change and nothing else.
| Status | Meaning | What to do |
|---|---|---|
401 | No key sent, or the key is not recognised. | Check the Authorization header. Retrying will not help. |
403 | The key is known but not active — usually
past_due after a failed payment. The body names the state. |
Update your card. Your key resumes working; you never need a new one. |
405 | Method other than GET. | Use GET. |
429 | Over 60 requests in a minute on this key. | Wait the number of seconds in Retry-After, then continue. Your key
is fine. If you are hitting this, cache your responses — the corpus only
changes every three hours. |
502 | The feed is briefly unavailable upstream. | Retry with backoff. Your last good response is still valid — the corpus only changes every three hours. |
503 | We could not verify your key. This is our problem, not a bad key. | Retry with backoff. |
Errors return JSON with an error field. Never treat a 5xx as an
authentication failure — the two are deliberately kept distinct so you are
not chasing a key problem during an outage.
curl "https://cisoai.au/api/news?limit=50&category=Threat%20Intelligence" \
-H "Authorization: Bearer $CISOAI_API_KEY"
// Server-side. Never ship the key to a browser.
const response = await fetch(
"https://cisoai.au/api/news?limit=200",
{ headers: { Authorization: `Bearer ${process.env.CISOAI_API_KEY}` } }
);
if (!response.ok) throw new Error(`news api ${response.status}`);
const { articles } = await response.json();
import os, requests
r = requests.get(
"https://cisoai.au/api/news",
params={"limit": 200, "distinct": 1},
headers={"Authorization": f"Bearer {os.environ['CISOAI_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
articles = r.json()["articles"]
The pattern to copy: a small server-side route holds the key and your page calls that route same-origin. This is how bugxhunter.com renders its feed — a Cloudflare Pages Function that proxies, with the key in a secret and never in the markup.
// functions/api/news.js — your origin, your cache, your key stays server-side
export async function onRequest({ env }) {
const upstream = await fetch("https://cisoai.au/api/news?limit=24", {
headers: { Authorization: `Bearer ${env.CISOAI_API_KEY}` },
});
if (!upstream.ok) return new Response('{"articles":[]}', {
headers: { "Content-Type": "application/json" },
});
return new Response(await upstream.text(), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=300",
},
});
}
Note the empty-list fallback rather than an error: a news section is worth degrading, not worth breaking a page over.
429 Rate limit exceeded with a Retry-After
header; wait that long and you are through. It exists so a runaway loop is throttled
rather than billed, and it is roughly two orders of magnitude above the polling this
page recommends. Planning something high-volume?
Tell us first and we will say honestly whether
this is the right fit.Briefs are rewritten summaries of publicly available reporting, not reproductions of it.
Every record names its source and links the originalUrl.
originalUrl when you republish. The reporting is
someone else's work and the link is how they are credited.