Stream and decode v10 reports via WebSocket using the Go SDK

In this tutorial, you'll learn how to use the Data Streams SDK for Go to subscribe to real-time V10 reports for Backed xStock streams via a WebSocket connection. You'll set up your Go project, listen for real-time reports from the Data Streams Aggregation Network, decode the report data, and log their attributes to your terminal.

Requirements

  • Git: Make sure you have Git installed. You can check your current version by running git --version in your terminal and download the latest version from the official Git website if necessary.
  • Go Version: Make sure you have Go version 1.21 or higher. You can check your current version by running go version in your terminal and download the latest version from the official Go website if necessary.
  • API Credentials: Access to Data Streams requires API credentials. If you haven't already, contact us to request mainnet or testnet access.

Tutorial

Set up your Go project

  1. Create a new directory for your project and navigate to it:

    mkdir my-data-streams-project
    cd my-data-streams-project
    
  2. Initialize a new Go module:

    go mod init my-data-streams-project
    
  3. Install the Data Streams SDK:

    go get github.com/smartcontractkit/data-streams-sdk/go
    

Establish a WebSocket connection and listen for real-time reports

  1. Create a new Go file, stream.go, in your project directory:

    touch stream.go
    
  2. Insert the following code example and save your stream.go file:

     package main
    
     import (
       "context"
       "fmt"
       "os"
       "time"
    
       streams "github.com/smartcontractkit/data-streams-sdk/go"
       feed "github.com/smartcontractkit/data-streams-sdk/go/feed"
       report "github.com/smartcontractkit/data-streams-sdk/go/report"
       v10 "github.com/smartcontractkit/data-streams-sdk/go/report/v10" // Import the v10 report schema for Backed xStock streams.
     )
    
     func main() {
       if len(os.Args) < 2 {
         fmt.Println("Usage: go run stream.go [StreamID1] [StreamID2] ...")
         os.Exit(1)
       }
    
       // Set up the SDK client configuration
       cfg := streams.Config{
         ApiKey:    os.Getenv("API_KEY"),
         ApiSecret: os.Getenv("API_SECRET"),
         WsURL: "wss://ws.testnet-dataengine.chain.link",
         Logger: streams.LogPrintf,
       }
    
       // Create a new client
       client, err := streams.New(cfg)
       if err != nil {
         cfg.Logger("Failed to create client: %v\n", err)
         os.Exit(1)
       }
    
       // Parse the feed IDs from the command line arguments
       var ids []feed.ID
       for _, arg := range os.Args[1:] {
         var fid feed.ID
         if err := fid.FromString(arg); err != nil {
           cfg.Logger("Invalid stream ID %s: %v\n", arg, err)
           os.Exit(1)
         }
         ids = append(ids, fid)
       }
    
       ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
       defer cancel()
    
       // Subscribe to the feeds
       stream, err := client.Stream(ctx, ids)
       if err != nil {
         cfg.Logger("Failed to subscribe: %v\n", err)
         os.Exit(1)
       }
    
       defer stream.Close()
         for {
             reportResponse, err := stream.Read(context.Background())
             if err != nil {
                 cfg.Logger("Error reading from stream: %v\n", err)
                 continue
             }
    
             // Log the contents of the report before decoding
             cfg.Logger("Raw report data: %+v\n", reportResponse)
    
                 // Decode each report as it comes in
                 decodedReport, decodeErr := report.Decode[v10.Data](reportResponse.FullReport)
                 if decodeErr != nil {
                     cfg.Logger("Failed to decode report: %v\n", decodeErr)
                     continue
                 }
    
             // Log the decoded report
             cfg.Logger("\n--- Report Stream ID: %s ---\n" +
               "------------------------------------------\n" +
               "Valid From Timestamp    : %d\n" +
               "Observations Timestamp  : %d\n" +
               "Native Fee (wei)        : %s\n" +
               "Link Fee (wei)          : %s\n" +
               "Expires At              : %d\n" +
               "Last Update Timestamp   : %d\n" +
               "Price                   : %s\n" +
               "Market Status           : %d\n" +
               "Current Multiplier      : %s\n" +
               "New Multiplier          : %s\n" +
               "Activation DateTime     : %d\n" +
               "Tokenized Price         : %s\n" +
               "------------------------------------------\n",
               reportResponse.FeedID.String(),
               decodedReport.Data.ValidFromTimestamp,
               decodedReport.Data.ObservationsTimestamp,
               decodedReport.Data.NativeFee.String(),
               decodedReport.Data.LinkFee.String(),
               decodedReport.Data.ExpiresAt,
               decodedReport.Data.LastUpdateTimestamp,
               decodedReport.Data.Price.String(),
               decodedReport.Data.MarketStatus,
               decodedReport.Data.CurrentMultiplier.String(),
               decodedReport.Data.NewMultiplier.String(),
               decodedReport.Data.ActivationDateTime,
               decodedReport.Data.TokenizedPrice.String(),
             )
    
             // Also, log the stream stats
             cfg.Logger("\n--- Stream Stats ---\n" +
             stream.Stats().String() + "\n" +
             "--------------------------------------------------------------------------------------------------------------------------------------------\n",
             )
         }
     }
    
  3. Download the required dependencies and update the go.mod and go.sum files:

    go mod tidy
    
  4. Set up the SDK client configuration within stream.go with your API credentials and the WebSocket URL:

    cfg := streams.Config{
        ApiKey:    os.Getenv("API_KEY"),
        ApiSecret: os.Getenv("API_SECRET"),
        WsURL: "wss://ws.testnet-dataengine.chain.link",
        Logger: streams.LogPrintf,
    }
    
    • Set your API credentials as environment variables:

      export API_KEY="<YOUR_API_KEY>"
      export API_SECRET="<YOUR_API_SECRET>"
      

      Replace <YOUR_API_KEY> and <YOUR_API_SECRET> with your API credentials.

    • WsURL is the WebSocket URL for the Data Streams Aggregation Network. Use wss://ws.testnet-dataengine.chain.link for the testnet environment.

    See the SDK Reference page for more configuration options.

  5. For this example, you'll subscribe to an RWA stream. See the RWA Streams page for a complete list of available Real World Assets.

    Execute your application. Replace [STREAM_ID] with your stream ID.

    go run stream.go [STREAM_ID]
    

    Expect output similar to the following in your terminal:

     2025-07-29T07:00:34-05:00 Raw report data: {"fullReport":"0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd6900000000000000000000000000000000000000000000000000000000012b136f000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b000000000000000000000000000000000000000000000000000000006888b7e2000000000000000000000000000000000000000000000000000000006888b7e200000000000000000000000000000000000000000000000000004c142b9ed5c0000000000000000000000000000000000000000000000000003eb04f48948f6f0000000000000000000000000000000000000000000000000000000068b044e200000000000000000000000000000000000000000000000018569aaaebd353c000000000000000000000000000000000000000000000000b997fe8cfd635800000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002dfef02967d39ebebf7741cb7581a91128167c68e9e485d1d34456cddbe3c131b65d697a86cd50e38642b582bd4b0cc58ac3bc67b9611df12bdf88208dcd0af2500000000000000000000000000000000000000000000000000000000000000027777290339633ffe117e3db85efc4a2f88617286fd033e57ac7642b06b77fe3a18fdfed657854ff5e1c253e57bd5334d85ee999dcc223b7286092ab03a5ece27","feedID":"0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b","validFromTimestamp":1753790434,"observationsTimestamp":1753790434}
    
     2025-07-29T07:00:34-05:00
    --- Report Stream ID: 0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b ---
    ------------------------------------------
    validFromTimestamp    : 1755282585
    observationsTimestamp : 1755282585
    nativeFee             : 72390983702980
    linkFee               : 14788098714955797
    expiresAt             : 1757874585
    lastUpdateTimestamp   : 1755282581729000000
    price                 : 329870000000000000000
    marketStatus          : 2
    currentMultiplier     : 1000000000000000000
    newMultiplier         : 0
    activationDateTime    : 0
    tokenizedPrice        : 0
    ------------------------------------------
    
     2025-07-29T07:00:34-05:00
     --- Stream Stats ---
     accepted: 1, deduplicated: 0, total_received 1, partial_reconnects: 0, full_reconnects: 0, configured_connections: 1, active_connections 1
     --------------------------------------------------------------------------------------------------------------------------------------------
    
     2025-07-29T07:00:35-05:00 Raw report data: {"fullReport":"0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd6900000000000000000000000000000000000000000000000000000000012b1372000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b000000000000000000000000000000000000000000000000000000006888b7e3000000000000000000000000000000000000000000000000000000006888b7e300000000000000000000000000000000000000000000000000004c144784e03d000000000000000000000000000000000000000000000000003eb0c331846a690000000000000000000000000000000000000000000000000000000068b044e300000000000000000000000000000000000000000000000018569aaaebd353c000000000000000000000000000000000000000000000000b997fe8cfd6358000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000029bc44aa6a85efdf31f3208a8c4333a083d4a52f6fae63d7d1ea2ff0f4f8243513e3d2caa118f64dc678ad157e51c3784b4fe38234bdc30ae8f1d43b096482b4f00000000000000000000000000000000000000000000000000000000000000025a53956d52ffedd51bc5ba08d64f3623c20549758e8a7ae11ef97df7b592d24d33a7f253d8e7a6b8d625aaf7cdd158b7dc82c392aa8a720d33bce348133aa713","feedID":"0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b","validFromTimestamp":1753790435,"observationsTimestamp":1753790435}
    
     2025-07-29T07:00:35-05:00
    --- Report Stream ID: 0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b ---
    ------------------------------------------
    validFromTimestamp    : 1755282585
    observationsTimestamp : 1755282585
    nativeFee             : 72390983702980
    linkFee               : 14788098714955797
    expiresAt             : 1757874585
    lastUpdateTimestamp   : 1755282581729000000
    price                 : 329870000000000000000
    marketStatus          : 2
    currentMultiplier     : 1000000000000000000
    newMultiplier         : 0
    activationDateTime    : 0
    tokenizedPrice        : 0
    ------------------------------------------
    
     2025-07-29T07:00:35-05:00
     --- Stream Stats ---
     accepted: 2, deduplicated: 0, total_received 2, partial_reconnects: 0, full_reconnects: 0, configured_connections: 1, active_connections 1
     --------------------------------------------------------------------------------------------------------------------------------------------
    
     2025-07-29T07:00:36-05:00 Raw report data: {"fullReport":"0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd6900000000000000000000000000000000000000000000000000000000012b1375000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b000000000000000000000000000000000000000000000000000000006888b7e4000000000000000000000000000000000000000000000000000000006888b7e400000000000000000000000000000000000000000000000000004c14ba7cd814000000000000000000000000000000000000000000000000003eb178e40a71610000000000000000000000000000000000000000000000000000000068b044e400000000000000000000000000000000000000000000000018569aaaebd353c000000000000000000000000000000000000000000000000b997fe8cfd63580000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000298ddee1e11820b03b811f2e79deab6dbef3b9677bdc715030dcfb14b49fc372adb29b400d69c9844def9907e264705716820c1e54ed2bf7d1b1943f5f1df6ee700000000000000000000000000000000000000000000000000000000000000022c8b64907a3e950a7cc1166b1e0e166bf4f27213ab911d397d1aa0fda70aad0c5581ce1fec7463821f6a988290edfdbde86a5c0c12c22123dc7635cd06c018ac","feedID":"0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b","validFromTimestamp":1753790436,"observationsTimestamp":1753790436}
    
     2025-07-29T07:00:36-05:00
    --- Report Stream ID: 0x0008b8ad9dc4061d1064033c3abc8a4e3f056e5b61d8533e8190eb96ef3b330b ---
    ------------------------------------------
    validFromTimestamp    : 1755282585
    observationsTimestamp : 1755282585
    nativeFee             : 72390983702980
    linkFee               : 14788098714955797
    expiresAt             : 1757874585
    lastUpdateTimestamp   : 1755282581729000000
    price                 : 329870000000000000000
    marketStatus          : 2
    currentMultiplier     : 1000000000000000000
    newMultiplier         : 0
    activationDateTime    : 0
    tokenizedPrice        : 0
    ------------------------------------------
    
     2025-07-29T07:00:36-05:00
     --- Stream Stats ---
     accepted: 3, deduplicated: 0, total_received 3, partial_reconnects: 0, full_reconnects: 0, configured_connections: 1, active_connections 1
     --------------------------------------------------------------------------------------------------------------------------------------------
    
    [...]
    

