Skip to content

WebSocket Stream ​

Open one WebSocket connection with your filters to receive new matching articles. You can also send control messages over the same connection.

Articles arrive when they become available in APITube. They may have been published earlier. See Delivery Order and Freshness for details.

What is WebSocket Stream? ​

WebSocket lets a client and server send messages over one open connection. This stream sends articles as JSON messages with a type field. You can send pings to check the connection. SSE Stream provides a one-way HTTP alternative.

Use cases:

  • Live news feeds and trading/monitoring dashboards
  • Real-time monitoring for specific topics, entities, or sentiment
  • Breaking-news alerts
  • Applications that process incoming articles

Benefits:

  • Receive articles as soon as they become available
  • Every message is a JSON object. Its type field tells you whether it contains an article, connection update or error.
  • The server confirms that the connection and your filters have been accepted
  • Check the connection with client ping, server pong and heartbeat messages
  • All /everything filters supported — language, category, sentiment, entities, and more
  • Field selection with the fl parameter to keep responses smaller
  • Use takeover to replace an old connection after a restart without replacing your API key

Endpoint ​

http
GET wss://api.apitube.io/v1/news/ws

One connection = one set of filters. To subscribe to different filter sets, open separate connections.

Authentication ​

Pass your API key in one of two ways:

  • Header (recommended): X-API-Key: YOUR_API_KEY
  • Query parameter: ?api_key=YOUR_API_KEY

Without a valid API key the connection is closed with WebSocket close code 1008 (Unauthorized). A scope-restricted key must include the websocket scope.

Quick Start ​

wscat (command line) ​

bash
npx wscat -c "wss://api.apitube.io/v1/news/ws?language.code=en&api_key=YOUR_API_KEY"

You first receive connected and subscribed messages. New matching articles then arrive in article messages.

JavaScript (Browser) ​

javascript
const ws = new WebSocket(
    'wss://api.apitube.io/v1/news/ws?api_key=YOUR_API_KEY&language.code=en'
);

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);

    if (msg.type === 'error') {
        console.error(msg.code, msg.message);
        return;
    }

    if (msg.type === 'article') {
        const article = msg.data;
        console.log(article.title, article.source?.domain);
    }
};

ws.onclose = (event) => {
    console.log('Disconnected:', event.code, event.reason);
    // Reconnect with exponential backoff (see below)
};

Node.js ​

javascript
import WebSocket from 'ws';

const ws = new WebSocket('wss://api.apitube.io/v1/news/ws?language.code=en', {
    headers: { 'X-API-Key': 'YOUR_API_KEY' }
});

ws.on('message', (raw) => {
    const msg = JSON.parse(raw.toString());

    if (msg.type === 'error') {
        console.error(msg.code, msg.message);
        return;
    }

    if (msg.type === 'article') {
        console.log(msg.data.title);
    }
});

ws.on('close', (code, reason) => {
    console.log('Closed:', code, reason.toString());
});

Python ​

python
import json
import websocket

def on_message(ws, message):
    msg = json.loads(message)

    if msg.get("type") == "error":
        print(f"Error: {msg['code']} - {msg['message']}")
        return

    if msg.get("type") == "article":
        article = msg["data"]
        print(f"{article['title']} — {article.get('source', {}).get('domain')}")

ws = websocket.WebSocketApp(
    "wss://api.apitube.io/v1/news/ws?language.code=en",
    header={"X-API-Key": "YOUR_API_KEY"},
    on_message=on_message,
)

ws.run_forever()

Message Format ​

Every message is a JSON object. Read its type field to decide how to handle it.

Server → Client ​

After a successful connection, the server sends two confirmation messages:

json
{"type":"connected","session_id":"req-a1b2c3","timestamp":1718800000000}
{"type":"subscribed","filters":{"language.code":"en"},"timestamp":1718800000000}
  • connected — the connection is established and authenticated. session_id identifies this session.
  • subscribed — your subscription is accepted. filters echoes the query parameters you connected with (your API key is not echoed back).

New matching articles then arrive in article messages:

json
{"type":"article","data":{"id":123456,"title":"...","source":{"domain":"reuters.com"},"published_at":"2026-07-03T...","language":{"code":"en"}}}
  • data — the full article JSON, in the same format as the /everything endpoint (or only the fields you requested with fl).

Control messages:

