NeutralFeed.com

Documentation

API reference

One base URL, four endpoints, and a response shape that has not changed since v1 shipped in 2017. There is no v2 planned.

Quickstart

Create a key on the pricing page, export it, and make one request. There is no SDK to install and no onboarding call.

shell

$ export NF_KEY=nf_live_8f2c4b19a0e7
$ curl -sH "Authorization: Bearer $NF_KEY" \
    "https://api.neutralfeed.com/v1/feed?limit=1" | jq .items[0].headline

"There are ten cloud genera"

Authentication

A bearer token in the Authorization header. Keys are scoped to an environment and may be restricted to a set of channels. There is no OAuth flow, because there is no user data to delegate access to — nothing the API returns belongs to anybody.

header

Authorization: Bearer nf_live_8f2c4b19a0e7

GET /v1/feed

Returns calibrated items. All parameters are optional; with none, you get one item from any channel at the default reading level.

ParameterDefaultNotes
channelallOne key or a comma-separated list.
limit11 to 50. Fetching a batch is one request; fetching them singly is not.
presetlobby, column, kiosk or ticker.
reading_level96, 9, 12 or 14.
unitsmetricmetric or imperial. Applied server-side.
localeen-GB41 supported. Affects number formatting as well as text.
rotation_tokenEcho back the token from the previous response and you will not see the same item twice inside the window.
max_novelty0.20Tighten it if our threshold is too lively for your room.

The item schema

Every item, on every endpoint, in every delivery method, has this shape.

application/json

{
  "id": "met_0088",
  "channel": "meteorology",
  "headline": "There are ten cloud genera",
  "body": "Cirrus, cirrocumulus, cirrostratus, altocumulus, ...",
  "calibration": {
    "stakes": 0.00,
    "controversy": 0.00,
    "urgency": 0.00,
    "novelty": 0.16
  },
  "sources": 5,
  "citations": [
    "WMO-No. 407, International Cloud Atlas, 2017 ed.",
    "Howard, L. (1803) On the Modifications of Clouds",
    "Met Office Fact Sheet 1: Clouds"
  ],
  "verified": "2026-03-08",
  "half_life": "indefinite",
  "reading_level": 8,
  "duration_s": 20
}

half_life

How long the item can be shown before it should be re-verified. Most are indefinite. A timetable is 1 year. Nothing in the corpus is shorter than a month; if it were, it would be news.

duration_s

How long to leave it on screen, derived from length and reading level. Honour it and the rotation feels considered. Ignore it and it feels like a slideshow.

GET /v1/channels

Returns the six channels with item counts and the date each was last added to. Useful for building a picker; not useful for much else, as the list changes about once every four years.

Streaming

GET /v1/stream holds the connection open and emits an item on each rotation, so a display that runs for six months makes one request rather than six hundred thousand.

text/event-stream

event: item
data: {"id":"inf_2290","headline":"The Falkirk Wheel turns on about 1.5 kilowatt-hours",...}

event: heartbeat
data: {"t":"2026-08-06T09:14:00Z"}

Web component

For pages rather than displays. One script, one element. If the network is unavailable it renders the last item it saw, and if it has never seen one it renders nothing at all rather than an error.

html

<!-- one script, deferred -->
<script src="https://cdn.neutralfeed.com/v1/nf.js" defer></script>

<neutral-feed
    key="nf_pub_2b71c0"
    channel="infrastructure"
    preset="lobby"></neutral-feed>

Errors and limits

StatusMeaning
200Fine. Check X-NF-Stale; if it is 1 you are over quota and this is the last good response rather than a fresh one.
304Nothing has rotated since your If-None-Match. The expected response for most polls.
401Missing or malformed key.
403Key is not scoped to the requested channel.
422A parameter is out of range. The body names it.
429Burst limit, not the daily quota. Retry after the header says to.

On exceeding the daily quota

The API does not start returning errors. It returns the most recent successful response with X-NF-Stale: 1. A blank screen in a public room is a worse outcome than a screen showing yesterday's item, and yesterday's item is still true.