Skip to main content

API Reference

Complete API reference for Helix News Feeds. All endpoints use JSON for request and response bodies.

Interactive playground

An interactive OpenAPI playground is available at api.feeds.onhelix.ai/docs. The spec is generated from the live route validators, so it cannot drift from the running implementation — useful for testing requests, inspecting response shapes, and generating client SDKs.

Base URL​

https://api.feeds.onhelix.ai

Authentication​

All requests require an API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

See the Authentication Guide for details.

News Feed Management​

Create a Feed​

Creates a new news feed to organize articles from multiple sources.

POST /feeds/news

Request​

Headers:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Body:

{
"name": "Technology News",
"description": "Latest technology and startup news"
}

Parameters:

NameTypeRequiredDescription
namestringYesFeed name. Minimum 1 character.
descriptionstringYesFeed description. Minimum 1 character.
configobjectNoversion (1) and media.retentionDaysAfterAdded (see below). Other keys, including contentModeration, are refused with 400.

config.media.retentionDaysAfterAdded is the number of days after an item enters the feed that its images stay available. A whole number from 1 to 365. The default is 30. Once you set it, you can change it but not remove it: to go back to the default, set it to 30.

A new feed starts disabled and with no filters. Add its websites (Sources) and its filters (Filters), then enable it. Enabling it brings in, from each of its enabled sources, the articles the source brought in during the past 14 days that pass its filters (once per source).

Response​

Status: 200 OK

{
"success": true,
"data": {
"id": "7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57",
"name": "Technology News",
"description": "Latest technology and startup news",
"organizationId": "660e8400-e29b-41d4-a716-446655440001",
"enabled": false,
"enabledSource": "system",
"enabledReason": null,
"config": { "version": 1 },
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}

Response object:

FieldTypeDescription
idstringUnique feed identifier. Use this when adding sources or retrieving items.
namestringFeed name
descriptionstringFeed description
organizationIdstringThe organization that owns the feed
enabledbooleanWhether the feed is on. A new feed starts disabled: it decides no articles until you enable it
enabledSourcestringHow enabled was last set: system (the default), override (a change through this API or by our team), cascade (turned off with what it depends on) or error (turned off after failures). You cannot set it
enabledReasonstring or nullThe reason recorded when enabled was last set, if any. You cannot set it
configobjectThe stored feed config: version and media.retentionDaysAfterAdded.
createdAtstring (ISO 8601)When the feed was created
updatedAtstring (ISO 8601)Last time the feed was modified

config echoes the stored config. Keys that were never set are omitted. When media.retentionDaysAfterAdded is absent, the default of 30 days applies. Create, get, list and update return the same feed object.

Errors​

  • 400: INVALID_BODY: a missing or empty name or description, or a config key other than version and media (including contentModeration), or media.retentionDaysAfterAdded outside 1 to 365.

Example​

curl -X POST https://api.feeds.onhelix.ai/feeds/news \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Technology News",
"description": "Latest technology and startup news"
}'

List Feeds​

Retrieves the news feeds your organization owns and those it subscribes to.

GET /feeds/news

Request​

Headers:

Authorization: Bearer YOUR_API_KEY

Response​

Status: 200 OK

{
"success": true,
"data": [
{
"id": "7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57",
"name": "Technology News",
"description": "Latest technology and startup news",
"organizationId": "660e8400-e29b-41d4-a716-446655440001",
"enabled": true,
"enabledSource": "override",
"enabledReason": "Enabled through the API",
"config": { "version": 1, "media": { "retentionDaysAfterAdded": 30 } },
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
},
{
"id": "a2d49f10-8c6e-4b37-b5e1-0f93c7d1e842",
"name": "Business News",
"description": "Business and finance updates",
"organizationId": "660e8400-e29b-41d4-a716-446655440001",
"enabled": false,
"enabledSource": "system",
"enabledReason": null,
"config": { "version": 1, "media": { "retentionDaysAfterAdded": 30 } },
"createdAt": "2024-01-14T15:20:00.000Z",
"updatedAt": "2024-01-14T15:20:00.000Z"
}
]
}

