Guides

Daily DEX OHLC and VWAP for a token, with exact fractions

Query daily DEX OHLC prices and VWAP from the Data API, handle exact rational fractions in TypeScript and Python, and backfill historical data efficiently.

What the DEX Daily Prices dataset is

BlockVectra's DEX dataset indexes decentralized exchange trade activity and computes aggregated daily pricing metrics. The DEX daily prices endpoint (getDexPrices) provides the daily volume-weighted average price (VWAP), price indicators (fields first_price, last_price, min_price, and max_price), and volume metrics for a specified token over a given date range.

Availability of this dataset varies across networks; chains providing this dataset are subject to the Supported Chains page.

This endpoint is not paginated: all matching daily rows within the requested date span are returned directly in data, and next_cursor is never present. If your application requires granular swap transactions rather than daily aggregates, use GET /{chain}/dex/swaps (see the Data API Reference).

Request parameters and specification limits

The endpoint route is GET https://dev-api.blockvectra.network/v1/data/{chain}/dex/prices. All requests require authentication by supplying your API key in the x-api-key header.

The endpoint accepts the following query parameters:

ParameterLocationTypeRequiredDescription
chainpathstringYesChain identifier, e.g. robinhood_mainnet
tokenquerystringYes20-byte base token address, 0x optional, either case
quotequerystringNoOptional 20-byte quote token address to restrict to a single base/quote pair
fromquerydate stringYesUTC start date, inclusive, YYYY-MM-DD
toquerydate stringYesUTC end date, inclusive, YYYY-MM-DD. to - from must be <= 90 days

Specification constraints and error codes

When a request violates specification constraints, the API returns a structured error body {"error":{"code","message"}}:

  • HTTP 400 (bad_request): Missing required query parameters (token, from, or to), invalid token/quote address syntax, invalid YYYY-MM-DD calendar dates, or from is after to.
  • HTTP 409 (span_exceeded): to - from is more than 90 days.
  • HTTP 404 (unknown_chain): {chain} is not a chain listed by GET /chains.
  • HTTP 422 (no_coverage): The chain does not support the dex_prices dataset capability.
  • HTTP 503 (unavailable): Service temporarily unavailable; retry according to the Retry-After header.

Request examples

The following examples query daily DEX prices for a base token across September 2026:

curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/dex/prices?token=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&from=2026-09-01&to=2026-09-30" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

Detailed field reference

Each entry in data represents aggregated daily DEX metrics for the token pair on that UTC date:

Token and quote assets

  • day (string): UTC date in YYYY-MM-DD format.
  • token (string): 20-byte base token address in lowercase 0x-prefixed hex.
  • token_symbol (string or null): Base token symbol.
  • token_name (string or null): Base token display name.
  • quote_token (string): Quote asset address. The all-zero address (0x0000000000000000000000000000000000000000) represents native ETH as the quote asset.
  • quote_symbol (string or null): Quote asset symbol ("ETH" when quote_token is the all-zero address).
  • quote_name (string or null): Quote asset display name ("Ether" when quote_token is the all-zero address).
  • base_decimals (integer or null): Base token decimals (0–255).
  • quote_decimals (integer or null): Quote asset decimals (18 when quote_token is the all-zero address).

Volume and trade counts

  • swap_count (integer): Swap count for this row.
  • base_volume_raw (string): Atomic base volume as an unsigned integer decimal string (UInt256String).
  • quote_volume_raw (string): Atomic quote volume as an unsigned integer decimal string (UInt256String).
  • base_volume (string or null): Human-readable base token volume scaled by base_decimals as a DecimalString; null when base_decimals is unknown.
  • quote_volume (string or null): Quote volume scaled by quote decimals as a DecimalString, or null.

Price indicators and VWAP

  • vwap (string or null): Volume-weighted average price as a DecimalString, or null.
  • first_price (string or null): First price indicator as a DecimalString, or null.
  • last_price (string or null): Last price indicator as a DecimalString, or null.
  • min_price (string or null): Minimum price indicator as a DecimalString, or null.
  • max_price (string or null): Maximum price indicator as a DecimalString, or null.

Exact fraction fields

  • first_price_numerator / first_price_denominator (string): Exact integer numerator and denominator for first_price (UInt256String).
  • last_price_numerator / last_price_denominator (string): Exact integer numerator and denominator for last_price (UInt256String).
  • min_price_numerator / min_price_denominator (string): Exact integer numerator and denominator for min_price (UInt256String).
  • max_price_numerator / max_price_denominator (string): Exact integer numerator and denominator for max_price (UInt256String).
  • refreshed_at (string): Refresh timestamp for this row (ISO-8601 UTC timestamp).

Envelope metadata (meta)

  • chain: Chain identifier.
  • chain_slug: Canonical uppercase chain slug.
  • chain_external_id: CAIP-2 formatted chain identifier.
  • as_of_block: The indexed head block number from which this response's finality watermark was computed (reported by this dataset, not checked against request parameters).
  • finalized_block: Reorg-safety watermark block number (not consensus finality).
  • coverage: Coverage classification (reports "full" for this endpoint).
  • refreshed_at: Metadata refreshed timestamp.

Why prices use exact numerators and denominators

On-chain DEX pricing originates from Automated Market Maker (AMM) liquidity pool reserve ratios or swap formulas.

Standard JSON numbers rely on IEEE-754 double-precision floats, which present precision limitations:

  1. Floating-point truncation and drift: Float64 values provide only 53 bits of precision, and dividing token quantities yields rounding drift that compounds across calculations.
  2. Transport safety: Formatting values as decimal strings (UInt256String) ensures numbers travel across HTTP without losing precision in JSON parsers.

