In Part 1, we built the cash register. We implemented the X402 Protocol to gate content behind a crypto-payment wall. Now, we need to stock the shelves.
Static data in crypto is dead data. Prices move in blocks (every 2 seconds on Base), and arbitrage opportunities vanish in milliseconds. To build a dashboard worth paying for, we cannot rely on standard REST APIs that we have to poll every few seconds. We need Push Architecture.
Enter the GoldRush Streaming API by Covalent.
This is not your average API. It uses GraphQL over WebSockets, a powerful combination that allows us to subscribe to specific on-chain events—like token price updates (OHLCV) or wallet movements—and receive them the instant they are validated by the network.
In Part 2, we will build the Live Intelligence Dashboard. We will connect to the GoldRush stream, filter for high-value signal, and pipe it directly to our authenticated users.
Before we code, let's understand the protocol. Most developers are used to REST (GET /transactions). GoldRush Streaming uses graphql-transport-ws.
REST: Request -> Wait -> Response. High latency. Heavy on server resources (polling).
GoldRush Streaming: Handshake -> Subscribe -> Stream -> Stream -> Stream. Low latency. Event-driven.
For our dashboard, we care about three streams available on BASE_MAINNET:
OHLCV (Open, High, Low, Close, Volume): Live price candles for tokens.
Wallet Activity: Real-time monitoring of specific "Whale" addresses.
New DEX Pairs: Detecting new liquidity pools the moment they launch (Alpha).
We will focus on OHLCV to build a live trading view.
To access the stream, you need a GoldRush API Key.
Go to the GoldRush Platform.
Sign up and create a new key.
Security Note: Unlike the private key in Part 1, this key is for data access. However, you should still keep it in your .env file server-side to prevent quota theft.
Update your .env file from Part 1:
# ... existing X402 config ...
GOLDRUSH_API_KEY=cqt_rX... # Your new keyWe need a WebSocket client capable of speaking GraphQL. The industry standard is graphql-ws.
Open your terminal in the Cursor project:
npm install graphql-ws ws
We will create a new service file to handle the streaming logic separate from our Express routes. Create streamService.js
Open streamService.js. We will build a class to manage the GoldRush connection.
Prompt for Cursor: "Create a Node.js class GoldRushStream using the graphql-ws library. It should connect to wss://gr-staging-v2.streaming.covalenthq.com/graphqland authenticate using a bearer token from env variables."
The resulting code should look structurally like this:
require('dotenv').config();
const { createClient } = require('graphql-ws');
const WebSocket = require('ws');
class GoldRushStream {
constructor() {
this.client = createClient({
webSocketImpl: WebSocket,
url: 'wss://[api.covalenthq.com/graphql](https://api.covalenthq.com/graphql)',
connectionParams: {
Authorization: `Bearer ${process.env.GOLDRUSH_API_KEY}`,
},
});
this.unsubscribe = null;
}
// Function to start the stream
startStream(callback) {
console.log("🌊 Connecting to GoldRush Stream...");
// Subscription logic will go here
}
}
module.exports = new GoldRushStream();
Now, let's visualize what we are building. We want a professional-grade interface that displays live OHLCV (Open, High, Low, Close, Volume) data candles.
After the user passes the X402 payment gate, they will be redirected to this real-time view:
This dashboard consumes the WebSocket feed we are about to configure and renders it into a clean table, updating dynamically as new blocks are confirmed on Base Mainnet.
This is the most critical part. We need to write the GraphQL query that tells GoldRush exactly what we want. We don't want the firehose of the entire blockchain; we want specific data.
We will subscribe to 1-minute price candles for Wrapped Ether (WETH) on Base Mainnet.
The query structure:
subscription {
ohlcv(
chain_name: "base-mainnet",
dex_name: "uniswap_v3",
pool_address: "0xd0b53d9277642d899df5f87a3966a34909ae9091", # WETH/USDC on Base
interval: "minute"
) {
time
open
high
low
close
volume
}
}Add this query to your streamService.js inside the startStream method:
startStream(onDataReceived) {
const query = `
subscription {
ohlcv(
chain_name: "base-mainnet",
dex_name: "uniswap_v3",
pool_address: "0xd0b53d9277642d899df5f87a3966a34909ae9091",
interval: "minute"
) {
time
close
volume
}
}
`;
this.unsubscribe = this.client.subscribe(
{ query },
{
next: (data) => {
// This callback fires every time a new block/event occurs
onDataReceived(data);
},
error: (err) => console.error('Stream Error:', err),
complete: () => console.log('Stream Closed'),
},
);
}Now we connect Part 1 and Part 2. We want to ensure that only paid users can access this live stream data.
In a real-world app, you would likely use WebSockets to push data to the frontend. For this tutorial, we will create a simple Buffer that holds the latest price, and our protected route will serve that latest price.
Import the Stream Service:const goldRush = require('./streamService');
Initialize the Data Store:
We need a variable to hold the latest data
let latestMarketData = { status: "Waiting for blockchain..." };
// Start the stream when the server starts
goldRush.startStream((data) => {
if (data.data && data.data.ohlcv) {
console.log("Update received:", data.data.ohlcv);
latestMarketData = data.data.ohlcv;
}
});3. Update the Protected Route:
Modify the /api/premium-data route we built in Part 1 to return latestMarketData instead of static text.
app.get('/api/premium-data', paymentMiddleware(premiumContentConfig), (req, res) => {
res.json({
success: true,
source: "GoldRush Streaming API",
data: latestMarketData
});
});
Raw blockchain data is often ugly. Prices might come back with 18 decimal places or scientific notation.
In server.js, let's add a formatting layer inside the stream callback before updating latestMarketData
goldRush.startStream((payload) => {
const rawData = payload.data.ohlcv[0]; // Get the first (latest) candle
if (rawData) {
latestMarketData = {
token: "WETH",
price: `$${parseFloat(rawData.close).toFixed(2)}`,
volume: parseFloat(rawData.volume).toLocaleString(),
trend: rawData.close > rawData.open ? "UP 🟢" : "DOWN 🔴",
timestamp: new Date().toLocaleTimeString()
};
console.log("Cache updated:", latestMarketData.timestamp);
}
});Streaming is sensitive. Here are the common pitfalls:
WebSockets die. It happens.
Solution: The graphql-ws client has built-in retry logic, but you should monitor the complete or error callbacks. If the stream closes, call startStream() again after a 5-second delay.
Error: 4403: Forbidden or Connection refused.
Solution: Check your .env file. Ensure GOLDRUSH_API_KEY is loaded correctly. If you regenerated the key, restart the server.
Symptom: Connection closes after 1 minute of silence.
Solution: GoldRush sends "ping" messages. Ensure your client handles PONGs (standard libraries do this automatically). Also, ensure you are subscribing to a chain with activity. Base Mainnet is active, but testnets can be quiet.
We are running locally, but "Vibecoding" means building for the world.
If you have 1,000 paying users, you don't want to open 1,000 WebSocket connections to GoldRush.
Optimization: Open ONE connection to GoldRush on your server. Cache the data in a Redis store or a local variable (as we did). Serve the 1,000 users from that local cache. This is the Multiplexer Pattern.
Never send your GoldRush API key to the frontend. Our architecture handles this perfectly: the key stays in server.js, and the frontend only sees the processed JSON.


Congratulations. You have vibecoded a full-stack, monetized, real-time Web3 application.
Let's recap what we built:
Economic Layer: A server that demands payment via X402 and verifies transactions on Base.
Intelligence Layer: A streaming service that consumes live GoldRush data via WebSockets.
Integration: A unified pipeline where crypto-payments unlock real-time crypto-data.
Where to go next?
Expand the Stream: Subscribe to "New Pair Created" events to build a "Sniper Bot" dashboard.
Session Tokens: Instead of paying per request, issue a JWT (JSON Web Token) after payment that gives 24-hour access.
Deploy: Push this to a VPS, set up Nginx with SSL, and start earning ETH for your data