Returns an array of feed objects sorted by creation date (newest first).

Example​

curl https://api.feeds.onhelix.ai/feeds/news \
-H "Authorization: Bearer YOUR_API_KEY"

Get a Feed​

Retrieves details for a specific feed.

GET /feeds/news/{feedId}

Request​

Path parameters:

NameTypeDescription
feedIdstringFeed identifier returned when creating the feed

Headers:

Authorization: Bearer YOUR_API_KEY

Response​

Status: 200 OK

{
"success": true,
"data": {
"id": "7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57",
"name": "Technology News",
"description": "Latest technology and startup news",
"organizationId": "660e8400-e29b-41d4-a716-446655440001",
"enabled": true,
"enabledSource": "override",
"enabledReason": "Enabled through the API",
"config": { "version": 1, "media": { "retentionDaysAfterAdded": 30 } },
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}

Example​

curl https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57 \
-H "Authorization: Bearer YOUR_API_KEY"

Subscribers to the feed can read it too.

Errors​

  • 400: INVALID_PARAMS: feedId is not a UUID.
  • 404: Feed not found, or your organization neither owns it nor subscribes to it.

Update a Feed​

Updates a feed's name, description, or enabled status.

PATCH /feeds/news/{feedId}

Request​

Path parameters:

NameTypeDescription
feedIdstringFeed identifier

Headers:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Body (all fields optional):

{
"name": "Tech & Startup News",
"description": "Updated description",
"enabled": true
}

Parameters:

NameTypeRequiredDescription
namestringNoUpdated feed name
descriptionstringNoUpdated feed description
enabledbooleanNoEnable or disable the feed. A disabled feed keeps its items but takes no new articles in. Enabling needs at least one include filter on the feed. Enabling a feed adds, once per source, the articles each enabled source brought in during the past 14 days that pass its filters: a source already brought in is not brought in again when you enable the feed later, and a source added while the feed was disabled is brought in when you enable it.
configobjectNoversion (1) and media.retentionDaysAfterAdded (days, 1 to 365, default 30: how long an item's images stay available after it enters the feed). Keys you leave out keep their value. Other keys, including contentModeration, are refused with 400.

Response​

Status: 200 OK

{
"success": true,
"data": {
"id": "7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57",
"name": "Tech & Startup News",
"description": "Updated description",
"organizationId": "660e8400-e29b-41d4-a716-446655440001",
"enabled": true,
"enabledSource": "override",
"enabledReason": "Enabled through the API",
"config": { "version": 1, "media": { "retentionDaysAfterAdded": 30 } },
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T14:25:00.000Z"
}
}

The updatedAt field reflects the modification time. A request whose enabled changes the feed's status records enabledSource: "override" with enabledReason "Enabled through the API" or "Disabled through the API"; one that leaves the status as it is leaves both as they are.

Which articles a feed carries is set by its filters, not by config. A contentModeration key in config is refused with 400: the safety floor applies to every news feed and cannot be switched off. See Which Articles a Feed Carries.

Only the feed's owner can change it. A refused update changes nothing.

Errors​

  • 400: INVALID_BODY: invalid fields, a config key other than version and media (including contentModeration), or config.media.retentionDaysAfterAdded outside 1 to 365.
  • 403: PERMISSION_DENIED: your organization subscribes to the feed but does not own it.
  • 404: Feed not found, or your organization neither owns it nor subscribes to it.
  • 422: VALIDATION_FAILED: enabled: true on a disabled feed that has no include filter. Add an include filter to the feed first.

Example​

curl -X PATCH https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Tech & Startup News",
"enabled": true
}'

Delete a Feed​

Permanently deletes a feed with its sources, filters, items and the feedback on them. The articles themselves stay: other feeds that carry them keep them. Only the feed's owner can delete it.

Deleting a feed also removes it from every Event Delivery subscription's pins. A feeds-scoped subscription left with zero pins becomes inactive with inactiveReason: "feed deleted". Subscribers receive news.item.removed for each article they were sent news.item.added for, about 10 to 30 minutes after the deletion. See Event Delivery: Feed deletion.

DELETE /feeds/news/{feedId}

Request​

Path parameters:

NameTypeDescription
feedIdstringFeed identifier

Headers:

Authorization: Bearer YOUR_API_KEY

Response​

Status: 200 OK

{
"success": true
}

Warning: This action is permanent and cannot be undone.

Errors​

  • 403: PERMISSION_DENIED: your organization subscribes to the feed but does not own it.
  • 404: Feed not found, or your organization neither owns it nor subscribes to it.

Example​

curl -X DELETE https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57 \
-H "Authorization: Bearer YOUR_API_KEY"

Get Feed Items​

Retrieves the items of a feed. Every item has the same shape, whether the article came from a website, Instagram, TikTok, Twitter or Facebook.

GET /feeds/news/{feedId}/items

Request​

Path parameters:

NameTypeDescription
feedIdstringFeed identifier

Query parameters:

All filters combine with AND.

NameTypeDefaultDescription
limitinteger50Number of items to return. Range: 1-100.
offsetinteger0Number of items to skip. Use for pagination.
addedSincestring (ISO 8601)-Only items added to the feed at or after this time.
addedUntilstring (ISO 8601)-Only items added to the feed at or before this time.
updatedSincestring (ISO 8601)-Only items whose updatedAt is at or after this time. Use it to poll for changes.
publishedSincestring (ISO 8601)-Only items published at or after this time. Items without publishedAt never match.
publishedUntilstring (ISO 8601)-Only items published at or before this time. Items without publishedAt never match.
hasCovertrue-Only items that have a cover image right now. The only accepted value is true. An item whose cover cannot be signed at request time still matches and is returned with cover: null.
orderBystringaddedadded: newest added to the feed first. published: newest published first; items without publishedAt come last, newest added first among themselves.

The five time filters take an ISO 8601 date-time with Z or a UTC offset, for example 2026-10-20T00:00:00Z or 2026-10-20T09:00:00-04:00. A date alone (2026-10-20) is a 400. include, since and until are not parameters of this endpoint and are refused with 400 (use addedSince and addedUntil).

Headers:

Authorization: Bearer YOUR_API_KEY

Response​

Status: 200 OK

{
"success": true,
"data": {
"items": [
{
"id": "3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10",
"newsPageId": "9b2e7c44-1d3a-4f65-8a7b-0c5d2e9f1b33",
"addedToFeedAt": "2026-10-20T14:03:00.000Z",
"updatedAt": "2026-10-20T14:03:00.000Z",
"title": "Sterling Heights approves library expansion",
"description": "The city council voted 6-1 on Monday to fund a 12,000-square-foot addition to the Sterling Heights Public Library.",
"url": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publishedAt": "2026-10-20T09:15:00.000Z",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"author": "Jane Smith",
"original": {
"publisher": {
"platform": "web",
"name": "Detroit Free Press",
"domain": "freep.com"
},
"url": "https://www.freep.com/story/news/local/michigan/macomb/2026/10/19/sterling-heights-library-expansion/1234567890/",
"publishedAt": "2026-10-19T11:30:00.000Z"
},
"about": {
"places": [
{
"name": "Sterling Heights",
"level": "place",
"displayText": "Sterling Heights, MI",
"geo": {
"lat": 42.57,
"lng": -83.025,
"bbox": [-83.08, 42.52, -82.97, 42.62]
},
"city": "Sterling Heights",
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
},
{
"name": "Macomb County",
"level": "district",
"displayText": "Macomb County, MI",
"geo": {
"lat": 42.65,
"lng": -82.9,
"bbox": [-83.1, 42.5, -82.7, 42.8]
},
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
}
]
},
"media": {
"cover": {
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
},
"images": [
{
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
}
]
}
}
],
"total": 1,
"pagination": {
"limit": 50,
"offset": 0,
"hasMore": false
}
}
}

Pagination object:

FieldTypeDescription
limitintegerItems per page (as requested)
offsetintegerCurrent offset (as requested)
hasMorebooleanWhether more items are available. If true, increment offset by limit to get the next page.

total is the number of items that match your filters across all pages.

An item that leaves the feed (for example, it no longer matches the feed's filters) disappears from the list. The list does not say so. Only the news.item.removed webhook does: see News feed changes.

Subscribers to the feed can read its items too.

Errors​

  • 400: INVALID_QUERY: a parameter out of range or malformed (limit outside 1-100, a date-only or zone-less time filter, hasCover other than true, an unknown orderBy), or include, since or until. The message names the parameter, and for since and until the one to use instead:

    {
    "success": false,
    "error": {
    "code": "INVALID_QUERY",
    "message": "Invalid query parameter 'since': `since` is not supported on news feed items; use `addedSince`"
    }
    }
  • 400: INVALID_PARAMS: feedId is not a UUID.

  • 401: Missing or invalid API key.

  • 404: Feed not found, or your organization neither owns it nor subscribes to it.

The item​

Optional fields are omitted when unknown, never sent as null. The exception is media.cover, which is null when there is no image or it cannot be served right now. Arrays are always present.

{
"id": "3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10",
"newsPageId": "9b2e7c44-1d3a-4f65-8a7b-0c5d2e9f1b33",
"addedToFeedAt": "2026-10-20T14:03:00.000Z",
"updatedAt": "2026-10-20T14:03:00.000Z",
"title": "Sterling Heights approves library expansion",
"description": "The city council voted 6-1 on Monday to fund a 12,000-square-foot addition to the Sterling Heights Public Library.",
"url": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publishedAt": "2026-10-20T09:15:00.000Z",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"author": "Jane Smith",
"original": {
"publisher": {
"platform": "web",
"name": "Detroit Free Press",
"domain": "freep.com"
},
"url": "https://www.freep.com/story/news/local/michigan/macomb/2026/10/19/sterling-heights-library-expansion/1234567890/",
"publishedAt": "2026-10-19T11:30:00.000Z"
},
"about": {
"places": [
{
"name": "Sterling Heights",
"level": "place",
"displayText": "Sterling Heights, MI",
"geo": {
"lat": 42.57,
"lng": -83.025,
"bbox": [-83.08, 42.52, -82.97, 42.62]
},
"city": "Sterling Heights",
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
},
{
"name": "Macomb County",
"level": "district",
"displayText": "Macomb County, MI",
"geo": {
"lat": 42.65,
"lng": -82.9,
"bbox": [-83.1, 42.5, -82.7, 42.8]
},
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
}
]
},
"media": {
"cover": {
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
},
"images": [
{
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
}
]
}
}
FieldTypeRequiredDescription
idstringyesFeed item id. Use it for the single-item endpoint, the feedback endpoint and de-duplication.
newsPageIdstringyesThe article or post. The same value in every feed that carries it.
addedToFeedAtstring (ISO 8601)yesWhen the item entered this feed.
updatedAtstring (ISO 8601)yesWhen the item last changed in a way that is announced by a webhook. See updatedAt.
titlestringnoHeadline. Social posts usually have none.
descriptionstringnoThe article description, or the post text.
urlstringnoThe article or the post. An http(s) URL.
publishedAtstring (ISO 8601)noWhen it was published. Omitted when unknown, or when the stated time has no timezone offset.
publisherobjectyesWho published the item. See Whose content it is.
authorstringnoThe byline as the article states it. "Staff" and similar are kept. Websites only.
originalobjectnoWho first published the item. Only for a syndicated article or a retweet.
aboutobjectyes{ places[] }: the places the item is about. places is empty when there are none.
mediaobjectyes{ cover, images[] }. See Images.

Strip-null semantics: check for key presence ('title' in item), not for null.

Whose content it is​

publisher is the source the feed crawls. It has no role.

FieldTypeDescription
platformstringweb, instagram, facebook, tiktok or twitter.
namestringFor a website: the site name from the page (og:site_name, unless it is a URL or just the site's domain), else the publisher name extracted from the page. For a social account: its display name. Omitted when unknown.
domainstringWebsites only: the site's domain without www..
handlestringSocial accounts only: the account's username.

author is the byline as written. It is not matched to a person.

original is present only when the item republishes someone else's content:

  • A syndicated article: original.publisher is the publisher the page names (name, and domain when known, with platform: "web"). original.url and original.publishedAt are present when the page states them.
  • A retweet: original.publisher is { handle, platform: "twitter" } for the account that was retweeted.
Where it is about​

about.places lists the places the item is about, most central first. There is no score. Each place is:

FieldTypeDescription
namestringThe place name.
levelstringThe narrowest level the place names: country, region, district, place, locality or neighborhood.
displayTextstringThe name, plus its region when the place is below region level (a US state as its two-letter code), plus its country when the place is below country level and not in the United States.
citystringThe city the place is in. Omitted when the place has none.
districtstringThe district or county. Omitted when the place has none.
regionstringThe state or region. Omitted when the place has none.
countrystringThe country. Omitted when the place has none.
geoobject{ lat, lng, bbox }. bbox is the place's bounding box [west, south, east, north]. lat and lng are the centre of that box.

Examples of displayText: Sterling Heights, MI, Macomb County, MI, Michigan, Tocumwal, New South Wales, Australia, Australia.

Names come from the geocoder's place hierarchy. A place the geocoder has not described yet is left out. When we fill in a place's hierarchy, it appears on the item at once, and the next time we process the item a news.item.updated announces it as about, even when nothing else changed.

Images​
FieldTypeDescription
coverobject|nullThe main image, or null when there is none or it cannot be served right now.
imagesobject[]Every image the item shows, cover first, in rank order. At most 6. Empty when none.

A website article has at most one image (its main image). A social post has up to 6: the slides of a carousel, or the cover of a reel, a TikTok or a video post. A tweet has none.

cover can be null while images has entries: the main image cannot be served right now (its link could not be made). Show no item image then; do not promote another image to cover. Read the cover null-safely (media.cover?.url).

Each image has:

  • url and expiresAt: a copy of the image that we host. The link is signed and temporary. Do not store the URL. If you want to keep the image, copy the file before expiresAt. Fetch the item again for a current link.
  • usage: always display_with_attribution. Show the image only with its source beside it, including credit when present.
  • kind: article_image (a website's main image), social_slide (a social image or one slide of a carousel) or video_thumbnail (the cover of a reel, a TikTok or a video post).
  • width, height: pixels. Omitted when unknown.
  • source: where it came from. imageUrl is the original address, for provenance only: do not display it. pageUrl is the page the image came from. publisher is the item's publisher. credit is the photo credit as the article states it. pageUrl, publisher and credit are omitted when unknown.

An image is served only while it is inside the feed's image window: 30 days after the item entered the feed by default. A feed can change this with config.media.retentionDaysAfterAdded (1 to 365). When the window passes, the item shows no images (media is { "cover": null, "images": [] }) and no webhook is sent. Images smaller than 200 px on the short edge are not used. Every image is checked by our moderation first, and an image that fails is not served.

updatedAt​

updatedAt moves only when we owe your webhooks an added or updated for the item. A re-signed image link, or an image leaving the window, does not move it. To poll for changes, ask for updatedSince with the time of your last poll:

curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items?updatedSince=2026-10-27T00:00:00Z" \
-H "Authorization: Bearer YOUR_API_KEY"

Poll from a few seconds before your previous poll and de-duplicate by id and updatedAt. Polling does not report items that left the feed: use the news.item.removed webhook for that.

Examples​

# Newest added first (default)
curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"

# Items with an image, newest published first
curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items?hasCover=true&orderBy=published" \
-H "Authorization: Bearer YOUR_API_KEY"

# Items published in a date range
curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items?publishedSince=2026-10-01T00:00:00Z&publishedUntil=2026-10-08T00:00:00Z" \
-H "Authorization: Bearer YOUR_API_KEY"

Get a Single Feed Item​

Returns one item from a news feed by its itemId, in the same shape as each element of Get Feed Items. Use it when you have an item id (from the list, a webhook or a saved reference) and want the latest version of that item.

GET /feeds/news/{feedId}/items/{itemId}

Request​

Path parameters:

NameTypeDescription
feedIdstring (UUID)Feed identifier
itemIdstring (UUID)Feed item identifier (from the id field)

There are no query parameters. include is refused with 400 and code INVALID_QUERY.

Headers:

Authorization: Bearer YOUR_API_KEY

Response​

Status: 200 OK

The body wraps one item. The fields are the ones in The item.

{
"success": true,
"data": {
"id": "3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10",
"newsPageId": "9b2e7c44-1d3a-4f65-8a7b-0c5d2e9f1b33",
"addedToFeedAt": "2026-10-20T14:03:00.000Z",
"updatedAt": "2026-10-20T14:03:00.000Z",
"title": "Sterling Heights approves library expansion",
"description": "The city council voted 6-1 on Monday to fund a 12,000-square-foot addition to the Sterling Heights Public Library.",
"url": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publishedAt": "2026-10-20T09:15:00.000Z",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"author": "Jane Smith",
"original": {
"publisher": {
"platform": "web",
"name": "Detroit Free Press",
"domain": "freep.com"
},
"url": "https://www.freep.com/story/news/local/michigan/macomb/2026/10/19/sterling-heights-library-expansion/1234567890/",
"publishedAt": "2026-10-19T11:30:00.000Z"
},
"about": {
"places": [
{
"name": "Sterling Heights",
"level": "place",
"displayText": "Sterling Heights, MI",
"geo": {
"lat": 42.57,
"lng": -83.025,
"bbox": [-83.08, 42.52, -82.97, 42.62]
},
"city": "Sterling Heights",
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
},
{
"name": "Macomb County",
"level": "district",
"displayText": "Macomb County, MI",
"geo": {
"lat": 42.65,
"lng": -82.9,
"bbox": [-83.1, 42.5, -82.7, 42.8]
},
"district": "Macomb County",
"region": "Michigan",
"country": "United States"
}
]
},
"media": {
"cover": {
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
},
"images": [
{
"url": "https://example.r2.cloudflarestorage.com/media/2026-10/library-expansion.jpg?X-Amz-Signature=...",
"expiresAt": "2026-10-28T12:00:00.000Z",
"usage": "display_with_attribution",
"kind": "article_image",
"source": {
"imageUrl": "https://www.macombdaily.com/wp-content/uploads/2026/10/library-expansion.jpg",
"pageUrl": "https://www.macombdaily.com/2026/10/19/sterling-heights-approves-library-expansion/",
"publisher": {
"platform": "web",
"name": "Macomb Daily",
"domain": "macombdaily.com"
},
"credit": "Jane Smith / The Macomb Daily"
},
"width": 1200,
"height": 800
}
]
}
}
}

Example​

curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items/3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10" \
-H "Authorization: Bearer YOUR_API_KEY"

Errors​

  • 400: INVALID_QUERY when include was sent; INVALID_PARAMS when feedId or itemId is not a UUID.
  • 401: Missing or invalid API key.
  • 404: Feed or item not found, the item belongs to a different feed, or your organization neither owns the feed nor subscribes to it. An item the list does not show (its article is behind a paywall, or its content could not be read) is a 404 too.

Submit Item Feedback​

Submits feedback for a specific feed item. Use this to indicate whether an article was relevant or useful.

POST /feeds/news/{feedId}/items/{itemId}/feedback

Request​

Path parameters:

NameTypeDescription
feedIdstringFeed identifier
itemIdstringFeed item identifier (from the id field)

Headers:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Body:

{
"rating": "positive",
"comment": "This article was very relevant to my interests."
}

Parameters:

NameTypeRequiredDescription
ratingstringYesFeedback rating: positive, negative, or neutral
commentstringNoOptional comment explaining the feedback. Maximum 5000 characters.

Response​

Status: 200 OK

{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440099"
}
}

Response object:

FieldTypeDescription
idstring (UUID)Unique feedback identifier

Examples​

Positive feedback:

curl -X POST https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items/3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10/feedback \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rating": "positive",
"comment": "Great article, very relevant!"
}'

Negative feedback without comment:

curl -X POST https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items/3f6c1d52-7a0e-4b8e-9d1c-5a2b8e4f6a10/feedback \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rating": "negative"
}'

Errors​

  • 400: INVALID_BODY for an invalid rating or a comment over 5000 characters; INVALID_PARAMS when feedId or itemId is not a UUID
  • 401: Missing or invalid API key
  • 404: The item is not in this feed, or your organization neither owns the feed nor subscribes to it

Notes​

  • Feedback is additive: you can submit multiple feedback entries for the same item
  • You can only submit feedback for items in feeds you own or are subscribed to

Sources​

RouteDoes
GET /feeds/news/{id}/sourcesEvery source on the feed, newest first
POST /feeds/news/{id}/sourcesAdd a website: { "type": "site", "url": … }
PATCH /feeds/news/{id}/sources/{sourceId}Enable or disable a source: { "enabled": … }
DELETE /feeds/news/{id}/sources/{sourceId}Remove a source from this feed

The same routes and source shape serve event feeds. Only websites can be added through the API (422 SOURCE_TYPE_NOT_ALLOWED for another type); sitemaps, index pages and social accounts are added by our team on request. You can list, enable, disable and remove every source. Adding a website starts a 14-day backfill of it; on an enabled feed, the articles the site already brought in during the past 14 days are added too (a disabled feed receives them when you enable it), as they are when you enable a source of an enabled feed that has not brought them in yet. Subscribers to the feed can list its sources but not change them (403 PERMISSION_DENIED). Adding sites to your event and news feeds shares one limit of 30 per minute per organization: over it, 429 RATE_LIMITED with a Retry-After header. See Sources for the rules, the source shape and every error.

curl -X POST https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/sources \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "site",
"url": "https://www.macombdaily.com/news/local/"
}'
{
"success": true,
"data": {
"id": "9b2f6c1e-4d3a-4e8b-9c7d-2a1b0f3e5d64",
"type": "site",
"identity": {
"domain": "www.macombdaily.com"
},
"enabled": true,
"addedAt": "2026-10-28T14:05:00.000Z"
}
}

