documentation · version 1.0 · rest · json

newsmetry api

market news in one api: a real-time feed with full text, a link to the original and publication time.

01overview

base url for every endpoint: https://api.newsmetry.xyz/api/v1

stories reach the api the moment they are fully collected. on the newsmetry website the same events open 15 minutes later.

openapi spec and interactive console: swagger ↗

02quickstart

  1. get a key: request access. it looks like nag_… and is shown only once — store it safely.
  2. send it in the header Authorization: Bearer <key>.
  3. request the feed — that is it.
curl
curl -H "Authorization: Bearer $NEWS_API_KEY" \
     "https://api.newsmetry.xyz/api/v1/news?limit=5"
python
import os, requests

API = "https://api.newsmetry.xyz/api/v1"
headers = {"Authorization": f"Bearer {os.environ['NEWS_API_KEY']}"}
r = requests.get(API + "/news?limit=5", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())
javascript
const API = "https://api.newsmetry.xyz/api/v1";
const res = await fetch(API + "/news?limit=5", {
  headers: { Authorization: `Bearer ${process.env.NEWS_API_KEY}` },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());

03keys and authorization

every request except /health needs a key. either header works:

Authorization: Bearer nag_…
X-API-Key: nag_…

send the key from your server only, never from a browser.

04limits and plans

every key is limited in three ways. all three show up in response headers; going over returns 429 with Retry-After.

rate limitrequests per second and per minute, sliding windows.
throttlinghow many requests per key may run at the same time.
quotafeed requests per day. resets at 00:00 utc.
planquota / dayper secondper minuteconcurrentitems per page
Freetrial: 5 requests a day515150
Proproduction: 1,000 requests a day1 00051204500

only news requests spend quota (/news, /news/{id}). /me and /health are free. a request that fails with a 5xx error is refunded.

05pagination and polling

the feed is paged by cursor, not page number: the response contains next_cursor — pass it as cursor to get the next batch. while has_more = true, there is more.

to fetch only new stories, read with order=asc and keep next_cursor — it is returned on the last page too. start from it next time and you get only stories that appeared since.

python — poll once a minute
import os, time, requests

API = "https://api.newsmetry.xyz/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['NEWS_API_KEY']}"
cursor = None  # keep it between runs — in a file or a database

while True:
    params = {"order": "asc", "limit": 50}
    if cursor:
        params["cursor"] = cursor
    r = session.get(f"{API}/news", params=params, timeout=30)
    if r.status_code == 429:  # rate limit or quota — wait as told
        time.sleep(int(r.headers.get("Retry-After", "60")))
        continue
    r.raise_for_status()
    page = r.json()
    for item in page["items"]:
        print(item["published_at"], item["title"])
    cursor = page["next_cursor"] or cursor
    if not page["has_more"]:
        time.sleep(60)

a story enters the feed and gets its id once it is complete, with full text. if a story has no text and never will (a short flash), it arrives with body_status = "empty".

06the news object

id stringstory id. grows with every new story — the cursor follows it.
title stringheadline.
summary string | nulllead or short description.
body string | nullfull text.
body_status stringfull — text present; empty — no text and there will be none (a short flash).
url string | nulllink to the original.
image_url string | nullimage.
lang string | nulllanguage: en, ru.
tags string[]sections.
published_at iso 8601publication time, utc.
collected_at iso 8601when the story appeared in newsmetry.
updated_at iso 8601last change to the record.
{
  "id": "48213",
  "title": "Fed holds rates steady, signals two cuts in 2027",
  "summary": "The Federal Reserve left its benchmark rate unchanged on Wednesday…",
  "body": "WASHINGTON — The Federal Reserve held interest rates steady…",
  "body_status": "full",
  "url": "https://www.reuters.com/markets/us/fed-holds-rates-2026-10-12/",
  "image_url": "https://www.reuters.com/resizer/…/fed.jpg",
  "lang": "en",
  "tags": [],
  "published_at": "2026-10-12T18:02:11Z",
  "collected_at": "2026-10-12T18:07:40Z",
  "updated_at": "2026-10-12T18:07:40Z"
}

every id is a string: javascript loses precision on numbers this long. times are iso 8601 in utc with the Z suffix.

07errors

every error is json of the same shape:

{
  "error": {
    "code": "quota_exceeded",
    "message": "daily quota exhausted (1000 requests), resets at 00:00 UTC"
  }
}
400 invalid_parameterinvalid parameter: message says which and what was expected, details lists the fields.
401 unauthorizedthe key is missing, unknown or was reissued.
403 forbiddenaccess was disabled by an administrator or the key expired.
404 not_foundno such story or path.
429 rate_limitedtoo fast: the per-second or per-minute limit was exceeded. wait Retry-After seconds.
429 throttledtoo many concurrent requests on one key. retry in a second.
429 quota_exceededdaily quota exhausted. it resets at 00:00 utc, in Retry-After seconds.
503 unavailabletemporarily unavailable. retry in Retry-After seconds.

08response headers

X-RateLimit-Limitrequests per minute allowed by the plan.
X-RateLimit-Remainingrequests left in the current minute.
X-RateLimit-Resetwhen the window frees up (unix time, seconds).
X-Quota-Limitdaily quota (only on requests that spend it).
X-Quota-Remainingquota left today.
X-Quota-Resetquota reset — the next utc midnight (unix time).
Retry-Afterseconds to wait before retrying — on 429 and 503 responses.

09news feed

GET/newsspends quota

stories, newest first.

limit integer · default 50how many stories to return: from 1 up to the plan ceiling.
cursor stringnext_cursor from the previous response — the next page.
order desc | asc · default descdesc — newest to oldest, asc — oldest to newest (for polling “what is new”).
lang stringlanguage: en, ru.
q stringheadline substring, case-insensitive (up to 200 characters).
from iso 8601published at or after, e.g. 2026-10-12T00:00:00Z.
to iso 8601published at or before.
curl
curl -H "Authorization: Bearer $NEWS_API_KEY" \
     "https://api.newsmetry.xyz/api/v1/news?limit=5"
example response · 200
{
  "items": [
    {
      "id": "48213",
      "title": "Fed holds rates steady, signals two cuts in 2027",
      "summary": "The Federal Reserve left its benchmark rate unchanged on Wednesday…",
      "body": "WASHINGTON — The Federal Reserve held interest rates steady…",
      "body_status": "full",
      "url": "https://www.reuters.com/markets/us/fed-holds-rates-2026-10-12/",
      "image_url": "https://www.reuters.com/resizer/…/fed.jpg",
      "lang": "en",
      "tags": [],
      "published_at": "2026-10-12T18:02:11Z",
      "collected_at": "2026-10-12T18:07:40Z",
      "updated_at": "2026-10-12T18:07:40Z"
    }
  ],
  "has_more": true,
  "next_cursor": "48213",
  "meta": {
    "quota_limit": 1000,
    "quota_remaining": 987
  }
}

10single story

GET/news/{id}spends quota

a story by id.

id string · requiredstory id from the feed.
curl
curl -H "Authorization: Bearer $NEWS_API_KEY" \
     "https://api.newsmetry.xyz/api/v1/news/48213"
example response · 200
{
  "id": "48213",
  "title": "Fed holds rates steady, signals two cuts in 2027",
  "summary": "The Federal Reserve left its benchmark rate unchanged on Wednesday…",
  "body": "WASHINGTON — The Federal Reserve held interest rates steady…",
  "body_status": "full",
  "url": "https://www.reuters.com/markets/us/fed-holds-rates-2026-10-12/",
  "image_url": "https://www.reuters.com/resizer/…/fed.jpg",
  "lang": "en",
  "tags": [],
  "published_at": "2026-10-12T18:02:11Z",
  "collected_at": "2026-10-12T18:07:40Z",
  "updated_at": "2026-10-12T18:07:40Z"
}

11my key and quota

GET/mefree

plan, limits and requests left today. free — handy for monitoring.

curl
curl -H "Authorization: Bearer $NEWS_API_KEY" \
     "https://api.newsmetry.xyz/api/v1/me"
example response · 200
{
  "id": "17",
  "name": "Trading bot",
  "key_prefix": "nag_q8Jx2LmA",
  "key_expires_at": null,
  "plan": {
    "code": "pro",
    "name": "Pro",
    "daily_quota": 1000,
    "rate_per_second": 5,
    "rate_per_minute": 120,
    "max_concurrency": 4,
    "max_page_size": 500,
    "unlimited": false
  },
  "quota": {
    "used": 13,
    "remaining": 987,
    "reset_at": "2026-10-13T00:00:00Z"
  }
}

12service health

GET/healthno key

whether the api is up and collection is running. no key needed.

curl
curl "https://api.newsmetry.xyz/api/v1/health"
example response · 200
{
  "status": "ok",
  "service": "newsmetry",
  "version": "1.0",
  "time": "2026-10-12T18:08:12Z",
  "db": "ok",
  "redis": "ok",
  "worker": "running"
}

early access

join newsmetry

leave your work email and we will open your trial as soon as accounts launch.