A tour of RTPR Wire

Wire is the real-time PR newswire dashboard you log into to read, filter, and alert on press releases the second they hit the wire. This page walks through every section of the app, with a deep focus on filter rules and a separate technical section for connecting to the alerts WebSocket.

What you get

When you sign in, you land on /wire. The header shows your plan, a live connection indicator, and four tabs:

  • Live — the firehose plus your ticker spotlight and watchlist.
  • Rules — your saved filter rules and the articles they've matched.
  • Connect — your API key and the real-time alerts WebSocket details (Pro only).
  • Account — billing, plan, cancellation.
Free vs. Pro. Free users see every section but the firehose is delayed by 5 minutes and rules / alerts are read-only. Upgrading flips the same UI to real-time and unlocks creating rules and the alerts WebSocket.

The Live Wire

The center of the dashboard is the live firehose: every press release from Business Wire, PR Newswire, GlobeNewswire and AccessWire as it crosses the tape. New rows fade in at the top with a gentle pulse so your eye catches movement without the page feeling chaotic.

Each row shows the ticker, the headline, the wire source, and a latency pill telling you how fast we delivered it. Click any row to open the full article — body text, tickers mentioned, and a permalink you can share.

On the free plan the feed is delayed by 5 minutes. The pill on each row shows when you would have received it on Pro. The feed never goes empty: when you first connect, we replay the last 20 articles so you have something to read immediately.

Ticker Spotlight

The left panel on the Live tab is the spotlight. Type a ticker (e.g. AAPL) and the panel pivots to show every article RTPR has stored for that ticker over the last 365 days, newest first. Useful for checking the recent news context before placing a trade.

Articles in the spotlight are clickable and open the same full-article view as the firehose.

Watchlist

The bottom-right panel is your watchlist. Add tickers you care about and the panel filters down to only those tickers as new articles arrive on the firehose. Think of it as an attention spotlight for your own portfolio without losing the broader feed.

The watchlist lives in your browser, so it's instant and free. To get a server-side push when something matches, use Filter Rules.

Filter Rules

