> For the complete documentation index, see [llms.txt](https://docs.blockrazor.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.blockrazor.io/streams/block-stream/base/get-flashblockstream.md).

# Get FlashBlockStream

### What is Base Get FlashBlockStream

`Get FlashBlockStream` is a real-time FlashBlock data subscription interface provided by BlockRazor for Base, used to obtain earlier stages of block data on Base with lower latency. This interface supports both gRPC and WebSocket protocols, making it suitable for trading systems, and monitoring systems that are more sensitive to data arrival time.

On Base, FlashBlock can be understood as a "sub-block" data stream that occurs before the formal block is formed, typically pushed continuously at a frequency of about 200ms. Compared to the standard block time of about 2 seconds, FlashBlock can provide transaction-related feedback much earlier, making it more suitable for low-latency scenarios that require quick on-chain awareness of changes.

For trading bots, quantitative strategies, real-time monitoring platforms, and front-end trading applications, waiting for blocks often means longer response times. The value of Get FlashBlockStream is to help the system receive transaction and block change signals earlier, before the block arrives, thus buying more time for subsequent judgment and response.

### Why choose Base Get FlashBlockStream

BlockRazor, based on [BEF](/core-technology/blockchain-edge-fabric.md), provides access points to multiple regions on Base, including Frankfurt, Virginia, and Tokyo. According to [Base Benchmark](https://blockrazor.io/zh/blog/20250922basebenchmark/), BlockRazor demonstrates an advantage over the official Base service in FlashBlock reception latency across multiple regions, with a particularly significant lead in the mid-to-high percentile range.

### FAQ

<details>

<summary>What is the difference between Get BlockStream and Get FlashBlockStream</summary>

The core difference between the two lies in the different data granularity, time points, and applicable scenarios.

* Get BlockStream\
  It is used to retrieve block that has already been formed on Base, focusing on confirmed blocks and transactions within. It is more suitable for monitoring confirmation results, block-level analysis, on-chain data processing, and data systems that need to stably consume blocks.
* **Get FlashBlockStream**\
  Used to retrieve FlashBlock data on Base. FlashBlock is a "sub-block" of data pushed by Base approximately every 200ms, providing pre-confirmation information for transactions much earlier than the standard 2-second formal block time. It is more suitable for scenarios that are more sensitive to low latency and want to see on-chain changes as early as possible.

</details>

### Endpoint

{% tabs %}
{% tab title="gRPC" %}

<table><thead><tr><th width="138.33984375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Virginia</td><td>virginia.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.grpc.base.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}

{% tab title="WebSocket" %}

<table><thead><tr><th width="118.8203125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>ws://frankfurt.base.blockrazor.xyz:81/ws</td></tr><tr><td>Virginia</td><td>ws://virginia.base.blockrazor.xyz:81/ws</td></tr><tr><td>Tokyo</td><td>ws://tokyo.base.blockrazor.xyz:81/ws</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Price

The price is $250 per data stream per month. Please go to the [Pricing](https://blockrazor.io/#/pricing) page to purchase.

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Request Example

{% tabs %}
{% tab title="gRPC" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/c4bec3d65e55ffb0da07253fa78aefe1b1c07e33/main.go#L93)

```go
// GetFlashBlockStream provides a simplified example of subscribing to and processing the flash block stream.
// Note: This function attempts to connect and subscribe only once. For production use, implement your own reconnection logic.
func GetFlashBlockStream(authToken string) {
	log.Printf("[FlashStream] Attempting to connect to gRPC server at %s...", grpcAddr)

	// Establish a connection to the gRPC server with a timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, grpcAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Printf("[FlashStream] Failed to connect to gRPC server: %v", err)
		return
	}
	defer conn.Close()

	log.Println("[FlashStream] Successfully connected to gRPC server.")
	client := basepb.NewBaseApiClient(conn)

	// Create a new context with authentication metadata for the stream subscription.
	streamCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", authToken))
	stream, err := client.GetRawFlashBlockStream(streamCtx, &basepb.GetRawFlashBlocksStreamRequest{})
	if err != nil {
		log.Printf("[FlashStream] Failed to subscribe to stream: %v", err)
		return
	}

	log.Println("[FlashStream] Subscription successful. Waiting for new flash blocks...")

	// Loop indefinitely to receive messages from the stream.
	for {
		block, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				log.Println("[FlashStream] Stream closed by the server (EOF).")
			} else {
				log.Printf("[FlashStream] An error occurred while receiving data: %v", err)
			}
			break // Exit the loop on error or stream closure.
		}

		// Process the received flash block data.
		jsonString, err := ParseFlashBlockByte(block.Message)
		if err != nil {
			log.Printf("[FlashStream] Failed to parse flash block data: %v", err)
			continue
		}

		var jsonMap map[string]interface{}
		if err := json.Unmarshal([]byte(jsonString), &jsonMap); err != nil {
			log.Printf("[FlashStream] Failed to unmarshal flash block JSON: %v", err)
			continue
		}
		printPretty(jsonMap)
	}
}
```

{% endtab %}

{% tab title="WebSocket-Go" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/main.go#L181)

```go
// GetWebSocketFlashBlockStream provides a simplified example of using the WebSocket API to subscribe to the flash block stream.
// Note: This function attempts to connect only once and will exit on a read error. For production use, implement your own reconnection logic.
func GetWebSocketFlashBlockStream(authToken string) {
	// Set up the HTTP header with the authorization token.
	header := http.Header{}
	header.Set("Authorization", authToken)

	// Dial the WebSocket server.
	dialer := websocket.DefaultDialer
	conn, resp, err := dialer.Dial(websocketAddr, header)
	if err != nil {
		if resp != nil {
			log.Fatalf("[WebSocket] Dial failed: %v (HTTP status: %s)", err, resp.Status)
		}
		log.Fatalf("[WebSocket] Dial failed: %v", err)
	}
	defer conn.Close()
	log.Printf("[WebSocket] Successfully connected to %s", websocketAddr)

	// Prepare the JSON-RPC subscription request.
	req := map[string]interface{}{
		"jsonrpc": "2.0",
		"method":  "subscribe_FlashBlock",
		"params":  []interface{}{},
		"id":      1,
	}
	reqB, _ := json.Marshal(req)
	if err := conn.WriteMessage(websocket.TextMessage, reqB); err != nil {
		log.Fatalf("[WebSocket] Failed to send subscription request: %v", err)
	}
	log.Printf("[WebSocket] Subscription request sent: %s", string(reqB))

	// Loop indefinitely to read messages from the server.
	for {
		msgType, msg, err := conn.ReadMessage()
		if err != nil {
			log.Printf("[WebSocket] Error reading message: %v", err)
			return // Exit the function on any read error.
		}
		if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage {
			continue // Ignore messages that are not text or binary.
		}

		// Parse the outer JSON-RPC response wrapper.
		var outer = &FlashBlockWebSocketResponse{}
		if err := json.Unmarshal(msg, &outer); err != nil {
			log.Printf("[WebSocket] Failed to parse JSON from server: %v\nRaw data: %s", err, string(msg))
			continue
		}

		// Extract the "result" field, which contains the actual flash block data, and process it.
		resultRaw := outer.Result
		if jsonString, err := ParseFlashBlockByte(resultRaw); err == nil {
			printPretty(jsonString)
		} else {
			log.Printf("[WebSocket] Received message without a valid result field. Raw data: %s", string(msg))
		}
	}
}
```

{% endtab %}

{% tab title="WebSocket-Cli" %}

```
wscat -H "Authorization: <AUTH_HEADER>" \
  -c ws://frankfurt.base.blockrazor.xyz:81/ws \
  --wait 1000 \
  --execute '{"jsonrpc": "2.0", "id": 1, "method": "subscribe_FlashBlock", "params": []}'
```

{% endtab %}

{% tab title="JS" %}

```javascript
const WebSocket = require('ws'); // WebSocket library for Node.js, install via npm(npm install ws) if not already installed
const zlib = require('zlib'); // Node.js built-in library for compression/decompression

// --- Configuration Parameters ---
// Replace with your actual authorization token
const AUTH_TOKEN = "<YOUR_AUTH_TOKEN>";
const WS_URL = "<WEBSOCKET_URL>"; // websocket URL

const SUBSCRIPTION_MESSAGE = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "subscribe_FlashBlock",
    "params": []
};
const WAIT_TIME_MS = 1000; // Time to wait before sending the subscription message


/**
 * Decompresses a Buffer using the Brotli algorithm.
 * @param {Buffer} dataBuffer - The data buffer to be decompressed.
 * @returns {Promise<string>} - A promise that resolves with the decompressed string.
 */
function ParseBrotliData(dataBuffer) {
    return new Promise((resolve, reject) => {
        // zlib.brotliDecompress is used for Brotli decompression
        zlib.brotliDecompress(dataBuffer, (err, decompressedBuffer) => {
            if (err) {
                // Return error if decompression fails
                return reject(new Error("Brotli decompression failed."));
            }
            // Convert the decompressed buffer to a string and resolve
            resolve(decompressedBuffer.toString('utf8'));
        });
    });
}

// --- WebSocket Connection and Operations ---

function connectWebSocket() {
    console.log(`Attempting to connect to: ${WS_URL}`);

    // Create custom Headers for authorization
    const headers = {
        'Authorization': AUTH_TOKEN
    };

    // Establish WebSocket connection, passing headers
    const ws = new WebSocket(WS_URL, {
        headers: headers
    });

    // 1. Connection established successfully
    ws.on('open', () => {
        console.log('✅ Connection established.');

        // Mimicking --wait 1000, wait 1 second before sending the message
        setTimeout(() => {
            const messageString = JSON.stringify(SUBSCRIPTION_MESSAGE);
            
            console.log(`➡️ Sending subscription message (after waiting ${WAIT_TIME_MS}ms):`);
            console.log(messageString);
            
            ws.send(messageString);
        }, WAIT_TIME_MS);
    });

    // 2. Message received
    ws.on('message', async (data) => {
        // Data is a Buffer received from the server, which we suspect is uncompressed JSON string.
        const rawJsonString = data.toString('utf8');
        
        try {
            // STEP 1: Parse the outer JSON-RPC wrapper
            const rpcResponse = JSON.parse(rawJsonString);

            // Check if 'result' or 'params' field exists and contains the Base64 data
            const base64Data = rpcResponse.result || (rpcResponse.params && rpcResponse.params.data);

            if (!base64Data || typeof base64Data !== 'string') {
                console.log(`\n⬅️ Received non-compressed JSON message:`);
                console.log(JSON.stringify(rpcResponse, null, 2));
                return;
            }

            console.log(`\n⬅️ Received compressed data in JSON wrapper. Base64 length: ${base64Data.length}`);

            // STEP 2: Decode Base64 string back into raw Buffer
            const rawBrotliBuffer = Buffer.from(base64Data, 'base64');
            console.log(`   Decoded Base64 to Buffer. Brotli Buffer size: ${rawBrotliBuffer.length} bytes.`);
            
            // STEP 3: Decompress the raw Brotli Buffer
            const decompressedString = await ParseBrotliData(rawBrotliBuffer);
            
            console.log("🌟 Successfully decompressed and parsed message:");

            // STEP 4: Parse the inner JSON content
            const finalData = JSON.parse(decompressedString);
            console.log(JSON.stringify(finalData, null, 2));

        } catch (e) {
            // Handle any error during the 4 steps (JSON parse, Base64 decode, Brotli decompress, final JSON parse)
            console.error("\n❌ Error during processing compressed payload:");
            console.error(`   Error message: ${e.message}`);
            console.log(`   Raw received string (first 200 chars): ${rawJsonString.substring(0, 200)}...`);
        }
    });

    // 3. Connection closed
    ws.on('close', (code, reason) => {
        console.log(`\n❌ Connection closed. Code: ${code}, Reason: ${reason.toString()}`);
    });

    // 4. Connection error
    ws.on('error', (error) => {
        console.error(`\n🔥 An error occurred: ${error.message}`);
    });
}

// Start the client
connectWebSocket();
```

{% endtab %}
{% endtabs %}

#### [proto](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/proto/BaseApi.proto)

```go
syntax = "proto3";

option go_package = "./basepb";


import "google/protobuf/wrappers.proto";

message BaseBlock {
  string parent_hash = 1;
  string fee_recipient = 2;
  bytes state_root = 3;
  bytes receipts_root = 4;
  bytes logs_bloom = 5;
  bytes prev_randao = 6;
  uint64 block_number = 7;
  uint64 gas_limit = 8;
  uint64 gas_used = 9;
  uint64 timestamp = 10;
  bytes extra_data = 11;
  repeated uint64 base_fee_per_gas = 12;
  string block_hash = 13;
  repeated bytes transactions = 14;

  repeated Withdrawal withdrawals = 15;
  google.protobuf.UInt64Value blob_gas_used = 16;
  google.protobuf.UInt64Value excess_blob_gas = 17;
  google.protobuf.BytesValue withdrawals_root = 18;
}

message Withdrawal {
  uint64 index = 1;
  uint64 validator = 2;
  bytes address = 3;
  uint64 amount = 4;
}

message GetRawFlashBlocksStreamRequest {
}

message GetBlockStreamRequest {
}

message SendTransactionRequest {
  string rawTransaction = 1;
}

message SendTransactionResponse {
  string txHash = 1;
}

message FlashBlockStrRequest {
}

message RawFlashBlockStrResponse {
  bytes message = 1;
}

service BaseApi {
  rpc SendTransaction(SendTransactionRequest) returns (SendTransactionResponse);
  rpc GetBlockStream(GetBlockStreamRequest) returns (stream BaseBlock);
  rpc GetRawFlashBlockStream(GetRawFlashBlocksStreamRequest) returns (stream RawFlashBlockStrResponse);
}
```

### Response

**Normal**

```go
{
    message: "185329……7e04b7"
}
```

**Abnormal**

```
rpc error: code = Unknown desc = Authentication information is missing. Please provide a valid auth token
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.blockrazor.io/streams/block-stream/base/get-flashblockstream.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