MessageWhen
{"type":"heartbeat","timestamp":...}Keep-alive, sent every 30 seconds
{"type":"pong","timestamp":...}Reply to a client ping
{"type":"error","code":"...","message":"..."}An error occurred (balance, limits, key revoked); the connection then closes

Client → Server ​

Send a ping message to confirm that your application is still connected. The server replies with pong:

json
{ "action": "ping" }

Any message from the client confirms that it is still connected. The server replies only to ping; it ignores other message content. Filters cannot change while the connection is open.

Filtering ​

All filters available on the /everything endpoint work with WebSocket Stream, passed as query parameters at connection time:

By language:

wss://api.apitube.io/v1/news/ws?language.code=en&api_key=YOUR_API_KEY

By category:

wss://api.apitube.io/v1/news/ws?category.id=medtop:13000000&api_key=YOUR_API_KEY

By source:

wss://api.apitube.io/v1/news/ws?source.domain=theverge.com&api_key=YOUR_API_KEY

Multiple filters:

wss://api.apitube.io/v1/news/ws?language.code=en&category.id=medtop:13000000&sentiment.overall.polarity=positive&api_key=YOUR_API_KEY

In plain language:

wss://api.apitube.io/v1/news/ws?prompt=breaking technology news in English&api_key=YOUR_API_KEY

The API converts prompt into filters before accepting the subscription. The subscribed message shows those filters, so you can check how the prompt was understood:

json
{"type":"subscribed","filters":{"organization.name":"Tesla","language.code":"en"},"timestamp":1718800000000}

If a prompt cannot be used, the API sends an error message and closes the connection with code 4001:

json
{"type":"error","code":"ER0800","message":"prompt must be a string between 3 and 500 characters."}

A new prompt translation costs 2 points once per connection. If the translation is already cached, it is free. prompt requires Basic or above. On Free and Starter, the socket closes with ER0706.

For the full list of available filters, see Parameters.

Field Selection ​

Use fl to receive only the fields you need and keep responses smaller. The data object in each article message contains only those fields:

wss://api.apitube.io/v1/news/ws?language.code=en&fl=id,title,source.domain&api_key=YOUR_API_KEY

Delivery Order and Freshness ​

Articles arrive in the order they become available in APITube. This may differ from their published_at order.

  • published_at is the original publication time when available. Otherwise, it shows when the article became available in APITube.
  • Delivery time varies by article and publisher. A newly delivered article may have an older published_at value.

Use published_at.start to receive only recent articles. Older articles are excluded before delivery and billing. Use source.rank.opr.min (Open PageRank) to select higher-ranked sources. These filters do not change delivery order. See Parameters.

Keep-Alive ​

The stream uses two kinds of connection checks:

  • Server heartbeat — every 30 seconds the server sends a {"type":"heartbeat"} message and a protocol-level WebSocket ping. Standard clients (the browser WebSocket API and the Node ws library) reply to the protocol ping automatically.
  • Client ping — you may send {"action":"ping"} at any time and the server replies with {"type":"pong"}.

If a client stops replying to pings, the API closes its connection after one or two heartbeat intervals. This frees the session even if no matching articles are arriving.

Reconnection ​

A new WebSocket connection starts with the newest matching articles. It does not replay articles missed while disconnected and has no Last-Event-ID equivalent. Use SSE Stream if you need automatic resume. For WebSocket, reconnect with increasing delays between attempts:

javascript
let reconnectDelay = 1000;

function connect() {
    const ws = new WebSocket('wss://api.apitube.io/v1/news/ws?language.code=en&api_key=YOUR_API_KEY');

    ws.onopen = () => {
        reconnectDelay = 1000; // reset on success
    };

    ws.onclose = () => {
        setTimeout(connect, reconnectDelay);
        reconnectDelay = Math.min(reconnectDelay * 2, 30000);
    };

    ws.onmessage = (event) => {
        const msg = JSON.parse(event.data);
        if (msg.type === 'article') console.log(msg.data.title);
    };
}

connect();

To recover articles missed during a disconnection, request that time range from /everything, then reconnect to the stream.

Parameters ​