Filters​

RouteDoes
GET /feeds/news/{id}/filtersThe feed's own and opt-in filters
GET /feeds/news/{id}/filters/{filterId}One filter
POST /feeds/news/{id}/filtersAdd your own filter, or one of our opt-in filters
PATCH /feeds/news/{id}/filters/{filterId}Change one of your own filters
DELETE /feeds/news/{id}/filters/{filterId}Remove a filter
POST /feeds/news/{id}/filter-sets/{setKey}Add a set of opt-in filters: standard_news or creator_social

An article is in the feed when no exclude filter matches and at least one include filter does; the safety floor always applies and is not listed. Only the feed's owner can read or change its filters. Filters read processors (GET /processors), such as the news_content_type built-in. See Filters and Opt-in filters for the condition format, the catalog and every error.

{
"name": "Strong news stories",
"action": "include",
"condition": {
"processor": "news_content_type",
"output": "editorial_news.significance",
"op": ">=",
"value": 7
}
}

Error Responses​

All endpoints use the same error envelope: success: false and an error object with a code, a message and, on some refusals, details.

400 Bad Request​

Invalid request parameters or body. The code says which part of the request was refused:

  • INVALID_BODY: the JSON body does not match the endpoint's schema (a missing field, a wrong type, a value out of range, an unknown key in config or in a source or filter body), or it is not valid JSON.
  • INVALID_PARAMS: a path parameter, such as a feed or item id that is not a UUID.
  • INVALID_QUERY: a query parameter on the item endpoints: since, until, include, a date-only or zone-less time filter, hasCover=false, an unknown orderBy, a limit outside 1-100.
{
"success": false,
"error": {
"code": "INVALID_BODY",
"message": "Invalid request body: Name is required"
}
}

