News API

One GET endpoint. One bearer token. Cyber-security stories from 50+ outlets, rewritten into plain-language briefs and tagged by topic, as JSON.

Authentication

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.

The endpoint

GET https://cisoai.au/api/news

That is the whole API. There is no second endpoint, no SDK, and no OAuth flow.

Parameters

ParameterTypeDefaultDescription
limitinteger20 How many stories to return. Clamped to your plan's ceiling — asking for more is not an error, you simply receive the ceiling.
categorystring Exact match on a story's category. See the list below.
sourcestring Exact match on the publishing outlet, e.g. Dark Reading.
distinct1 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.

Categories

Threat Intelligence · Security News · Cybersecurity Research · Government Advisory · Industry News

Sources

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.

Response

{
  "articles": [ /* see fields below */ ],
  "count": 20,
  "plan": "pro",
  "limit": 20,
  "fullBody": true
}

Article fields

FieldTypeDescription
headlinestringRewritten headline.
summarystringOne sentence.
bodystringThe full rewritten article, paragraphs separated by \n\n. Paid plans only.
keyTakeawaystringOne sentence of practical advice.
tagsstring[]Topic tags assigned at ingest.
categorystringOne of the categories above.
sourcestringPublishing outlet.
originalUrlstringThe original article. Link to this when you republish.
originalTitlestringThe headline as published by the source.
publishedAtISO 8601When the source published it. Never in the future.
createdAtISO 8601When we ingested it.
slugstringStable identifier. cisoai.au/brief/<slug> is the human-readable page.
idstringInternal identifier. Prefer slug.
imageUrlstringSource 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 and paid

FreePaid
Stories per request20200
bodyNot includedFull text
FiltersYesYes
Key requiredNoYes
Endpointapi.cisoai.au/api/newscisoai.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.

Pricing and keys →

Errors

StatusMeaningWhat to do
401No key sent, or the key is not recognised. Check the Authorization header. Retrying will not help.
403The 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.
405Method other than GET.Use GET.
429Over 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.
502The feed is briefly unavailable upstream. Retry with backoff. Your last good response is still valid — the corpus only changes every three hours.
503We 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.

Examples

curl

curl "https://cisoai.au/api/news?limit=50&category=Threat%20Intelligence" \
  -H "Authorization: Bearer $CISOAI_API_KEY"

Node

// 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();

Python

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"]

Serving it to your own front end

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.

Limits and freshness

Managing your key

Attribution and terms

Briefs are rewritten summaries of publicly available reporting, not reproductions of it. Every record names its source and links the originalUrl.

Questions: [email protected]
Get a key  ·  Home  ·  Coverage analysis