ParameterDescription
api_keyAPI key (alternative to X-API-Key header)
takeovertrue to evict your oldest connection instead of being rejected at the connection limit
flField selection (e.g., id,title,source.domain)
promptDescribe filters in plain language (3–500 characters). The subscribed message shows the selected filters. Requires Basic or above.
All /everything filterslanguage.code, category.id, source.domain, sentiment.overall.polarity, title, organization.name, and more

Technical Details ​

PropertyValue
ProtocolWebSocket (wss://), sends messages in both directions
Poll intervalEvery 10 seconds
Heartbeat intervalEvery 30 seconds
Articles per poll cycleUp to 50
Max message size128 KB
Max connection lifetime6 hours (then reconnect)
Billing1 credit per delivered article
ReconnectionManual (exponential backoff)

Concurrent Connections ​

Your plan limits how many WebSocket connections you can keep open at the same time:

PlanMax concurrent WebSocket connections
Free1
Starter2
Basic2
Professional10
Corporate50

On Free, you can open 1 WebSocket connection with 1,000 deliveries per day. Article text is a preview; links, images, descriptions and other fields are full. Articles arrive with a 12-hour delay. Paid plans include full text and more connections: Starter 2, Basic 2, Professional 10 and Corporate 50.

Opening more connections than your plan allows sends an error message with code ER0403 and closes the connection with code 4003:

json
{"type":"error","code":"ER0403","message":"Maximum WebSocket connections (10) reached for your plan."}

Takeover ​

Connect with ?takeover=true to replace your oldest connection when all slots are in use. This frees a slot after a restart without replacing your API key. The old client receives an error message with ER0365, and its connection closes with code 4007:

wss://api.apitube.io/v1/news/ws?language.code=en&takeover=true&api_key=YOUR_API_KEY

Use takeover if your application stopped without closing its old connection. Replacing the API key also closes all sessions opened with that key.

Billing ​

Each article delivered through the stream costs 1 credit. Credits are deducted in real time as articles arrive — never for the connection itself or for heartbeats.

How it works:

  1. When you connect, the server checks your balance. With no WebSocket credits and no paid balance, it sends an error message with code ER0404 and closes the connection with code 4002.
  2. While the stream is active, 1 credit is deducted per article sent to you — only after the article is confirmed delivered to an open socket.
  3. If your balance runs out mid-stream, the server sends an error message with code ER0402 ("WebSocket points exhausted") and closes the connection.

Credit source priority:

  • Plan WebSocket credits (ws_points) are used first.
  • If those are exhausted and you have a positive paid balance, the paid balance is used instead.
Each connection is billed independently

Each connection is billed separately. If an article matches several of your streams, you pay once per delivery. Use one stream where possible, or choose filters that do not overlap.

Error Handling ​

The API sends errors in an error message, then closes the connection:

json
{"type":"error","code":"ER0402","message":"WebSocket points exhausted."}
Error codeMeaningClose code
ER0401Invalid filters4001
ER0402WebSocket credits exhausted mid-stream4002
ER0403Maximum WebSocket connections reached4003
ER0404No WebSocket credits at connection time4002
ER0361API key revoked4004
ER0362Connection lifetime (6h) exceeded — reconnect4005
ER0365Connection replaced by a newer one (takeover)4007
ER0177Monthly pay-as-you-go spending limit reached4006

WebSocket close codes at a glance:

Close codeReason
1000Normal close
1008Unauthorized (invalid or missing API key)
4001Invalid filters
4002Balance exhausted / no credits
4003Connection limit reached
4004API key revoked
4005Lifetime exceeded
4006Pay-as-you-go spending limit reached
4007Replaced by a new connection using takeover

SSE or WebSocket? ​

Both deliver the same articles in real time with the same filters and per-article billing. Choose by connection style:

SSE StreamWebSocket Stream
ProtocolHTTP (one-way)WebSocket (two-way)
Message formatevent: article + data:{"type":"article","data":{...}}
ReconnectionAutomatic via Last-Event-IDManual (exponential backoff)
Slot recoverySession list/terminate endpointstakeover option
Best forSimple one-way reading, browser EventSourceApps that send and receive messages, with ping/pong checks

Choose SSE for a one-way feed with automatic resume. Choose WebSocket to receive JSON messages and send control messages on the same connection. To receive articles at your own URL, use Webhooks.

Next Steps ​

Support ​

If you encounter issues:

  1. Check the Technical FAQ
  2. Verify your API key on apitube.io
  3. Contact support through the APITube website