401 Unauthorized​

Missing or invalid API key.

{
"success": false,
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Authentication failed for strategy: api-key"
}
}

Common causes:

  • Missing Authorization header
  • Incorrect header format
  • Invalid API key

403 Forbidden​

Your organization subscribes to the feed but does not own it, and the request would change it (update, delete, its sources) or reads its filters.

{
"success": false,
"error": {
"code": "PERMISSION_DENIED",
"message": "Access denied to NewsFeed with id '7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57'"
}
}

404 Not Found​

The resource does not exist, or your organization neither owns the feed nor subscribes to it.

{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "NewsFeed with id '7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57' not found in database"
}
}

Common causes:

  • Incorrect ID
  • Resource was deleted
  • A feed of another organization

409 Conflict​

Resource already exists.

{
"success": false,
"error": {
"code": "ALREADY_EXISTS",
"message": "News feed source with site 'www.macombdaily.com' already exists"
}
}

Common causes:

  • Adding a website the feed already has, with or without www.
  • Adding an opt-in filter the feed already has a filter for: a 409 with its own code, FILTER_ALREADY_PRESENT, not ALREADY_EXISTS (see Opt-in filters)

422 Unprocessable Entity​

The request is well formed but a rule refuses it.

{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed for field 'enabled': the feed needs at least one include filter; add an include filter first"
}
}

