Skip to content
Live news Screener Coverage Pricing Docs REST API WebSocket API MCP server Company Use cases Blog Changelog Status Compare Contact
ALGRF$0.400.00(0.00%)Algo Grande Commissions Independent NI 43-101 Update to Capture First Year of Progress at Adelita[5:30:00 PM ET]ZEFIF$0.45+0.02(+3.56%)Zefiro Announces Closing of Non-Brokered Private Placement for Gross Proceeds of Approximately C$5.0 Million[5:29:00 PM ET]BLK$1,118.50-18.79(-1.65%)BlackRock® Canada Announces the Termination of iShares Convertible Bond Index ETF[5:23:34 PM ET]ITFS$1.43-0.02(-1.10%)Itafos Announces Release Date for Q3 2026 Results and Business Update Webcast[5:15:00 PM ET]FAGI$0.01-0.00(-13.33%)Full Alliance Group, Inc (OTC:FAGI) YAHBEE Highlights Community-Commerce Model for Agentic Commerce Era[5:15:00 PM ET]TGT$152.76-5.35(-3.38%)Vital Farms Introduces a Higher Standard for Free-Range Eggs Through Limited Pilot at Select Target Retail Stores[5:05:00 PM ET]NU$15.22+0.49(+3.33%)Notice to Market: Nu Holdings Ltd.[5:03:00 PM ET]POR$44.55-0.35(-0.78%)Portland General Electric Declares Dividend[5:00:00 PM ET]ANTX$3.55-0.09(-2.47%)AN2 Therapeutics, Inc. Reports Inducement Grants Under Listing Rule 5635(c)(4)[5:00:00 PM ET]EML$24.18-0.67(-2.70%)The Eastern Company Announces Timing of Third Quarter 2026 Earnings Release and Conference Call[4:55:00 PM ET]MYE$32.13-0.70(-2.13%)Myers Industries Announces Quarterly Dividend[4:45:00 PM ET]FCAP$63.35-0.59(-0.92%)First Capital, Inc. Reports Quarterly Earnings[4:45:00 PM ET]AIRDF$0.33-0.01(-3.96%)Rocket Doctor Grants Equity[4:42:20 PM ET]
Tutorial 8 min read

Build a real-time stock news alert bot in Python (WebSocket + Discord)

Step-by-step: stream live stock news over WebSocket and post filtered alerts to Discord in under 60 lines of Python.

In this tutorial you'll build a small Python service that watches the stock news stream for the tickers you care about and posts each new headline to a Discord channel. It takes about 15 minutes and fewer than 60 lines of code.

What you need: Python 3.10+, a MarketNewsAPI key on Pro (WebSocket, included in the 7-day trial), and a Discord webhook URL (Server settings → Integrations → Webhooks).

Why use a WebSocket instead of polling?

Polling a REST endpoint every few seconds wastes requests and still adds latency: on average, half your polling interval. A WebSocket keeps one connection open and the server pushes each article as soon as it's ingested and enriched. You also filter on the server, so your bot only receives the articles it will act on.

1. Install dependencies

pip install websockets httpx

2. Connect and subscribe

Connect to the stream with your key, then send a subscribe message. Channels are combined with OR: you get an article if it matches any of them, and you get it only once.

bot.py
import asyncio
import json
import os

import httpx
import websockets

API_KEY = os.environ["MARKETNEWSAPI_KEY"]
WEBHOOK = os.environ["DISCORD_WEBHOOK_URL"]
URL = f"wss://ws.marketnewsapi.com/v1/stream?api_key={API_KEY}"
CHANNELS = ["ticker:NVDA", "ticker:TSLA", "keyword:guidance"]

3. Post each article to Discord

Each article message carries the full article object, including an AI summary, so the alert is useful without opening the link.

async def post(client: httpx.AsyncClient, article: dict, matched: list[str]) -> None:
    symbols = article["tickers"]
    await client.post(WEBHOOK, json={
        "embeds": [{
            "title": article["title"][:256],
            "url": article["url"],
            "description": article.get("summary") or article.get("snippet") or "",
            "color": 0x22C55E,
            "footer": {"text": f"{article['source']['name']} · {', '.join(symbols)}"},
            "timestamp": article["published_at"],
        }]
    })

4. Run forever, reconnect automatically

websockets.connect used as an async iterator reconnects with exponential backoff, so a deploy or network blip doesn't kill the bot.

async def main() -> None:
    async with httpx.AsyncClient(timeout=10) as client:
        async for ws in websockets.connect(URL):
            try:
                await ws.send(json.dumps({"action": "subscribe", "channels": CHANNELS}))
                async for raw in ws:
                    msg = json.loads(raw)
                    if msg["type"] == "article":
                        await post(client, msg["data"], msg["matched"])
                    elif msg["type"] == "error":
                        print("server error:", msg)
            except websockets.ConnectionClosed as exc:
                print("disconnected:", exc.code, exc.reason)
                continue


if __name__ == "__main__":
    asyncio.run(main())

Try it for 7 days. Signup starts Pro. A card is required. Real-time REST, WebSocket, and MCP, with no request cap. After that it's $15/month, or Free if you stop.

Start 7-day free trial

Variation: only alert on press releases

For earnings and company announcements, subscribe to type:press_release and keep articles that mention a holding:

HOLDINGS = {"AAPL", "MSFT", "NVDA"}
CHANNELS = ["type:press_release"]

# inside the loop:
if msg["type"] == "article" and HOLDINGS & set(msg["data"]["tickers"]):
    await post(client, msg["data"], msg["matched"])

Don't miss articles during reconnects

Store the published_at of the last article you processed. After a reconnect, call GET /v1/news with the same tickers and from=<that timestamp> to backfill anything published while you were offline. Articles have stable ids, so de-duplicating is a set lookup.

Deploying it

Any always-on host works: a $5 VPS, Fly.io, Railway or a container next to your other services. Memory use is tiny. Pro includes 3 concurrent connections, so you can run production and staging at the same time.

Frequently asked questions

Can I use the REST API instead of the WebSocket for alerts?
Yes. Poll GET /v1/news with your tickers and from set to the last article you processed, every 10 to 30 seconds. You will add latency and extra calls. Pro is the same $15 a month either way, and the 7-day trial includes both. The socket is the simpler choice once you want the headline as it lands. Use REST to backfill after a reconnect.
How many tickers can I subscribe to on one connection?
Up to 200 channels per connection. A channel can be a ticker, a type, a keyword, a source, or a category. For a wider net, subscribe to category:stocks and filter in the bot, or open another connection. Pro includes 3 concurrent WebSocket connections. The 7-day trial includes the same limits. A card is required.
Does the Python alert bot work on the Free plan?
No. Free does not include the WebSocket. Free is 100 REST requests a day, headlines delayed 15 minutes, and the last year of history. The bot in this tutorial needs Pro, which is included for 7 days when you sign up. After that, Pro is $15 a month or $162 a year. Keep the API key in an environment variable, not in the script.

Real-time stock news for $15 a month.

Start a 7-day Pro trial. A card is required. REST, WebSocket, and MCP are included. If you don't subscribe, the account stays on Free.