Decoded report details

The decoded report details include:

FieldValueDescription
feedId[STREAM_ID]Unique identifier for the Data Streams feed.
validFromTimestamp1755282585Earliest timestamp when the price is valid.
observationsTimestamp1755282585Latest timestamp when the price is valid.
nativeFee72390983702980Cost to verify report onchain (native token).
linkFee14788098714955797Cost to verify report onchain (LINK).
expiresAt1757874585Expiration date of the report.
lastUpdateTimestamp1755282581729000000Timestamp of the last valid price update.
price329870000000000000000Last traded price from the real-world equity market (18 decimals). For readability: 329.87.
marketStatus2Status of the real-world equity market. Possible values: 0 (Unknown), 1 (Closed), 2 (Open), 3 (Halted).
currentMultiplier1000000000000000000Currently applied multiplier accounting for past corporate actions (18 decimals). For readability: 1.0.
newMultiplier0Multiplier to be applied at the activationDateTime (set to 0 if none is scheduled).
activationDateTime0When the next corporate action takes effect (set to 0 if none is scheduled).
tokenizedPrice024/7 tokenized equity price as traded on supported exchanges (currently always returns 0).

Payload for onchain verification

In this tutorial, you log and decode the fullReport payload to extract the report data. In a production environment, you should verify the data to ensure its integrity and authenticity. Refer to the Verify report data onchain tutorial.

Explanation

Establishing a WebSocket connection and listening for reports

Your application uses the Stream function in the Data Streams SDK's client package to establish a real-time WebSocket connection with the Data Streams Aggregation Network.

Once the WebSocket connection is established, your application subscribes to one or more streams by passing an array of feed.IDs to the Stream function. This subscription lets the client receive real-time updates whenever new report data is available for the specified streams.

Decoding a report

As data reports arrive via the established WebSocket connection, they are processed in real-time:

  • Reading streams: The Read method on the returned Stream object is continuously called within a loop. This method blocks until new data is available, ensuring that all incoming reports are captured as soon as they are broadcasted.

  • Decoding reports: For each received report, the SDK's Decode function parses and transforms the raw data into a structured format.

Handling the decoded data

In this example, the application logs the structured report data to the terminal. However, this data can be used for further processing, analysis, or display in your own application.

What's next

Get the latest Chainlink content straight to your inbox.