Common causes:

  • Enabling a feed that has no include filter (VALIDATION_FAILED)
  • Adding a source of a type other than site (SOURCE_TYPE_NOT_ALLOWED) or a site whose host is private or does not resolve: see Sources
  • A filter or opt-in the feed cannot take (INVALID_CONDITION, MISSING_OPT_IN_PARAM, INVALID_OPT_IN_PARAM, LAST_INCLUDE_FILTER): see Filters and Opt-in filters

429 Too Many Requests​

Adding sites to your feeds is limited to 30 per minute per organization. The response has code RATE_LIMITED and a Retry-After header with the seconds to wait.

500 Internal Server Error​

Unexpected server error.

{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal server error occurred"
}
}

If this persists: Retry the request

503 Service Unavailable​

A service the request needs is briefly unavailable. The response has code SERVICE_UNAVAILABLE and a Retry-After header: retry after that many seconds.


Best Practices​

Paginate Large Result Sets​

Always use pagination for feeds with many articles:

async function getAllFeedItems(feedId) {
const allItems = [];
let offset = 0;
const limit = 100; // Max allowed
let hasMore = true;

while (hasMore) {
const response = await fetch(
`https://api.feeds.onhelix.ai/feeds/news/${feedId}/items?limit=${limit}&offset=${offset}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);

const result = await response.json();
allItems.push(...result.data.items);

hasMore = result.data.pagination.hasMore;
offset += limit;
}

return allItems;
}

Use Date Filters​

Filter by date range to reduce response size:

# Get today's articles
TODAY=$(date -u +%Y-%m-%dT00:00:00Z)
curl "https://api.feeds.onhelix.ai/feeds/news/7c1e5a90-3b2d-4f68-9a14-6d8e0b2c4f57/items?addedSince=$TODAY" \
-H "Authorization: Bearer YOUR_API_KEY"