By providing the exact integer numerator and denominator for price indicators, BlockVectra enables exact mathematical calculations without floating-point conversion. Quantitative models, arbitrage monitors, and financial accounting systems can evaluate prices and ratios without floating-point inaccuracies.

Handling exact fractions in TypeScript (BigInt)

In TypeScript, you can use native BigInt for cross-multiplication comparisons and fixed-point conversions without floating-point conversion:

interface DexDailyPrice {
  first_price_numerator: string;
  first_price_denominator: string;
  last_price_numerator: string;
  last_price_denominator: string;
}

// 1. Ratio comparison without floating-point conversion: check if close price is higher than open price
// a / b > c / d  is equivalent to  a * d > c * b
export function isPriceUp(row: DexDailyPrice): boolean {
  const openNum = BigInt(row.first_price_numerator);
  const openDen = BigInt(row.first_price_denominator);
  const closeNum = BigInt(row.last_price_numerator);
  const closeDen = BigInt(row.last_price_denominator);

  return closeNum * openDen > openNum * closeDen;
}

// 2. Convert fraction to a fixed-point decimal string with arbitrary scale (without floating-point loss)
export function fractionToFixedString(
  numeratorStr: string,
  denominatorStr: string,
  decimals = 18
): string {
  const num = BigInt(numeratorStr);
  const den = BigInt(denominatorStr);
  if (decimals === 0) {
    return (num / den).toString();
  }
  const scaleFactor = 10n ** BigInt(decimals);

  const scaled = (num * scaleFactor) / den;
  const intPart = scaled / scaleFactor;
  const remainder = scaled % scaleFactor;
  const fracPart = remainder.toString().padStart(decimals, "0");

  return `${intPart}.${fracPart}`;
}

Handling exact fractions in Python

Python provides standard library modules built specifically for rational and decimal calculations: fractions.Fraction and decimal.Decimal.

from decimal import Decimal, getcontext
from fractions import Fraction

# 1. Exact rational calculations with fractions.Fraction
open_price = Fraction(
    int(row["first_price_numerator"]),
    int(row["first_price_denominator"])
)
close_price = Fraction(
    int(row["last_price_numerator"]),
    int(row["last_price_denominator"])
)

# Exact price delta without floating-point rounding error
price_delta = close_price - open_price
print(f"Price delta (fraction): {price_delta}")

if open_price != 0:
    percentage_change = (price_delta / open_price) * 100
    print(f"Percentage change: {float(percentage_change):.4f}%")

# 2. Arbitrary-precision decimal arithmetic with decimal.Decimal
getcontext().prec = 50

if int(row["first_price_denominator"]) != 0:
    open_decimal = Decimal(row["first_price_numerator"]) / Decimal(row["first_price_denominator"])
    print(f"High-precision open: {open_decimal}")

Backfilling one year of daily prices

To backfill a year of data (365 days) within the 90-day span limit, divide the full date range into consecutive windows of at most 90 days and issue chunked requests:

interface DateSpan {
  from: string;
  to: string;
}

/**
 * Split a large date range into consecutive spans of at most maxDays (default: 90)
 */
export function splitDateRange(startDateStr: string, endDateStr: string, maxDays = 90): DateSpan[] {
  const spans: DateSpan[] = [];
  let currentStart = new Date(startDateStr);
  const end = new Date(endDateStr);

  while (currentStart <= end) {
    const chunkEnd = new Date(currentStart);
    chunkEnd.setUTCDate(chunkEnd.getUTCDate() + (maxDays - 1));
    const effectiveEnd = chunkEnd < end ? chunkEnd : end;

    spans.push({
      from: currentStart.toISOString().slice(0, 10),
      to: effectiveEnd.toISOString().slice(0, 10),
    });

    const nextStart = new Date(effectiveEnd);
    nextStart.setUTCDate(nextStart.getUTCDate() + 1);
    currentStart = nextStart;
  }

  return spans;
}

/**
 * Backfill token daily prices across multiple 90-day chunks
 */
export async function backfillTokenDailyPrices(
  chain: string,
  token: string,
  startDate: string,
  endDate: string,
  apiKey: string
) {
  const chunks = splitDateRange(startDate, endDate, 90);
  const allDailyPrices = [];

  for (const chunk of chunks) {
    const url = new URL(`https://dev-api.blockvectra.network/v1/data/${chain}/dex/prices`);
    url.searchParams.set("token", token);
    url.searchParams.set("from", chunk.from);
    url.searchParams.set("to", chunk.to);

    const res = await fetch(url, {
      headers: { "x-api-key": apiKey },
    });

    if (!res.ok) {
      throw new Error(`Failed to fetch span ${chunk.from}..${chunk.to}: HTTP ${res.status}`);
    }

    const json = await res.json();
    allDailyPrices.push(...json.data);
  }

  return allDailyPrices;
}

Capacity and CU usage calculations

Every Data API endpoint meters consumption in Compute Units (CU). The per-call CU weight for data.dex_prices and the estimated consumption for token backfills are calculated below. All figures are computed at build time from active platform plan data:

Method Weightdata.dex_prices: 15 CU / call
  • Backfilling 1 year of daily prices for 200 tokens: with a max span of 90 days per request, covering 365 days takes 5 chunks per token, totaling 1,000 calls. Total consumption is 15,000 CU (approx 0.1% of the free plan cycle allowance), about $0.0015 at list price.
  • Daily maintenance (refreshing 200 tokens once per day): 200 calls/day (3,000 CU/day), totaling approximately 6,000 calls per 30-day cycle (90,000 CU, approx 0.3% of the free quota), about $0.009/month at list price.

When scaling your backfill volume or requiring higher request concurrency, top up your account balance in the Console to upgrade to a paid account. For active rates and unit conversions, see the Pricing page.

On this page