Skip to main content
The WebSocket server provides a real-time streaming interface for Injective chain events. It wraps the gRPC Chain Stream server, allowing you to subscribe to blockchain updates using standard WebSocket connections instead of gRPC streams.

Prerequisites

  • A running Injective node with the Chain Stream gRPC server enabled
  • The [injective-websocket] section configured in your node’s app.toml

Configuration

Configure the WebSocket server in your node’s app.toml under the [injective-websocket] section.
OptionTypeDefaultDescription
addressstring"" (disabled)Address and port to bind the WebSocket server (e.g., "0.0.0.0:9998"). Leave empty to disable.
max-open-connectionsint0 (unlimited)Maximum simultaneous WebSocket connections. Set a reasonable limit in production.
read-timeoutduration10sMaximum time to wait for reading a complete client request.
write-timeoutduration10sMaximum time to wait for writing a response to the client.
max-body-bytesint641000000 (1MB)Maximum HTTP request body size in bytes.
max-header-bytesint1048576 (1MB)Maximum HTTP header size in bytes.
max-request-batch-sizeint10Maximum RPC calls allowed in a single batch request.

Example configuration

app.toml
###############################################################################
###                  Injective Websocket Configuration                      ###
###############################################################################

[injective-websocket]

# Address defines the websocket server address to bind to.
# Leave empty to disable the websocket server.
address = "0.0.0.0:9998"

# MaxOpenConnections sets the maximum number of simultaneous connections.
# Set to 0 for unlimited (not recommended for production).
max-open-connections = 100

# ReadTimeout defines the HTTP read timeout.
read-timeout = "10s"

# WriteTimeout defines the HTTP write timeout.
write-timeout = "10s"

# MaxBodyBytes defines the maximum allowed HTTP body size (in bytes).
max-body-bytes = 1000000

# MaxHeaderBytes defines the maximum allowed HTTP header size (in bytes).
max-header-bytes = 1048576

# MaxRequestBatchSize defines the maximum number of RPC calls per batch request.
max-request-batch-size = 10
The WebSocket server requires the Chain Stream server to be enabled. Ensure the following is also configured in your app.toml:
chainstream-server = "0.0.0.0:9999"
chainstream-server-buffer-capacity = 100
chainstream-publisher-buffer-capacity = 100

Connecting to the server

Connect to the WebSocket endpoint at:
ws://<node-address>:<port>/injstream-ws
example.js
const ws = new WebSocket('ws://localhost:9998/injstream-ws');

ws.onopen = () => {
  console.log('Connected to Injective WebSocket');
};

ws.onmessage = (event) => {
  const response = JSON.parse(event.data);
  console.log('Received:', response);
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = () => {
  console.log('Disconnected from WebSocket');
};

Subscribing to events

Send a JSON-RPC 2.0 request with the subscribe method. Each request must include:
  • jsonrpc — Always "2.0"
  • id — A positive integer used to identify responses for this subscription
  • method"subscribe"
  • params.req.subscription_id — A client-provided unique identifier for this subscription
  • params.req.filter — An object containing your subscription filters
const subscribeRequest = {
  jsonrpc: '2.0',
  id: 1,
  method: 'subscribe',
  params: {
    req: {
      subscription_id: 'my-oracle-prices',
      filter: {
        oracle_price_filter: {
          symbol: ['*']  // Use '*' to subscribe to all symbols
        }
      }
    }
  }
};

ws.send(JSON.stringify(subscribeRequest));
A successful subscription returns:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "success"
}
After subscribing, you will receive stream updates with the same id.

Unsubscribing from events

Send a request with the unsubscribe method and the subscription_id you used when subscribing:
const unsubscribeRequest = {
  jsonrpc: '2.0',
  id: 100,
  method: 'unsubscribe',
  params: {
    req: {
      subscription_id: 'my-oracle-prices'
    }
  }
};

ws.send(JSON.stringify(unsubscribeRequest));
A successful unsubscribe returns:
{
  "jsonrpc": "2.0",
  "id": 100,
  "result": "success"
}
The subscription_id must match exactly the ID you provided when creating the subscription.

Available filters

You can subscribe to any combination of the following event types. At least one filter must be specified. Use "*" as a wildcard to match all values for a parameter.
FilterDescriptionParameters
bank_balances_filterBank balance changesaccounts
subaccount_deposits_filterSubaccount deposit changessubaccount_ids
spot_trades_filterSpot market tradesmarket_ids, subaccount_ids
derivative_trades_filterDerivative market tradesmarket_ids, subaccount_ids
spot_orders_filterSpot order updatesmarket_ids, subaccount_ids
derivative_orders_filterDerivative order updatesmarket_ids, subaccount_ids
spot_orderbooks_filterSpot orderbook updatesmarket_ids
derivative_orderbooks_filterDerivative orderbook updatesmarket_ids
positions_filterPosition updatessubaccount_ids, market_ids
oracle_price_filterOracle price updatessymbol
order_failures_filterOrder failure notificationsaccounts
conditional_order_trigger_failures_filterConditional order trigger failuressubaccount_ids, market_ids

Response format

Stream responses follow the JSON-RPC 2.0 format. Each response contains updates for a single block. Only fields matching your subscription filters contain data.
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block_height": "12345678",
    "block_time": "1702123456789",
    "bank_balances": [],
    "subaccount_deposits": [],
    "spot_trades": [],
    "derivative_trades": [],
    "spot_orders": [],
    "derivative_orders": [],
    "spot_orderbook_updates": [],
    "derivative_orderbook_updates": [],
    "positions": [],
    "oracle_prices": [],
    "gas_price": "160000000",
    "order_failures": [],
    "conditional_order_trigger_failures": []
  }
}

Security considerations

The WebSocket server has certain security limitations to be aware of when deploying to production.
The server accepts connections from any origin. Deploy behind a reverse proxy that validates the Origin header to mitigate Cross-Site WebSocket Hijacking.
location /injstream-ws {
    if ($http_origin !~* "^https://(app\.injective\.network|your-domain\.com)$") {
        return 403;
    }

    proxy_pass http://127.0.0.1:9998;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}
The server only supports unencrypted ws:// connections. Use a TLS-terminating reverse proxy to provide wss:// connections.
server {
    listen 443 ssl;
    server_name your-domain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location /injstream-ws {
        proxy_pass http://127.0.0.1:9998;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
The server does not implement authentication. Use a reverse proxy with authentication middleware, API key validation, or network-level access controls (firewalls, VPNs).
While max-open-connections limits total connections, there is no per-client rate limiting. Implement rate limiting at the proxy level:
limit_conn_zone $binary_remote_addr zone=ws_conn:10m;
limit_conn ws_conn 10;  # Max 10 connections per IP

Operational notes

  • When a connection closes, all associated subscriptions are automatically cancelled
  • The server sends ping frames periodically to detect stale connections
  • The subscription_id must be unique within your connection — different connections can reuse the same IDs
  • Implement reconnection logic with exponential backoff on the client side
  • Use specific filters rather than wildcards (*) when possible to reduce data volume
  • Always set max-open-connections to a reasonable limit in production