Skip to content
Live news Screener Coverage Pricing Docs REST API WebSocket API MCP server Company Use cases Blog Changelog Status Compare Contact
AIRDF$0.33-0.01(-3.96%)Rocket Doctor Grants Equity[4:42:20 PM ET]OXSQ$1.31-0.02(-1.62%)Oxford Square Capital Corp. Schedules Third Quarter 2026 Earnings Release and Conference Call for October 30, 2026[4:37:45 PM ET]CSV$35.41-1.57(-4.25%)Carriage Services Announces 2026 Third Quarter Earnings Release and Conference Call Schedule[4:31:58 PM ET]GD$349.36-1.43(-0.41%)Amy Johnson Appointed President of GDIT General Dynamics (NYSE:GD) announced today that Amy Johnson has been appointed as president of GDIT, effective November 3. Johnson is a 27-year...[4:30:00 PM ET]ACRE$3.35+0.09(+2.76%)Ares Commercial Real Estate Corporation Schedules Earnings Release and Conference Call for the Third Quarter Ended September 30, 2026 Ares Commercial Real Estate Corporation (NYSE: ACRE) announced today that it will report earnings for the third quarter ended September 30, 2026 on...[4:30:00 PM ET]ESOA$9.26+0.12(+1.31%)ENERGY SERVICES OF AMERICA COMPLETES FAMCO ACQUISITION Energy Services of America Corporation (the "Company" or "Energy Services") (Nasdaq: ESOA) today announced that its subsidiary, FAMCO Acquisition,...[4:30:00 PM ET]AVTGF$0.240.00(0.00%)Avanti Gold Announces Results of Annual General and Special Meeting[4:30:00 PM ET]AWK$142.75-0.48(-0.34%)California American Water Submits Monterey Desalination Project Financing and Cost Updates to the California Public Utilities Commission Today, California American Water submitted an application to the California Public Utilities Commission (CPUC) updating the project cost estimates, a ...[4:21:00 PM ET]PBT$33.68+0.41(+1.23%)PERMIAN BASIN ROYALTY TRUST ANNOUNCES RECORD DATE AND MEETING DATE FOR SPECIAL MEETING TO CONSIDER SOFTVEST PROPOSALS Argent Trust Company, as trustee ("Trustee") of the Permian Basin Royalty Trust (NYSE: PBT) ("Permian" or the "Trust"), today announced that it has...[4:20:00 PM ET]MMT$4.15-0.05(-1.19%)Aberdeen Multi-Market Income Fund (MMT) Announces Upcoming 1-for-4 Reverse Stock Split The Board of Trustees of Aberdeen Multi-Market Income Fund (NYSE: MMT) has approved a 1-for-4 reverse stock split of the Fund's issued and...[4:17:00 PM ET]FICO$749.99-73.28(-8.90%)FICO Holds Mortgage Score Pricing at $10 for Lenders Enrolled in Preferred Program[4:15:00 PM ET]CIVB$28.29+0.14(+0.50%)Civista Bancshares, Inc. Appoints Jeremy Normington-Slay to Board of Directors Civista Bancshares, Inc. (NASDAQ: CIVB) ("Civista") today announced the appointment of Jeremy Normington-Slay, DPT, MBA, FACHE, to the Boards of...[4:15:00 PM ET]NKTR$37.22-1.43(-3.70%)Nektar Therapeutics Reports Inducement Grants Under Nasdaq Listing Rule 5635(c)(4) Nektar Therapeutics (NASDAQ: NKTR) today announced that, on October 20, 2026, the Organization and Compensation Committee of Nektar's Board of...[4:15:00 PM ET]YMAT$2.12+0.20(+10.42%)J-Star Holding Co., Ltd. Files Interim Financial Report on Form 6-K[4:15:00 PM ET]MFI$8.390.00(0.00%)mF International to Wind Down Legacy Financial Trading Business, Shift Focus Completely to Digital Asset Strategy mF International Limited (NASDAQ:MFI) ("mF International" or the "Company") today announced that its Board of Directors has approved the wind-down...[4:15:00 PM ET]DTCFF$0.28-0.02(-5.28%)Defence Therapeutics Announces Convertible Debenture Interest Payment[4:15:00 PM ET]
WebSocket

WebSocket best practices

View as markdown

A production consumer in five habits.

1. Reconnect with backoff

Connections drop: deploys (1001), network changes, laptop sleep. Reconnect automatically with exponential backoff and jitter (1s, 2s, 4s … capped at 30s), and re-send your subscriptions after every reconnect.

2. Detect dead connections

The server sends a heartbeat every 30 seconds. If you see no message for 45 seconds, assume the connection is dead, close it and reconnect.

3. Backfill gaps over REST

Keep the newest published_at you've processed. After reconnecting, call GET /news with the same filters and from= that timestamp, then de-duplicate by id.

4. Read fast, process elsewhere

Don't do slow work (HTTP calls, database writes, model inference) inside the read loop. Push messages onto an in-memory queue and process them in a worker. Slow readers get disconnected with 4030.

5. One connection per process

A single connection can carry 200 channels. Use the 3 connections on the Pro plan for redundancy or separate environments, not one per ticker.

Reference implementation

resilient consumer
import asyncio
import json
import os
import random
import time

import httpx
import websockets

KEY = os.environ["MARKETNEWSAPI_KEY"]
CHANNELS = ["ticker:AAPL", "ticker:NVDA", "type:press_release"]
last_seen = None
seen_ids = set()


async def handle(article):
    if article["id"] in seen_ids:
        return
    seen_ids.add(article["id"])
    print(article["published_at"], article["title"])


async def backfill(since):
    async with httpx.AsyncClient(timeout=10) as http:
        r = await http.get(
            "https://api.marketnewsapi.com/v1/news",
            params={"tickers": "AAPL,NVDA", "from": since, "sort": "published_asc", "limit": 100},
            headers={"Authorization": f"Bearer {KEY}"},
        )
        for a in r.json()["data"]:
            await handle(a)


async def run():
    global last_seen
    delay = 1
    while True:
        try:
            async with websockets.connect(f"wss://ws.marketnewsapi.com/v1/stream?api_key={KEY}") as ws:
                await ws.send(json.dumps({"action": "subscribe", "channels": CHANNELS}))
                if last_seen:
                    await backfill(last_seen)
                delay = 1
                while True:
                    msg = json.loads(await asyncio.wait_for(ws.recv(), timeout=45))
                    if msg["type"] == "article":
                        last_seen = msg["data"]["published_at"]
                        await handle(msg["data"])
        except websockets.ConnectionClosed as e:
            if e.code in (4001, 4003):
                raise
        except (asyncio.TimeoutError, OSError):
            pass
        await asyncio.sleep(delay + random.random())
        delay = min(delay * 2, 30)


asyncio.run(run())

Comma-separated tickers are OR'd, so one backfill call covers every symbol on the subscription.