> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decibel.trade/llms.txt
> Use this file to discover all available pages before exploring further.

# Read SDK

> Query market data, account state, and subscribe to real-time updates

<Info>
  A [Node API token](/quickstart/node-api-key) is required. Without one, requests return `401 Unauthorized`.
</Info>

## Purpose

* Fetch market data (prices, depth, trades, candlesticks) and account views (orders, positions, Trading Accounts).
* Works over REST and Aptos views. No signing or private keys required.

## When to use

* Use for dashboards, analytics, and read-heavy server/frontend use cases.
* Do not use for submitting transactions; use the Write SDK instead.

## Initialization

```ts theme={null}
import { DecibelReadDex, TESTNET_CONFIG } from "@decibeltrade/sdk";

const read = new DecibelReadDex(TESTNET_CONFIG, {
  // Required: SDK will send Authorization: Bearer <YOUR_NODE_API_KEY> on fullnode requests
  nodeApiKey: process.env.APTOS_NODE_API_KEY!,
  onWsError: (e) => console.warn("WS error", e), // optional
  onChainFallback: ({ method }) => console.warn(`${method} fell back to chain views`), // optional
});
```

## Main readers

* Markets: `read.markets.getAll()`, `getAllSpot()`, `getByName()`
* Spot asset contexts (24h stats + live mid per spot market): `read.spotAssetContexts.getAll()`
* Prices: `read.marketPrices.getAll()`, `getByName()`, `subscribeByName()`, `subscribeAllSpotMids()`
* Depth: `read.marketDepth.subscribeByName()`, `subscribeByAddr()`
* Trades: `read.marketTrades.getByName()`, `getByAddr()`, `subscribeByName()`, `subscribeByAddr()`
* Candlesticks: `read.candlesticks.getByName()`, `getByAddr()`, `subscribeByName()`, `subscribeByAddr()`
* Accounts and orders:
  * `read.accountOverview.getByAddr()`
  * `read.userFees.getByAddr()`
  * `read.userOpenOrders.getByAddr()` / `read.userOrderHistory.getByAddr()`
  * `read.userOrders.getOrder()` (single order by `orderId` / `clientOrderId`)
  * `read.userBulkOrders.getByAddr()` / `getStatus()` / `getFills()`
  * `read.userPositions.getByAddr()`
  * `read.userTradeHistory.getByAddr()`
  * `read.userSubaccounts.getByAddr()`
* Portfolio and leaderboard:
  * `read.portfolioChart.getByAddr()`
  * `read.leaderboard.getTopUsers()`
* Vaults and delegations:
  * `read.vaults.getUserOwned()` / `getAll()`
  * `read.delegations.getForSubaccount()`
* Funded First Trade:
  * `read.fundedFirstTrade.getEligibility()` / `getActiveTrial()`
  * `read.fundedFirstTrade.getTrialHistory()` / `getCampaignLocks()` (count-based pagination)
  * `read.fundedFirstTrade.subscribeByAddr()`
