Stream Real-Time Events

Listen to marketplace events in real time using @opensea/stream-js.

The OpenSea Stream SDK (@opensea/stream-js) delivers marketplace events over WebSocket in real time — no polling required. Subscribe to listings, sales, transfers, offers, and more by collection or globally.

Streaming events do not count toward your API rate limits.

Prerequisites

Step 1: Install

npm install @opensea/stream-js

For Node.js, also install WebSocket and storage dependencies:

npm install ws node-localstorage

Step 2: Connect

Browser

import { OpenSeaStreamClient } from "@opensea/stream-js";

const client = new OpenSeaStreamClient({
  token: "YOUR_OPENSEA_API_KEY",
});

Node.js

import { OpenSeaStreamClient } from "@opensea/stream-js";
import { WebSocket } from "ws";
import { LocalStorage } from "node-localstorage";

const client = new OpenSeaStreamClient({
  token: "YOUR_OPENSEA_API_KEY",
  connectOptions: {
    transport: WebSocket,
    sessionStorage: LocalStorage,
  },
});

Step 3: Subscribe to Events

Subscribe to specific event types on a collection by slug, or use * for all collections.

// New listings for a specific collection
client.onItemListed("boredapeyachtclub", (event) => {
  console.log("New listing:", event);
});

// Sales across all collections
client.onItemSold("*", (event) => {
  console.log("Sale:", event);
});

// Transfers for a specific collection
client.onItemTransferred("pudgypenguins", (event) => {
  console.log("Transfer:", event);
});

// Metadata updates
client.onItemMetadataUpdated("my-collection", (event) => {
  console.log("Metadata updated:", event);
});

// Offers received
client.onItemReceivedOffer("boredapeyachtclub", (event) => {
  console.log("Offer:", event);
});

// Bids received
client.onItemReceivedBid("*", (event) => {
  console.log("Bid:", event);
});

Available Event Methods

MethodFires when
onItemListedA new listing is created
onItemSoldAn item is sold
onItemTransferredAn item is transferred
onItemMetadataUpdatedItem metadata changes
onItemReceivedOfferAn item receives an offer
onItemReceivedBidAn item receives a bid
onItemCancelledA listing or offer is cancelled

Unsubscribing

Each subscription method returns an unsubscribe function:

const unsubscribe = client.onItemListed("boredapeyachtclub", (event) => {
  console.log(event);
});

// Later, stop listening
unsubscribe();

Event Payload Structure

Events include key fields like:

{
  event_type: "item_listed",
  sent_at: "2025-01-15T12:00:00Z",
  payload: {
    item: {
      chain: { name: "ethereum" },
      nft_id: "ethereum/0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D/1234",
      permalink: "https://opensea.io/assets/ethereum/0x.../1234",
      metadata: { name: "Bored Ape #1234", image_url: "..." }
    },
    base_price: "1000000000000000000",
    payment_token: { symbol: "ETH", decimals: 18 },
    collection: { slug: "boredapeyachtclub" },
    maker: { address: "0x..." },
    event_timestamp: "2025-01-15T12:00:00Z"
  }
}

Use event_timestamp for ordering — events can arrive out of order.

Example: Price Alert Bot

Build a bot that watches for listings below a price threshold:

import { OpenSeaStreamClient } from "@opensea/stream-js";
import { WebSocket } from "ws";
import { LocalStorage } from "node-localstorage";

const COLLECTION = "pudgypenguins";
const MAX_PRICE_ETH = 10;

const client = new OpenSeaStreamClient({
  token: "YOUR_OPENSEA_API_KEY",
  connectOptions: {
    transport: WebSocket,
    sessionStorage: LocalStorage,
  },
});

client.onItemListed(COLLECTION, (event) => {
  const priceWei = BigInt(event.payload.base_price);
  const priceEth = Number(priceWei) / 1e18;

  if (priceEth < MAX_PRICE_ETH) {
    console.log(
      `Alert: ${event.payload.item.metadata.name} listed at ${priceEth} ETH`,
    );
    console.log(`Link: ${event.payload.item.permalink}`);
  }
});

console.log(`Watching ${COLLECTION} for listings under ${MAX_PRICE_ETH} ETH...`);

Reconnection and Best Practices

  • Automatic reconnection — The SDK handles reconnection automatically. Lost messages during disconnects are not re-sent (best-effort delivery).
  • Heartbeat — The SDK sends heartbeats to keep the connection alive. No manual ping is needed.
  • Use event_timestamp — Events can arrive out of order. Sort by event_timestamp if ordering matters.
  • Filter server-side — Subscribe to specific collections rather than * when possible to reduce bandwidth.
  • Handle errors gracefully — Wrap event handlers in try/catch to prevent a single bad event from crashing your listener.

Using Without the SDK

Any language with a WebSocket client can connect directly:

  • Endpoint: wss://stream.openseabeta.com/socket/websocket?token=<API_KEY>
  • Heartbeat: Send {"topic": "phoenix", "event": "heartbeat", "payload": {}, "ref": 0} every 30 seconds
  • Subscribe: Send {"topic": "collection:<slug>", "event": "phx_join", "payload": {}, "ref": 0}
  • Unsubscribe: Send the same message with "event": "phx_leave"

Streaming vs. Polling

Streaming (@opensea/stream-js)Polling (REST API)
LatencyReal-time (sub-second)Depends on poll interval
Rate limitsDoes not countCounts toward limits
DeliveryBest-effort, no replayPaginated, can backfill
Use caseBots, alerts, live dashboardsAnalytics, backfilling history

For historical data or guaranteed completeness, use the REST events endpoints (GET /api/v2/events/collection/{slug}). For real-time reactions, use streaming.

Next Steps