baba/apiOpenAPI schema

Documentation

View .md

Paging and filters

Lists are paged by keyset cursor, not by offset. A cursor points at a position in the ordering rather than counting rows, so a story published while you page does not shift the page under you or make you miss a record.

Cursors

Read a page, do the work, then send nextCursor back as cursor with every other parameter unchanged. Changing a filter mid-walk invalidates the cursor and returns 400 bad_request. nextCursor is null on the final page.

The value is opaque. Do not parse it, store it as a sort key, or construct one.

Walking every page
const key = process.env.BABA_NEWS_API_KEY;
let cursor = null;

do {
  const params = new URLSearchParams({ lang: 'en', state: 'blindspot', limit: '50' });
  if (cursor) params.set('cursor', cursor);

  const res = await fetch(`https://news.itsbaba.com/api/v1/stories?${params}`, {
    headers: { Authorization: `Bearer ${key}` },
  });
  if (res.status === 429) {
    await new Promise((r) => setTimeout(r, Number(res.headers.get('Retry-After') ?? 60) * 1000));
    continue;
  }
  if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error.code}`);

  const page = await res.json();
  for (const story of page.data) handle(story);
  cursor = page.nextCursor;
} while (cursor);

Filters

ParameterRangeNotes
langen, heOutput language. Hebrew falls back to English field by field where a translation is missing. Anything that is not he or iw reads as en.
stateOne state idbreaking, developing, consensus, blindspot, undercovered, thread_update, quiet, resolved.
topicRepeatablepolitics, economy, tech, security, health, culture, sports, world, weather, general.
sourceRepeatableNewsroom keys, as returned by the sources endpoint.
since0 to 30Jerusalem calendar days back, today inclusive. Above 30 is clamped to 30.
limit1 to 100Page size, default 40. Above 100 is clamped, and the clamped value comes back as limit.
cursorOpaquenextCursor from the previous page, with every other filter unchanged.
q2 to 120 charsSearch only. Sending it to the stories endpoint is a 400.

Repeatable parameters are OR'd within themselves and AND'd across: topic=politics&topic=economy&source=ynet means politics or economy, filed by Ynet.

Conditional reads

Every read carries an ETag. Send it back as If-None-Match and an unchanged resource answers 304 with no body. It still counts as a request against quota, but it costs no bandwidth and no parsing.

Conditional read
curl --get 'https://news.itsbaba.com/api/v1/brief' \
  --header "Authorization: Bearer $BABA_NEWS_API_KEY" \
  --header 'If-None-Match: "9f2c...c41"' \
  --dump-header -

The story object

These fields appear on every story, in lists and in detail.

FieldTypeMeaning
idintegerCluster id. The path segment for the story detail endpoint.
urlstringCanonical baba URL for the story.
headlinestringLead headline in the requested language.
summarystring | nullMachine-written summary of the cluster.
topicstringDesk id.
langen | heLanguage of this payload, after fallback.
publishedAtstringISO 8601 instant the lead article was published.
latestAtstringISO 8601 instant of the newest article in the cluster. The sort key.
updatedAtstring | nullWhen the lead body last changed. Null unless it changed more than 60 seconds after publish.
outletsintegerNumber of newsrooms in the cluster.
sidesobjectCount of articles per press side, keyed by side.
missingSidesstring[]Sides that published nothing on this story.
statestring | nullCluster state. Null when none has been computed.
verdictstring | nullEditorial label attached to the state, when there is one.
leanobjectCounts of left, center, right and unrated newsrooms.
sourcesstring[]Newsroom keys in the cluster. Each is a key for the sources endpoints.
threadobject | nullRunning thread this story belongs to: id, title, days.

Story detail adds members (one record per article), timeline (firstAt, lastAt, spreadMinutes, firstSite, and the first timestamp per side), framingNote, and an outlets counted from the members it returns.