Rules are the heart of Wire. A rule is a saved condition that evaluates against every press release in real time. When an article matches, the row appears in your Rules tab feed and a real-time alert fires (in-app, by email, and on the alerts WebSocket if you're connected).

Rules are defined as a JSON tree of conditions. The Wire app ships with a Builder tab for the common shapes, a JSON tab for everything else, and a Test tab for trying a rule against a sample article before you save it.

The basic shape

Every rule is a boolean tree of conditions. The root is one of three combinators:

  • all — every child must match (AND).
  • any — at least one child must match (OR).
  • not — exactly one child, inverted.

Leaves are conditions that look at one field of the article (title, body, ticker, exchange, etc.) using one operator (contains, equals, in, proximity, numeric_match, etc.).

Example 1 — A single keyword in the title

The simplest possible rule: alert on every article with the word bankruptcy in the title.

Bankruptcy headlinesDSL
{
  "field": "title",
  "op": "keyword",
  "value": "bankruptcy"
}

`keyword` does case-insensitive word-boundary matching, so it won't match the word `bankruptcy` if it appears as part of a longer word.

Example 2 — Two keywords, either one is fine

Use any when more than one phrase should trigger the same alert.

FDA approvalDSL
{
  "any": [
    { "field": "title_or_body", "op": "keyword", "value": "FDA approval" },
    { "field": "title_or_body", "op": "keyword", "value": "FDA authorization" },
    { "field": "title_or_body", "op": "keyword", "value": "FDA clearance" }
  ]
}

`title_or_body` searches the headline and the article text in one pass.

Example 3 — Keyword AND a watchlist of tickers

Use all when several conditions must all be true.

Mega-cap upgradesDSL
{
  "all": [
    {
      "field": "tickers",
      "op": "in",
      "value": ["AAPL", "MSFT", "NVDA", "GOOGL", "AMZN", "META"]
    },
    { "field": "title", "op": "keyword", "value": "upgrade" }
  ]
}

Example 4 — Numeric thresholds in the body

numeric_match parses currency, percentages and counts directly out of the article text, including suffixes like "5B" for $5 billion. You only need to give it a min or max bound.

Buybacks larger than $5BDSL
{
  "all": [
    {
      "field": "title_or_body",
      "op": "keyword",
      "value": "share repurchase"
    },
    {
      "field": "title_or_body",
      "op": "numeric_match",
      "kind": "currency",
      "min": 5000000000
    }
  ]
}

Supported `kind` values: `currency`, `percentage`, `count`.

Example 5 — Words near each other (proximity)

Sometimes the words you care about have to appear close to each other to mean what you think they mean. proximity matches when every term in terms appears in the field within within tokens of each other.

Trial readouts hitting primary endpointDSL
{
  "all": [
    {
      "field": "title_or_body",
      "op": "in",
      "value": ["phase 2", "phase 3", "phase ii", "phase iii"]
    },
    {
      "field": "body",
      "op": "proximity",
      "terms": ["primary", "endpoint"],
      "within": 4
    }
  ]
}

Example 6 — Excluding noise with `not`

Use not to subtract: alert on contract wins for biotech tickers, but skip earnings noise.

Biotech contract wins (no earnings)DSL
{
  "all": [
    { "field": "exchange", "op": "in", "value": ["NASDAQ", "NYSE"] },
    {
      "field": "title_or_body",
      "op": "any",
      "value": ["contract award", "supply agreement", "DOD contract"]
    },
    {
      "not": {
        "field": "title",
        "op": "contains",
        "value": "earnings"
      }
    }
  ]
}

Example 7 — A complex multi-leg rule

Putting it all together — a small-cap FDA approval that triggers only during market hours, capped at 30 alerts per hour with a friendly display name and a throttle.

Small-biotech FDA, market hoursDSL
{
  "name": "Small-biotech FDA, market hours",
  "active": true,
  "definition": {
    "all": [
      {
        "any": [
          { "field": "title_or_body", "op": "keyword", "value": "FDA approval" },
          { "field": "title_or_body", "op": "keyword", "value": "FDA clearance" },
          {
            "field": "body",
            "op": "proximity",
            "terms": ["Phase", "3", "endpoint"],
            "within": 6
          }
        ]
      },
      { "field": "exchange", "op": "in", "value": ["NASDAQ", "NYSE"] },
      { "field": "tickers_length", "op": "lte", "value": 2 },
      { "field": "session", "op": "in", "value": ["pre_market", "regular"] }
    ]
  },
  "throttle": { "max_per_hour": 30 }
}

`session` recognises `pre_market`, `regular`, `after_hours`, `overnight`, `weekend`. `tickers_length` is the number of tickers mentioned — useful to avoid round-up press releases.

Quick reference

Available fields:

  • title, body, title_or_body — the article text
  • ticker, tickers, tickers_length — the symbols mentioned
  • exchange — NASDAQ, NYSE, OTC, etc.
  • author — the wire source
  • created — publish timestamp
  • session — market session at publish time

Common operators:

  • contains, keyword — substring vs. whole-word match
  • starts_with, ends_with, equals
  • in, not_in — membership in a list
  • gt, gte, lt, lte — numeric comparisons
  • proximity — terms near each other
  • numeric_match — currency / percentage / count thresholds
Test before you save. The Test tab in the rule editor lets you paste any sample article and run the rule against it instantly. The matched evidence is shown inline — useful for debugging why a complex rule did or didn't fire.

Real-time Alerts

When a rule matches, RTPR delivers the alert through three channels:

  1. In the dashboard — a row appears in the Rules tab matched-feed within milliseconds, even on tabs you're not looking at.
  2. In your inbox — optional email per rule (Pro).
  3. On your personal socket — a coalesced JSON frame on the alerts WebSocket, ready to be piped into your own trading system.

All three fire from the same backend evaluation, so they stay in sync. If three rules match a single article, the dashboard shows three rows but the WebSocket emits a single coalesced frame with all three rule names — your downstream code stays simple.

Alerts WebSocket Reference

The alerts WebSocket is for programmatic use — your trading bot, your own dashboard, a notification bridge. It's available on the Pro plan; the API key and connection snippet for your account live in the Connect tab.

Endpoint

wss://ws.rtpr.io/ws-alerts?apiKey=YOUR_API_KEY

Authentication

Pass your API key as a query string parameter. The server closes the connection with code 4001 if the key is invalid, or 4004 if your subscription is no longer active. Treat any close as a cue to re-authenticate (refresh the key from the Connect tab) and reconnect with backoff.

Payload

When one of your rules matches an article, the gateway emits a single coalesced frame for that customer + article pair, even if several rules fired. Programmatic clients route on rule_name.

{
  "type": "alert",
  "ticker": "AAPL",
  "rules": [
    { "rule_name": "Apple guidance" },
    { "rule_name": "Big tech FOMC" }
  ],
  "article_published_at": "2026-04-29T18:30:00Z",
  "article_url": "https://rtpr.io/a/lseg_n123"
}

The frame is intentionally minimal: it tells you which ticker, which rules, and where to read the article. Fetch the full body from the linked URL or the REST /articles endpoint when you need it. This keeps the socket fast and lets you re-fetch on your own cadence.

Heartbeat

The server sends a JSON {"type":"ping"} every 30 seconds. Reply with {"type":"pong"}. If the server doesn't see your pong within 90 seconds it will close the connection.

Minimal Python client

# pip install websockets
import asyncio, json, websockets

URL = "wss://ws.rtpr.io/ws-alerts?apiKey=YOUR_API_KEY"

async def run():
    async with websockets.connect(URL) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("type") == "alert":
                names = [r["rule_name"] for r in msg["rules"]]
                print(f"{msg['ticker']} matched: {', '.join(names)}")
                print(f"  -> {msg['article_url']}")
            elif msg.get("type") == "ping":
                await ws.send(json.dumps({"type": "pong"}))

asyncio.run(run())

Minimal Node.js client

// npm install ws
const WebSocket = require('ws');

const ws = new WebSocket(
  'wss://ws.rtpr.io/ws-alerts?apiKey=YOUR_API_KEY'
);

ws.on('message', (raw) => {
  const msg = JSON.parse(raw);
  if (msg.type === 'alert') {
    const names = msg.rules.map((r) => r.rule_name).join(', ');
    console.log(`${msg.ticker} matched: ${names}`);
    console.log(`  -> ${msg.article_url}`);
  } else if (msg.type === 'ping') {
    ws.send(JSON.stringify({ type: 'pong' }));
  }
});

ws.on('close', (code) => {
  console.log('Closed', code, '— reconnect with backoff');
});
Backoff. If the connection drops, reconnect with exponential backoff starting at ~2 seconds, capped at one minute. If you see a string of failures (auth issues, gateway down), switch to a slow 5-minute poll until the next success — this keeps you off the rate limiter.

Plans & Limits

  • Free — Live firehose with a 5-minute delay. Read-only access to rules and alerts (so you can see what Pro gets). Ticker spotlight and watchlist work with full functionality.
  • Pro ($99/month) — Real-time firehose, create up to 25 filter rules, real-time alerts in-app, by email and on the dedicated WebSocket. Includes a 7-day free trial.

Hard caps per rule: 200 conditions, 50 KB serialized JSON, 20 keywords or tickers per in list. Throttle ranges from 1 to 10,000 alerts per hour. Articles are retained for 365 days.

Need help?