# Fetch and decode Data Streams reports using the TypeScript SDK
Source: https://docs.chain.link/data-streams/tutorials/ts-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/ts-sdk) for TypeScript to fetch and decode [reports](/data-streams/reference/report-schema-overview) from the Data Streams Aggregation Network. You'll set up your TypeScript project, retrieve reports, decode them, and log their attributes.

## 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](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) if necessary.
- **Node.js**: Make sure you have Node.js 20.0 or higher. You can check your current version by running node --version in your terminal and download the latest version from the official [Node.js website](https://nodejs.org/) if necessary.
- **TypeScript**: Make sure you have TypeScript 5.3 or higher. You can check your current version by running npx tsc --version in your terminal and install or update TypeScript by running npm install -g typescript if necessary.
- **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 TypeScript project, installing the SDK and pasting example code. This will let you decode reports for both single and multiple [streams](/data-streams/crypto-streams), logging their attributes to your terminal.

### Set up your TypeScript 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 Node.js project:

   ```bash
    npm init -y
   ```

3. Install the TypeScript SDK and other required packages:

   ```bash
    npm install @chainlink/data-streams-sdk dotenv
    npm install -D tsx
   ```

4. Set your API credentials:

   Option 1 - Environment variables:

   ```bash
   export API_KEY="your_api_key_here"
   export USER_SECRET="your_user_secret_here"
   ```

   Option 2 - `.env` file:

   ```bash
   # Create .env file
   touch .env

   # Add your credentials
   API_KEY="your_api_key_here"
   USER_SECRET="your_user_secret_here"
   ```

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

1. Create a new new TypeScript file, `singleStream.ts`, in your project directory:

   ```bash
   touch singleStream.ts
   ```

2. Insert the following code example and save your `singleStream.ts` file:

   ```typescript
   import { createClient, decodeReport, LogLevel, getReportVersion, formatReport } from "@chainlink/data-streams-sdk"
   import "dotenv/config"

   async function main() {
     if (process.argv.length < 3) {
       console.error("Please provide a feed ID as an argument")
       console.error(
         "Example: npx tsx singleStream.ts 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782"
       )
       process.exit(1)
     }

     const feedId = process.argv[2]
     const version = getReportVersion(feedId)

     try {
       const config = {
         apiKey: process.env.API_KEY || "YOUR_API_KEY",
         userSecret: process.env.USER_SECRET || "YOUR_USER_SECRET",
         endpoint: "https://api.testnet-dataengine.chain.link",
         wsEndpoint: "wss://ws.testnet-dataengine.chain.link",
         // Comment to disable SDK logging:
         logging: {
           logger: console,
           logLevel: LogLevel.INFO,
         },
       }

       const client = createClient(config)
       console.log(`\nFetching latest report for feed ${feedId} (${version})...\n`)

       // Get raw report data
       const report = await client.getLatestReport(feedId)
       console.log(`Raw Report Blob: ${report.fullReport}`)

       // Decode the report
       const decodedData = decodeReport(report.fullReport, report.feedID)

       // Combine decoded data with report metadata
       const decodedReport = {
         ...decodedData,
         feedID: report.feedID,
         validFromTimestamp: report.validFromTimestamp,
         observationsTimestamp: report.observationsTimestamp,
       }
       console.log(formatReport(decodedReport, version))
     } catch (error) {
       if (error instanceof Error) {
         console.error("Error:", error.message)
       } else {
         console.error("Unknown error:", error)
       }
       process.exit(1)
     }
   }

   main()
   ```

> **NOTE: Report detection**
>
> The TypeScript SDK automatically detects the report version based on the provided feed ID.

1. 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
   npx tsx singleStream.ts 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
   ```

   Expect output similar to the following in your terminal:

   ```bash
   Fetching latest report for feed 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 (V3)...

   [2025-09-23T00:09:49.042Z] [DataStreams] Request successful: GET https://api.testnet-dataengine.chain.link/api/v1/reports/latest?feedID=0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 - 200
   Raw Report Blob: 0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd690000000000000000000000000000000000000000000000000000000001f6f486000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba7820000000000000000000000000000000000000000000000000000000068d1e54c0000000000000000000000000000000000000000000000000000000068d1e54c00000000000000000000000000000000000000000000000000004539f757bc7c0000000000000000000000000000000000000000000000000034754304206ea30000000000000000000000000000000000000000000000000000000068f9724c0000000000000000000000000000000000000000000000e3e84d950bcd8d80000000000000000000000000000000000000000000000000e3e48f23626b5660000000000000000000000000000000000000000000000000e3eb7a12d8af1b00000000000000000000000000000000000000000000000000000000000000000002e7c71643e93efb8e759b1d1a8826579853d3aa2c96f59a2813e833a374c786f8f13a497a753af14c6b7329f704f148779b20aae62ed450167c61b9b5d8fcb0e100000000000000000000000000000000000000000000000000000000000000024905f1b4a6313246988a33d2aa923e3023065bbc8c874956aa5df25ec8b7b3e918a25b251713158aae6be85a188c1292e6b5288f2f96e75d632c4e6b659dfa53

   Report Metadata:
   Feed ID: 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
   Valid From: 1758586188
   Observations: 1758586188

   Decoded Data:
   Native Fee: 76115265174652
   LINK Fee: 14765629481447075
   Expires At: 1761178188
   Price: 4204150104000000000000
   Bid Price: 4203880326000000000000
   Ask Price: 4204378800000000000000
   --------------------------------------------------
   ```

   Your application has successfully fetched and decoded data for both streams.

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

### Fetch and decode reports for multiple streams

1. Create a new TypeScript file, `multipleStreams.ts`, in your project directory:

   ```bash
   touch multipleStreams.ts
   ```

2. Insert the following code example in your `multipleStreams.ts` file:

   ```typescript
   import { createClient, decodeReport, LogLevel, getReportVersion, formatReport } from "@chainlink/data-streams-sdk"
   import "dotenv/config"

   async function main() {
     if (process.argv.length < 3) {
       console.error("Please provide feed IDs as arguments")
       console.error("Get latest reports for multiple feeds:")
       console.error("  npx tsx multipleStreams.ts <feedID1> <feedID2> [feedID3...]")
       console.error("\nExample:")
       console.error(
         "  npx tsx multipleStreams.ts 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265"
       )
       process.exit(1)
     }

     const feedIds = process.argv.slice(2)

     try {
       const config = {
         apiKey: process.env.API_KEY || "YOUR_API_KEY",
         userSecret: process.env.USER_SECRET || "YOUR_USER_SECRET",
         endpoint: "https://api.testnet-dataengine.chain.link",
         wsEndpoint: "wss://ws.testnet-dataengine.chain.link",
         // Comment to disable SDK logging:
         logging: {
           logger: console,
           logLevel: LogLevel.INFO,
         },
       }

       const client = createClient(config)
       console.log(`\nFetching latest reports for ${feedIds.length} feed(s):`)
       feedIds.forEach((feedId) => {
         const version = getReportVersion(feedId)
         console.log(`- ${feedId} (${version})`)
       })
       console.log()

       // Get latest reports for each feed ID
       const reports = []
       for (const feedId of feedIds) {
         try {
           const report = await client.getLatestReport(feedId)
           reports.push(report)
         } catch (error) {
           console.error(`Failed to get report for ${feedId}:`, error)
           continue
         }
       }

       console.log(`Found ${reports.length} reports:\n`)

       // Process reports
       reports.forEach((report, index) => {
         const version = getReportVersion(report.feedID)
         console.log(`Raw Report Blob #${index + 1}: ${report.fullReport}`)

         try {
           // Decode the report
           const decodedData = decodeReport(report.fullReport, report.feedID)

           // Combine decoded data with report metadata
           const decodedReport = {
             ...decodedData,
             feedID: report.feedID,
             validFromTimestamp: report.validFromTimestamp,
             observationsTimestamp: report.observationsTimestamp,
           }
           console.log(formatReport(decodedReport, version))
         } catch (error) {
           console.error(`Failed to decode report for ${report.feedID}:`, error)
         }
       })
     } catch (error) {
       if (error instanceof Error) {
         console.error("Error:", error.message)
       } else {
         console.error("Unknown error:", error)
       }
       process.exit(1)
     }
   }

   main()
   ```

3. Before running the example, verify that your API credentials are still set in your current terminal session:

   ```bash
   echo $API_KEY
   echo $API_SECRET
   ```

   If the commands above don't show your credentials, set them again:

   ```bash
   export API_KEY="<YOUR_API_KEY>"
   export USER_SECRET="<YOUR_USER_SECRET>"
   ```

4. Read from two testnet crypto streams (ETH/USD and LINK/USD) by running:

   ```bash
   npx tsx multipleStreams.ts 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265
   ```

   Expect to see the output below in your terminal:

   ```bash
   [2025-09-24T01:50:28.313Z] [DataStreams] Data Streams client initialized

   Fetching latest report for feed 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 (V3)...

   [2025-09-24T01:50:28.607Z] [DataStreams] Request successful: GET https://api.testnet-dataengine.chain.link/api/v1/reports/latest?feedID=0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 - 200
   Raw Report Blob: 0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd690000000000000000000000000000000000000000000000000000000001fb09db000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba7820000000000000000000000000000000000000000000000000000000068d34e640000000000000000000000000000000000000000000000000000000068d34e64000000000000000000000000000000000000000000000000000045686a2caed300000000000000000000000000000000000000000000000000347613062ce6c40000000000000000000000000000000000000000000000000000000068fadb640000000000000000000000000000000000000000000000e34fc8e3afa8f400000000000000000000000000000000000000000000000000e34cf02d97047e60000000000000000000000000000000000000000000000000e3533bbd1e9ba3400000000000000000000000000000000000000000000000000000000000000000021ae965e613bfb4580ea819f8c12736562222e515b412054c49ec77ded163d9ee4493fb7dfb713181ea1a0d4e1a7fc4b7a3618484f9c989c4262c201e749df2cd000000000000000000000000000000000000000000000000000000000000000217c165a50d34910db8667c1229128ea4110fba082eb0308b75282fffdb594f68467797ce8a8b515f8e3ab08e4723a1fe1a9cfa5079968cd4bb7a9ec1a7a943cc

   Report Metadata:
   Feed ID: 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
   Valid From: 1758678628
   Observations: 1758678628

   Decoded Data:
   Native Fee: 76314760228563
   LINK Fee: 14766522869016260
   Expires At: 1761270628
   Price: 4193160000000000000000
   Bid Price: 4192954886000000000000
   Ask Price: 4193408500000000000000
   --------------------------------------------------
   ```

### Decoded report details

The decoded [crypto v3 report](/data-streams/reference/report-schema-v3) 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 | `1734216283`                                                         | The timestamp indicating when the data was captured.                                                                                                                                                                                                                                                                                         |
| Benchmark Price        | `3865052126782320350000`                                             | The observed price in the report, with 18 decimals. For readability: `3,865.0521267823204` USD per ETH.                                                                                                                                                                                                                                      |
| Bid                    | `3864985478146740000000`                                             | The highest price a buyer is willing to pay for an asset, with 18 decimals. For readability: `3,864.9854781467400` 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                    | `3865140837060103650000`                                             | The lowest price a seller is willing to accept for an asset, with 18 decimals. For readability: `3,865.1408370601037` 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   | `1734216283`                                                         | The start validity timestamp for the report, indicating when the data becomes relevant.                                                                                                                                                                                                                                                      |
| Expires At             | `1734302683`                                                         | The expiration timestamp of the report, indicating the point at which the data becomes outdated.                                                                                                                                                                                                                                             |
| Link Fee               | `3379350941986000`                                                   | The fee to pay in LINK tokens for the onchain verification of the report data. With 18 decimals. For readability: `0.03379350941986` LINK. **Note:** This example fee is not indicative of actual fees.                                                                                                                                      |
| Native Fee             | `25872872271800`                                                     | 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. **Note:** This example fee is not indicative of actual fees.                                                                                                                                        |

### 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.

## Explanation

### Initializing the client and configuration

The Data Streams TypeScript client is initialized in two steps:

1. Configure the client with a config object:

```typescript
const config = {
  apiKey: process.env.API_KEY || "YOUR_API_KEY",
  userSecret: process.env.USER_SECRET || "YOUR_USER_SECRET",
  endpoint: "https://api.testnet-dataengine.chain.link",
  wsEndpoint: "wss://ws.testnet-dataengine.chain.link",
  // Optional logging:
  logging: {
    logger: console,
    logLevel: LogLevel.INFO,
  },
}
```

The configuration requires:

- `apiKey` and `userSecret` for authentication (required)
- `endpoint` for the API endpoint (required)
- `logging` for debugging and error tracking (optional)

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

2. Create the client with `createClient`:

```typescript
const client = createClient(config)
```

The client handles:

- Authentication with HMAC signatures
- Connection management and timeouts
- Error handling and retries

### Fetching reports

The TypeScript SDK provides two main methods to fetch reports:

1. Latest report for a single stream with `getLatestReport`:

```typescript
const report = await client.getLatestReport(feedId)
```

- Takes a feed ID string
- Returns a single `ReportResponse` with the most recent data
- No timestamp parameter needed
- Useful for real-time price monitoring

2. Latest reports for multiple streams by calling `getLatestReport` in a loop:

```typescript
for (const feedId of feedIds) {
  const report = await client.getLatestReport(feedId)
  reports.push(report)
}
```

- Takes an array of feed ID strings
- Calls `getLatestReport` for each feed ID individually
- Returns the most recent data for each stream
- Useful for monitoring multiple assets simultaneously
- Each request gets the latest available data without timestamp constraints

Each API request automatically:

- Handles authentication with API credentials
- Manages request timeouts via the Node/SDK configuration
- Processes responses into structured types

### Decoding reports

Reports are decoded in two steps using the TypeScript SDK helper functions:

1. Report decoding with `decodeReport` (auto-detects report version by feed ID):

```typescript
const decodedData = decodeReport(report.fullReport, report.feedID)
```

This step:

1. Takes the raw `fullReport` bytes/string from the response
2. Uses the report schema that matches the feed (SDK detects version)
3. Validates the format and decodes into a structured object
4. Returns decoded data that can be combined with report metadata and formatted using `formatReport()`

For more details, see the [Report Format section of the SDK Reference](/data-streams/reference/data-streams-api/ts-sdk#report-format).

### Error handling

The TypeScript examples use standard try/catch patterns and optional timeouts:

1. Request timeouts / cancellation

Use your application's timeout/cancellation mechanism (for example, AbortController) when making requests to the SDK or wrap calls in a manual timeout.

2. Error checking

```typescript
try {
  const report = await client.getLatestReport(feedId)
} catch (err) {
  console.error("Failed to fetch report:", err)
  process.exit(1) // fatal error
}
```

- Fatal errors (client creation, missing credentials) typically exit the process
- Non-fatal errors (single report decode) can be logged and skipped when processing multiple feeds
- All errors should be logged with context for easier debugging

Learn more about SDK error handling in the [SDK Reference](/data-streams/reference/data-streams-api/ts-sdk#error-handling).

3. SDK logging

The SDK can log requests and responses when `logging` is enabled in the config. In the example above we pass `console` as the logger and set `LogLevel.INFO`.

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

For more information about SDK logging configuration and monitoring options, see the [SDK Reference](/data-streams/reference/data-streams-api/ts-sdk#observability-logs--metrics).

> > > > > > > c98d3598 (draft ts-sdk-fetch)