# Fetch and decode Data Streams reports using the Rust SDK
Source: https://docs.chain.link/data-streams/tutorials/rust-sdk-fetch

> For the complete documentation index, see [llms.txt](/llms.txt).

<DataStreams section="dsNotes" />

In this tutorial, you'll learn how to use the [Data Streams SDK](/data-streams/reference/data-streams-api/rust-sdk) for Rust to fetch and decode [reports](/data-streams/reference/report-schema-overview) from the Data Streams Aggregation Network. You'll set up your Rust project, retrieve reports, decode them, and log their attributes.

## Requirements

- **Rust**: Make sure you have Rust installed. You can install Rust by following the instructions on the official [Rust website](https://www.rust-lang.org/tools/install).
- **API Credentials**: Access to Data Streams requires API credentials. If you haven't already, [contact us](https://chain.link/contact?ref_id=datastreams) to request mainnet or testnet access.

## Tutorial

You'll start with the set up of your Rust project, installing the SDK and pasting example code. Next, you'll fetch and decode reports for crypto streams and log their attributes to your terminal.

### Set up your Rust project

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

   ```bash
   mkdir my-data-streams-project && cd my-data-streams-project
   ```

2. Initialize a new Rust project:

   ```bash
   cargo init
   ```

3. Add the following dependencies to your `Cargo.toml` file:

   ```toml
   [dependencies]
   chainlink-data-streams-sdk = "1.0.3"
   chainlink-data-streams-report = "1.0.3"
   tokio = { version = "1.4", features = ["full"] }
   hex = "0.4"
   ```

### Fetch and decode a report with a single stream

1. Replace the contents of `src/main.rs` with the following code:

   ```rust
   use chainlink_data_streams_report::feed_id::ID;
   // NOTE: Use the report version (v3, v8, etc.) that matches your stream
   use chainlink_data_streams_report::report::{ decode_full_report, v3::ReportDataV3 };
   use chainlink_data_streams_sdk::client::Client;
   use chainlink_data_streams_sdk::config::Config;
   use std::env;
   use std::error::Error;

   #[tokio::main]
   async fn main() -> Result<(), Box<dyn Error>> {
      // Get feed ID from command line arguments
      let args: Vec<String> = env::args().collect();
      if args.len() < 2 {
         eprintln!("Usage: cargo run [FeedID]");
         std::process::exit(1);
      }
      let feed_id_input = &args[1];

      // Get API credentials from environment variables
      let api_key = env::var("API_KEY").expect("API_KEY must be set");
      let api_secret = env::var("API_SECRET").expect("API_SECRET must be set");

      // Initialize the configuration
      let config = Config::new(
         api_key,
         api_secret,
         "https://api.testnet-dataengine.chain.link".to_string(),
         "wss://api.testnet-dataengine.chain.link/ws".to_string()
      ).build()?;

      // Initialize the client
      let client = Client::new(config)?;

      // Parse the feed ID
      let feed_id = ID::from_hex_str(feed_id_input)?;

      // Fetch the latest report
      let response = client.get_latest_report(feed_id).await?;
      println!("\nRaw report data: {:?}\n", response.report);

      // Decode the report
      let full_report = hex::decode(&response.report.full_report[2..])?;
      let (_report_context, report_blob) = decode_full_report(&full_report)?;
      // NOTE: Use the report version (v3, v8, etc.) that matches your stream
      let report_data = ReportDataV3::decode(&report_blob)?;

      // Print decoded report details
      // NOTE: Adjust for your report and desired output
      println!("\nDecoded Report for Stream ID {}:", feed_id_input);
      println!("------------------------------------------");
      println!("Observations Timestamp: {}", response.report.observations_timestamp);
      println!("Benchmark Price       : {}", report_data.benchmark_price);
      println!("Bid                   : {}", report_data.bid);
      println!("Ask                   : {}", report_data.ask);
      println!("Valid From Timestamp  : {}", response.report.valid_from_timestamp);
      println!("Expires At            : {}", report_data.expires_at);
      println!("Link Fee              : {}", report_data.link_fee);
      println!("Native Fee            : {}", report_data.native_fee);
      println!("------------------------------------------");

      Ok(())
   }
   ```

> **NOTE: Adapt the example**
>
> The above example decodes a crypto report using the [v3 schema](/data-streams/reference/report-schema-v3). If
> you're working with a different type of stream (e.g., RWA, NAV), make sure to adjust the import statement,
> decoding logic, and access patterns to match the appropriate report version (e.g., v8, v9). Refer to the [Report
> Schemas](/data-streams/reference/report-schema-overview) for your stream. <br /> Learn more about [adapting code
> for different report schema versions](#adapting-code-for-different-report-schema-versions).

1. Set up your API credentials as environment variables:

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

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

   The Rust code sample reads these environment variables using `std::env::var("API_KEY")` and `std::env::var("API_SECRET")` when building the client configuration:

   ```rust
   let api_key = std::env::var("API_KEY").expect("API_KEY must be set");
   let api_secret = std::env::var("API_SECRET").expect("API_SECRET must be set");

   // Initialize the configuration
   let config = Config::new(
      api_key,
      api_secret,
      "https://api.testnet-dataengine.chain.link".to_string(),
      "wss://api.testnet-dataengine.chain.link/ws".to_string()
   ).build()?;
   ```

   This configuration also specifies the `rest_url`, which is the base URL for the API, along with the WebSocket endpoint [for subscribing to a streamed data report](/data-streams/tutorials/rust-sdk-stream). In this example, both are set to the testnet URLs for Data Streams.

   See the [Rust SDK Reference](/data-streams/reference/data-streams-api/rust-sdk#configuration-reference) page for more configuration options.

2. Read from a [testnet crypto stream](/data-streams/crypto-streams#testnet-crypto-streams). The below example executes the application, reading from the `ETH/USD` crypto stream:

   ```bash
   cargo run -- 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
   ```

   Expect output similar to the following in your terminal:

   ```bash
   Raw report data: Report { feed_id: 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782, valid_from_timestamp: 1734124400, observations_timestamp: 1734124400, full_report: "0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff1578000000000000000000000000000000000000000000000000000000004f56930f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba78200000000000000000000000000000000000000000000000000000000675ca37000000000000000000000000000000000000000000000000000000000675ca3700000000000000000000000000000000000000000000000000000174be1bd8758000000000000000000000000000000000000000000000000000cb326ce8c3ea800000000000000000000000000000000000000000000000000000000675df4f00000000000000000000000000000000000000000000000d3a30bcc15e207c0000000000000000000000000000000000000000000000000d3a1557b5e634060200000000000000000000000000000000000000000000000d3ab99a974ff10f400000000000000000000000000000000000000000000000000000000000000000292bdd75612560e46ed9b0c2437898f81eb0e18b6b902a161b9708e9177175cf3b8ef2b279f230f766fb29306250ee90856516ee349ca42b2d7fb141deb006745000000000000000000000000000000000000000000000000000000000000000221c156e80276827e1bfeb6542ab064dfa958f5be955f516fb62b1c93437472c31cc65fcaba68c9d661701190bc32025a0690af0eefe027ac218fd15c588dd4d5" }


   Decoded Report for Stream ID 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782:
   ------------------------------------------
   Observations Timestamp: 1734124400
   Benchmark Price       : 3904011708000000000000
   Bid                   : 3903888333211164500000
   Ask                   : 3904628100124598400000
   Valid From Timestamp  : 1734124400
   Expires At            : 1734210800
   Link Fee              : 3574678975954600
   Native Fee            : 25614677280600
   ------------------------------------------
   ```

   Your application has successfully decoded the report data.

   [Learn more about the decoded report details](#decoded-report-details).

#### Decoded report details

The decoded report details include:

| Attribute                | Value                                                                | Description                                                                                                                                                                                                                                                                                                                                  |
| ------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Stream ID`              | `0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782` | The unique identifier for the stream. In this example, the stream is for ETH/USD.                                                                                                                                                                                                                                                            |
| `Observations Timestamp` | `1734124400`                                                         | The timestamp indicating when the data was captured.                                                                                                                                                                                                                                                                                         |
| `Benchmark Price`        | `3904011708000000000000`                                             | The observed price in the report, with 18 decimals. For readability: `3,904.0117080000000` USD per ETH.                                                                                                                                                                                                                                      |
| `Bid`                    | `3903888333211164500000`                                             | The highest price a buyer is willing to pay for an asset, with 18 decimals. For readability: `3,903.8883332111645` USD per ETH. Learn more about the [Bid price](/data-streams/concepts/liquidity-weighted-prices). (For [DEX State Price streams](/data-streams/concepts/dex-state-price-streams), this value equals `Benchmark Price`.)    |
| `Ask`                    | `3904628100124598400000`                                             | The lowest price a seller is willing to accept for an asset, with 18 decimals. For readability: `3,904.6281001245984` USD per ETH. Learn more about the [Ask price](/data-streams/concepts/liquidity-weighted-prices). (For [DEX State Price streams](/data-streams/concepts/dex-state-price-streams), this value equals `Benchmark Price`.) |
| `Valid From Timestamp`   | `1734124400`                                                         | The start validity timestamp for the report, indicating when the data becomes relevant.                                                                                                                                                                                                                                                      |
| `Expires At`             | `1734210800`                                                         | The expiration timestamp of the report, indicating the point at which the data becomes outdated.                                                                                                                                                                                                                                             |
| `Link Fee`               | `3574678975954600`                                                   | The fee to pay in LINK tokens for the onchain verification of the report data. With 18 decimals. For readability: `0.003574678975954600` LINK. **Note:** This example fee is not indicative of actual fees.                                                                                                                                  |
| `Native Fee`             | `25614677280600`                                                     | The fee to pay in the native blockchain token (e.g., ETH on Ethereum) for the onchain verification of the report data. With 18 decimals. For readability: `0.0000256146772806000` ETH. **Note:** This example fee is not indicative of actual fees.                                                                                          |

For descriptions and data types of other report schemas, see the [Report Schema Overview](/data-streams/reference/report-schema-overview).

#### Payload for onchain verification

In this tutorial, you logged and decoded the `full_report` payloads to extract the report data. However, in a production environment, you should verify the data to ensure its integrity and authenticity.

Refer to the [Verify report data onchain](/data-streams/tutorials/evm-onchain-report-verification) tutorial to learn more.

## Adapting code for different report schema versions

When working with different versions of [Data Stream reports](/data-streams/reference/report-schema-overview), you'll need to adapt your code to handle the specific report schema version they use:

1. Import the correct schema version module. Examples:
   - For v3 schema (as used in this example):

     ```rust
      use chainlink_data_streams_report::report::{ decode_full_report, v3::ReportDataV3 };
     ```

   - For v8 schema:

     ```rust
     use chainlink_data_streams_report::report::{ decode_full_report, v8::ReportDataV8 };
     ```

2. Update the decode function to use the correct schema version. Examples:
   - For v3 schema (as used in this example):

     ```rust
     let report_data = ReportDataV3::decode(&report_blob)?;
     ```

   - For v8 schema:

     ```rust
     let report_data = ReportDataV8::decode(&report_blob)?;
     ```

3. Access fields according to the schema version structure. Refer to the [Report Schemas](/data-streams/reference/report-schema-overview) documentation for complete field references for each version.

## Explanation

### Initializing the API client and configuration

The API client is initialized in two steps:

1. [`Config::new`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/sdk/src/config.rs#L131) creates a configuration with your API credentials and endpoints. This function:
   - Validates your API key and secret
   - Sets up the REST API endpoint for data retrieval
   - Configures optional settings like TLS verification

2. [`Client::new`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/sdk/src/client.rs#L131) creates the HTTP client with your configuration. This client:
   - Handles authentication automatically
   - Manages HTTP connections
   - Implements retry logic for failed requests

### Fetching reports

The SDK provides several methods to fetch reports through the REST API:

1. Latest report: [`get_latest_report`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/sdk/src/client.rs#L165) retrieves the most recent report for a feed:
   - Takes a feed ID as input
   - Returns a single report with the latest timestamp
   - Useful for applications that need the most current data

2. Historical report: [`get_report`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/sdk/src/client.rs#L242) fetches a report at a specific timestamp:
   - Takes both feed ID and timestamp
   - Returns the report closest to the requested timestamp
   - Helpful for historical analysis or verification

Each API request automatically:

- Generates HMAC authentication headers
- Includes proper timestamps
- Handles HTTP response status codes
- Deserializes the JSON response into Rust structures

### Decoding reports

Reports are decoded in three stages:

1. Hex decoding: The `full_report` field comes as a hex string prefixed with "0x":

   ```rust
   let full_report = hex::decode(&response.report.full_report[2..])?;
   ```

2. Report separation: [`decode_full_report`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/report/src/report.rs#L83) splits the binary data:
   - Extracts the report context (metadata)
   - Isolates the report blob (actual data)
   - Validates the report format

3. Data extraction: [`ReportDataV3::decode`](https://github.com/smartcontractkit/data-streams-sdk/blob/main/rust/crates/report/src/report/v3.rs#L80) parses the report blob into structured data:
   - Benchmark price
   - Bid and ask prices for [liquidity-weighted pricing](/data-streams/concepts/liquidity-weighted-prices)
     - **Note:** For [DEX State Price streams](/data-streams/concepts/dex-state-price-streams), which also use the V3 schema, the `bid` and `ask` fields contain the same value as `benchmark_price`.
   - Fee information for onchain verification
   - Timestamp information

### Error handling

The example demonstrates Rust's robust error handling:

1. Type-safe errors:
   - Uses custom error types for different failure scenarios
   - Implements the `Error` trait for proper error propagation
   - Provides detailed error messages for debugging

2. Error propagation:
   - Uses the `?` operator for clean error handling
   - Converts errors between types when needed
   - Bubbles up errors to the main function

3. Input validation:
   - Checks command-line arguments
   - Validates environment variables
   - Verifies feed ID format

The decoded data can be used for further processing, analysis, or display in your application. For production environments, you must verify the data onchain using the provided `full_report` payload.