* Chain views: `read.tokenBalance()`, `read.fungibleAssetMetadata()` (symbol/decimals/name for any fungible-asset address, e.g. `secondary_collateral` entries), `read.spotMarketAssets()` (a spot market's base/quote asset addresses — not exposed by `/markets`)

## Examples

### Markets

```ts theme={null}
const markets = await read.markets.getAll();
const btc = await read.markets.getByName("BTC-USD");

// getAll() returns perp markets only. Pass includeSpot for both products
// (narrow rows with isSpotMarket / isPerpMarket), or use getAllSpot() for
// spot markets alone. On spot rows, sz_decimals is the base-asset decimals
// and px_decimals the quote-asset decimals; perp-only fields are zeroed.
import { isSpotMarket } from "@decibeltrade/sdk";
const allMarkets = await read.markets.getAll({ includeSpot: true });
const spotMarkets = await read.markets.getAllSpot();
```

### Spot on the shared order endpoints

The order endpoints serve perp and spot on shared routes, with each row tagged
`asset_type` (`"perp"` | `"spot"`). The account-scoped list readers
(`userOpenOrders`, `userOrderHistory`, `userTradeHistory`, `userBulkOrders`,
and `userBulkOrders.getFills`) default to `assetType: "perp"`, so existing
perp consumers keep their exact pre-spot responses and pagination. Pass
`"spot"` to scope to spot, or `"all"` to receive both products merged
(server-side union pagination):

```ts theme={null}
const perpOnly = await read.userOpenOrders.getByAddr({ subAddr });
const spotOnly = await read.userOrderHistory.getByAddr({ subAddr, assetType: "spot" });
const merged = await read.userOpenOrders.getByAddr({ subAddr, assetType: "all" });
const spotFills = await read.userTradeHistory.getByAddr({ subAddr, assetType: "spot" });

// Single order by orderId (perp + spot) or clientOrderId (perp only).
// assetType defaults to unset here: the API checks perp, then spot.
const order = await read.userOrders.getOrder({ subAddr, market, orderId: "42" });

// Bulk orders: placement status by sequence number, and fills
const status = await read.userBulkOrders.getStatus({ subAddr, market, sequenceNumber: 7 });
const fills = await read.userBulkOrders.getFills({ subAddr, assetType: "all" });
```

Spot trade rows also carry `fee_asset`, the FA address `fee_amount` is
denominated in (base asset for the buyer, quote for the seller); it is absent
on perp rows, where fees are implicitly USDC.

### Prices and candlesticks

```ts theme={null}
const price = await read.marketPrices.getByName("BTC-USD");

// Subscribe (unsubscribe by calling the returned function)
const unsubscribe = read.marketPrices.subscribeByName("BTC-USD", (msg) => {
  console.log("Price update", msg);
});

// Spot mids: one row per spot market, mid is null unless both book sides
// have liquidity, last_trade_price is null until the first fill
const unsubscribeSpot = read.marketPrices.subscribeAllSpotMids((msg) => {
  console.log("Spot mids", msg.mids);
});

// Candlesticks
import { CandlestickInterval } from "@decibeltrade/sdk";
const candles = await read.candlesticks.getByName({
  marketName: "BTC-USD",
  interval: CandlestickInterval.OneMinute,
  startTime: Date.now() - 60 * 60 * 1000,
  endTime: Date.now(),
});
```

Name-based market-data methods (`candlesticks`, `marketDepth`, `marketTrades`)
derive the market address from the name, which differs between perp and spot
markets even when they share a name. They accept an optional `assetType`
(`"perp"` default, or `"spot"`):

```ts theme={null}
const spotCandles = await read.candlesticks.getByName({
  marketName: "APT/USDC",
  assetType: "spot",
  interval: CandlestickInterval.OneMinute,
  startTime: Date.now() - 60 * 60 * 1000,
  endTime: Date.now(),
});
const unsubTrades = read.marketTrades.subscribeByName("APT/USDC", onTrades, "spot");
const unsubDepth = read.marketDepth.subscribeByName("APT/USDC", 1, onDepth, "spot");

// If you already hold the market address, skip derivation entirely — the
// address encodes the product, so no assetType is needed
const tradesByAddr = await read.marketTrades.getByAddr({ marketAddr: market.market_addr });
const unsubTradesByAddr = read.marketTrades.subscribeByAddr(market.market_addr, onTrades);
const unsubDepthByAddr = read.marketDepth.subscribeByAddr(market.market_addr, 1, onDepth);
const byAddr = await read.candlesticks.getByAddr({
  marketAddr: market.market_addr,
  interval: CandlestickInterval.OneMinute,
  startTime: Date.now() - 60 * 60 * 1000,
  endTime: Date.now(),
});
const unsubCandles = read.candlesticks.subscribeByAddr(
  market.market_addr,
  CandlestickInterval.OneMinute,
  onCandle,
);
```

### Account views

```ts theme={null}
const ownerAddr = "0x...owner";
const subs = await read.userSubaccounts.getByAddr({ ownerAddr });
const overview = await read.accountOverview.getByAddr({ subAddr: subs[0].subaccount_addr });

// Spot inventory: PFS holdings with cost-basis PnL, open-order escrow
// reservations, and lifetime spot metrics. Null for wallet-only owners;
// absent on API versions that predate spot support.
overview.spot?.positions; // [{ asset_addr, asset_symbol, amount, usd_value, entry_notional_usd, unrealized_pnl_usd }]
overview.spot?.in_flight_orders; // escrow reserved by open spot orders
overview.spot?.metrics; // cumulative volume / fees / realized PnL (USD)
```

### User fees

```ts theme={null}
const subAddr = "0x....subaccount";
const fees = await read.userFees.getByAddr({ subAddr });
// fees.user_taker_rate, fees.user_maker_rate, fees.fee_tier (decimals; 0.00045 = 0.045%)
// fees.fee_schedule.tiers.vip[], fees.daily_user_volume[]
```

### User positions

```ts theme={null}
const subAddr = "0x....subaccount";

const stopPositions = read.userPositions.subscribeByAddr(subAddr, (data) => {
  data.positions.forEach((position) => {
    console.log("Position delta", position.market_name, position.open_size);
  });
});

// Stop streaming for this subaccount
stopPositions();
```

## Notes

* For raw REST/WS endpoints, see the API Reference tabs.
