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, with 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, since 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-api.opensea.io/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