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

# OHLCV Pairs Query

export const OhlcvChartSnippet = ({addressType = "pair", fixedChain, pairOptions, mode = "subscription"}) => {
  const isQueryMode = mode === "query";
  const LOGO_URLS = {
    dark: "https://www.datocms-assets.com/86369/1720810597-goldrush-logo-powered-by-covalent-dark.png",
    light: "https://www.datocms-assets.com/86369/1720810603-goldrush-logo-powered-by-covalent-light.png"
  };
  const chainDefaults = {
    SOLANA_MAINNET: {
      pair: "8WwcNqdZjCY5Pt7AkhupAFknV2txca9sq6YBkGzLbvdt",
      token: "So11111111111111111111111111111111111111112"
    },
    BASE_MAINNET: {
      pair: "0xb94b22332ABf5f89877A14Cc88f2aBC48c34B3Df",
      token: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf"
    },
    BSC_MAINNET: {
      pair: "0xcF59B8C8BAA2dea520e3D549F97d4e49aDE17057",
      token: "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"
    },
    MONAD_MAINNET: {
      pair: "0x659bD0BC4167BA25c62E05656F78043E7eD4a9da",
      token: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A"
    },
    HYPERCORE_MAINNET: {
      pair: "xyz:GOLD-USDC",
      token: "GOLD"
    },
    MEGAETH_MAINNET: {
      pair: "0x587F6eeAfc7Ad567e96eD1B62775fA6402164b22",
      token: "REDSTONE-BTC"
    }
  };
  const isDarkTheme = () => {
    if (typeof document !== "undefined") {
      const root = document.documentElement;
      if (root.classList.contains("dark")) return true;
      const themeAttr = root.getAttribute("data-theme");
      if (themeAttr === "dark") return true;
    }
    if (typeof window !== "undefined" && window.matchMedia) {
      return window.matchMedia("(prefers-color-scheme: dark)").matches;
    }
    return false;
  };
  const normalizeArray = data => {
    if (Array.isArray(data)) return data;
    if (data) return [data];
    return [];
  };
  const formatPrice = price => {
    const value = Number.isFinite(price) ? price : 0;
    return `$${value.toFixed(4)}`;
  };
  const helperRef = React.useRef(null);
  const sessionRef = React.useRef(null);
  const [helperReady, setHelperReady] = React.useState(false);
  const initialChain = fixedChain || "HYPERCORE_MAINNET";
  const [chainName, setChainName] = React.useState(initialChain);
  const [address, setAddress] = React.useState(() => {
    if (pairOptions && pairOptions.length > 0) return pairOptions[0];
    const defaults = chainDefaults[initialChain] || ({});
    return addressType === "pair" ? defaults.pair || "" : defaults.token || "";
  });
  const [interval, setInterval] = React.useState("ONE_MINUTE");
  const [timeframe, setTimeframe] = React.useState("ONE_HOUR");
  const [status, setStatus] = React.useState("Idle");
  const [formError, setFormError] = React.useState("");
  const [latestEvent, setLatestEvent] = React.useState(null);
  const [candles, setCandles] = React.useState([]);
  const [volumes, setVolumes] = React.useState([]);
  const [chartTitle, setChartTitle] = React.useState("");
  const [customAddress, setCustomAddress] = React.useState(false);
  const chainOptions = React.useMemo(() => {
    const baseChains = ["SOLANA_MAINNET", "BASE_MAINNET", "BSC_MAINNET", "MONAD_MAINNET", "MEGAETH_MAINNET"];
    baseChains.push("HYPERCORE_MAINNET");
    return baseChains;
  }, [addressType]);
  const intervalOptions = React.useMemo(() => ["ONE_SECOND", "ONE_MINUTE", "ONE_HOUR"], []);
  const timeframeOptions = React.useMemo(() => ["ONE_MINUTE", "FIFTEEN_MINUTES", "ONE_HOUR", "ONE_DAY"], []);
  const styles = React.useMemo(() => ({
    panel: {
      border: "1px solid rgba(148, 163, 184, 0.4)",
      borderRadius: "0.9rem",
      padding: "1.5rem",
      background: "var(--mint-panel-bg, #fff)",
      boxShadow: "0 8px 24px rgba(15, 23, 42, 0.08)"
    },
    label: {
      fontSize: "0.85rem",
      fontWeight: 600,
      display: "flex",
      flexDirection: "column",
      gap: "0.25rem",
      color: "inherit"
    },
    input: {
      width: "100%",
      border: "1px solid var(--mint-border, #d1d5db)",
      borderRadius: "0.5rem",
      padding: "0.55rem 0.75rem",
      fontSize: "0.95rem",
      fontFamily: "inherit",
      background: "var(--mint-input-bg, var(--mint-panel-bg, #fff))",
      color: "var(--mint-foreground, #0f172a)"
    },
    select: {
      width: "100%",
      border: "1px solid var(--mint-border, #d1d5db)",
      borderRadius: "0.5rem",
      padding: "0.55rem 2.5rem 0.55rem 0.75rem",
      fontSize: "0.95rem",
      fontFamily: "inherit",
      backgroundColor: "var(--mint-input-bg, #fff)",
      color: "var(--mint-foreground, #0f172a)",
      cursor: "pointer",
      appearance: "none",
      backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M6 9L1 4h10z'/%3E%3C/svg%3E\")",
      backgroundRepeat: "no-repeat",
      backgroundPosition: "right 0.75rem center",
      backgroundSize: "12px 12px"
    },
    buttonBase: {
      borderRadius: "0.5rem",
      border: "none",
      fontWeight: 600,
      cursor: "pointer",
      fontSize: "0.9rem",
      padding: "0.55rem 1.25rem"
    },
    status: {
      fontSize: "0.85rem",
      fontWeight: 600,
      color: "inherit"
    },
    log: {
      background: isDarkTheme() ? "#000000" : "#000000",
      borderRadius: "0.75rem",
      color: "var(--mint-foreground, #e5e7eb)",
      padding: "1rem",
      fontSize: "0.8rem",
      fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas",
      marginTop: "1rem",
      border: "1px solid var(--mint-border, #1f2937)"
    },
    logViewport: {
      overflowY: "auto",
      overflowX: "auto",
      paddingRight: "0.35rem",
      background: "transparent"
    }
  }), []);
  const closeSession = React.useCallback(() => {
    if (sessionRef.current?.close) {
      sessionRef.current.close();
      sessionRef.current = null;
    }
  }, []);
  React.useEffect(() => {
    const attachHelper = () => {
      if (typeof window === "undefined") return false;
      if (window.goldrushStreamingHelper) {
        helperRef.current = window.goldrushStreamingHelper;
        setHelperReady(true);
        return true;
      }
      return false;
    };
    if (!attachHelper()) {
      const timer = setInterval(() => {
        if (attachHelper()) clearInterval(timer);
      }, 300);
      return () => {
        clearInterval(timer);
        closeSession();
      };
    }
    return closeSession;
  }, [closeSession]);
  const responseFieldName = React.useMemo(() => addressType === "pair" ? "ohlcvCandlesForPair" : "ohlcvCandlesForToken", [addressType]);
  const handleConnect = () => {
    setFormError("");
    if (!helperRef.current) {
      setFormError("Helper script is still loading. Please try again.");
      return;
    }
    const trimmedAddress = address.trim();
    if (!trimmedAddress) {
      const addressTypeLabel = addressType === "pair" ? "pair address" : "token address";
      setFormError(`Enter at least one ${addressTypeLabel}.`);
      return;
    }
    const escapedAddress = trimmedAddress.replace(/"/g, '\\"');
    closeSession();
    setLatestEvent(null);
    setCandles([]);
    setVolumes([]);
    setChartTitle("");
    const operationName = addressType === "pair" ? "ohlcvCandlesForPair" : "ohlcvCandlesForToken";
    const addressParam = addressType === "pair" ? "pair_addresses" : "token_addresses";
    const addressField = "pair_address";
    const operationKeyword = isQueryMode ? "query" : "subscription";
    const query = `${operationKeyword} {
      ${operationName}(
        chain_name: ${chainName}
        ${addressParam}: ["${escapedAddress}"]
        interval: ${interval}
        timeframe: ${timeframe}
      ) {
        chain_name
        ${addressField}
        interval
        timeframe
        timestamp
        open
        high
        low
        close
        volume
        volume_usd
        quote_rate
        quote_rate_usd
        base_token {
          contract_ticker_symbol
        }
        quote_token {
          contract_ticker_symbol
        }
      }
    }`;
    const runner = isQueryMode ? helperRef.current.runQuery : helperRef.current.startSubscription;
    sessionRef.current = runner({
      query,
      onStatus: value => setStatus(value),
      onEvent: payload => {
        setLatestEvent(payload);
        const items = normalizeArray(payload?.data?.[responseFieldName] || payload);
        if (items.length > 0) {
          const latest = items[items.length - 1];
          const baseSymbol = latest?.base_token?.contract_ticker_symbol;
          const quoteSymbol = latest?.quote_token?.contract_ticker_symbol;
          if (baseSymbol && quoteSymbol) {
            const nextTitle = `${baseSymbol} / ${quoteSymbol}`;
            setChartTitle(prev => prev === nextTitle ? prev : nextTitle);
          }
        }
        setCandles(prev => {
          const map = new Map(prev.map(entry => [entry.time, entry]));
          for (const item of items) {
            if (!item?.timestamp || typeof item.open !== "number" || typeof item.high !== "number" || typeof item.low !== "number" || typeof item.close !== "number") {
              continue;
            }
            const time = Math.floor(new Date(item.timestamp).getTime() / 1000);
            map.set(time, {
              time,
              open: item.open,
              high: item.high,
              low: item.low,
              close: item.close
            });
          }
          return Array.from(map.values()).sort((a, b) => a.time - b.time);
        });
        setVolumes(prev => {
          const map = new Map(prev.map(entry => [entry.time, entry]));
          for (const item of items) {
            if (!item?.timestamp || typeof item.volume_usd !== "number") {
              continue;
            }
            const time = Math.floor(new Date(item.timestamp).getTime() / 1000);
            map.set(time, {
              time,
              value: item.volume_usd,
              color: item.close >= item.open ? "rgba(34, 197, 94, 0.15)" : "rgba(239, 68, 68, 0.15)"
            });
          }
          return Array.from(map.values()).sort((a, b) => a.time - b.time);
        });
      },
      onError: message => setFormError(message)
    });
  };
  const handleDisconnect = () => {
    setFormError("");
    closeSession();
  };
  const handleClear = () => {
    setLatestEvent(null);
    setCandles([]);
    setVolumes([]);
    setChartTitle("");
  };
  const latestPayload = latestEvent?.data?.[responseFieldName];
  const formattedPayload = React.useMemo(() => {
    if (!latestPayload) return "";
    try {
      return JSON.stringify(latestPayload, null, 2);
    } catch (error) {
      console.warn("Failed to format latest payload", error);
      return "";
    }
  }, [latestPayload]);
  const primaryItemHeight = React.useMemo(() => {
    if (!latestPayload) return 0;
    const firstEntry = Array.isArray(latestPayload) ? latestPayload[0] : latestPayload;
    if (!firstEntry) return 0;
    try {
      const pretty = JSON.stringify(firstEntry, null, 2) || "";
      const lines = pretty.split("\n").length || 1;
      return lines * 18 + 64;
    } catch (error) {
      console.warn("Failed to measure latest payload", error);
      return 0;
    }
  }, [latestPayload]);
  const TradingViewChart = ({candles, volumes}) => {
    const containerRef = React.useRef(null);
    const chartRef = React.useRef(null);
    const seriesRef = React.useRef(null);
    const volumeSeriesRef = React.useRef(null);
    const stateRef = React.useRef({
      initialized: false,
      lastTime: null
    });
    const [libReady, setLibReady] = React.useState(typeof window !== "undefined" && !!window.LightweightCharts);
    React.useEffect(() => {
      if (libReady) return;
      if (typeof window === "undefined") return;
      if (window.LightweightCharts) {
        setLibReady(true);
        return;
      }
      if (window.__lwc_loading__) return;
      window.__lwc_loading__ = true;
      const script = document.createElement("script");
      script.src = "https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js";
      script.async = true;
      script.onload = () => setLibReady(true);
      script.onerror = () => console.warn("Failed to load Lightweight Charts library.");
      document.head.appendChild(script);
    }, [libReady]);
    React.useEffect(() => {
      if (!libReady || !containerRef.current || chartRef.current) return;
      const LightweightCharts = window.LightweightCharts;
      if (!LightweightCharts) return;
      const container = containerRef.current;
      const dark = isDarkTheme();
      const chart = LightweightCharts.createChart(container, {
        layout: {
          background: {
            type: "solid",
            color: dark ? "rgba(15, 23, 42, 0)" : "rgba(255, 255, 255, 0)"
          },
          textColor: dark ? "#e5e7eb" : "#0f172a"
        },
        grid: {
          vertLines: {
            color: dark ? "rgba(100, 116, 139, 0.25)" : "rgba(148, 163, 184, 0.12)"
          },
          horzLines: {
            color: dark ? "rgba(100, 116, 139, 0.25)" : "rgba(148, 163, 184, 0.12)"
          }
        },
        timeScale: {
          timeVisible: true,
          secondsVisible: true,
          borderColor: "rgba(148, 163, 184, 0.12)"
        },
        rightPriceScale: {
          borderVisible: true,
          borderColor: "rgba(148, 163, 184, 0.12)"
        },
        leftPriceScale: {
          visible: false,
          borderColor: "rgba(148, 163, 184, 0.12)"
        },
        localization: {
          priceFormatter: formatPrice
        },
        width: container.clientWidth,
        height: container.clientHeight
      });
      const series = chart.addCandlestickSeries({
        upColor: dark ? "#4ade80" : "#22c55e",
        downColor: dark ? "#f87171" : "#ef4444",
        wickUpColor: dark ? "#4ade80" : "#22c55e",
        wickDownColor: dark ? "#f87171" : "#ef4444",
        borderVisible: false,
        priceFormat: {
          type: "custom",
          minMove: 0.0001,
          formatter: formatPrice
        },
        priceScaleId: "right",
        scaleMargins: {
          top: 0.1,
          bottom: 0.3
        }
      });
      const volumeSeries = chart.addHistogramSeries({
        priceFormat: {
          type: "volume"
        },
        priceScaleId: "volume",
        scaleMargins: {
          top: 0.8,
          bottom: 0
        }
      });
      chart.priceScale("volume").applyOptions({
        scaleMargins: {
          top: 0.8,
          bottom: 0
        },
        position: "left",
        entireTextOnly: false
      });
      volumeSeries.applyOptions({
        lastValueVisible: false,
        priceLineVisible: true
      });
      chartRef.current = chart;
      seriesRef.current = series;
      volumeSeriesRef.current = volumeSeries;
      const onResize = () => {
        if (!chartRef.current || !containerRef.current) return;
        chartRef.current.applyOptions({
          width: containerRef.current.clientWidth,
          height: containerRef.current.clientHeight
        });
      };
      window.addEventListener("resize", onResize);
      return () => {
        window.removeEventListener("resize", onResize);
        if (chartRef.current) {
          chartRef.current.remove();
          chartRef.current = null;
          seriesRef.current = null;
          volumeSeriesRef.current = null;
        }
      };
    }, [libReady]);
    React.useEffect(() => {
      if (!seriesRef.current) return;
      if (!candles || candles.length === 0) {
        seriesRef.current.setData([]);
        stateRef.current = {
          initialized: false,
          lastTime: null
        };
        return;
      }
      if (!stateRef.current.initialized) {
        seriesRef.current.setData(candles);
        if (volumeSeriesRef.current && volumes && volumes.length > 0) {
          volumeSeriesRef.current.setData(volumes);
        }
        stateRef.current.initialized = true;
        stateRef.current.lastTime = candles[candles.length - 1].time;
        if (chartRef.current) {
          chartRef.current.timeScale().fitContent();
        }
        return;
      }
      const last = candles[candles.length - 1];
      if (stateRef.current.lastTime === last.time) {
        seriesRef.current.update(last);
      } else if (stateRef.current.lastTime == null || stateRef.current.lastTime < last.time) {
        seriesRef.current.update(last);
        stateRef.current.lastTime = last.time;
      } else {
        seriesRef.current.setData(candles);
        if (volumeSeriesRef.current && volumes && volumes.length > 0) {
          volumeSeriesRef.current.setData(volumes);
        }
        stateRef.current.lastTime = last.time;
      }
    }, [candles, volumes]);
    React.useEffect(() => {
      if (!volumeSeriesRef.current) return;
      if (!volumes || volumes.length === 0) {
        volumeSeriesRef.current.setData([]);
        return;
      }
      const last = volumes[volumes.length - 1];
      const lastCandleTime = candles && candles.length > 0 ? candles[candles.length - 1].time : null;
      if (lastCandleTime === last.time) {
        volumeSeriesRef.current.update(last);
      } else if (lastCandleTime == null || lastCandleTime < last.time) {
        volumeSeriesRef.current.update(last);
      } else {
        volumeSeriesRef.current.setData(volumes);
      }
    }, [volumes, candles]);
    const dark = isDarkTheme();
    return <div style={{
      position: "relative",
      width: "100%",
      height: 300,
      borderRadius: "0.75rem",
      overflow: "hidden",
      marginTop: "0.5rem",
      padding: "10px 10px 30px 10px",
      boxShadow: dark ? "inset 0 0 0 1px rgba(15, 23, 42, 0.9)" : "inset 0 0 0 1px rgba(148, 163, 184, 0.15)",
      background: dark ? "#000000" : "#ffffff"
    }}>
        <div ref={containerRef} style={{
      width: "100%",
      height: "100%",
      position: "relative",
      zIndex: 1
    }} />
        <div style={{
      position: "absolute",
      bottom: 6,
      left: "50%",
      transform: "translateX(-50%)",
      fontSize: "0.7rem",
      color: dark ? "#9ca3af" : "#6b7280",
      pointerEvents: "none"
    }}>
          Time (UTC)
        </div>
        <div style={{
      position: "absolute",
      top: "50%",
      right: 6,
      transform: "translateY(-50%) rotate(-90deg)",
      transformOrigin: "center",
      fontSize: "0.7rem",
      color: dark ? "#9ca3af" : "#6b7280",
      pointerEvents: "none"
    }}>
          Price
        </div>
        <img src={dark ? LOGO_URLS.dark : LOGO_URLS.light} alt="GoldRush Logo" style={{
      position: "absolute",
      top: "50%",
      left: "50%",
      transform: "translate(-50%, -50%)",
      width: "200px",
      height: "auto",
      opacity: 0.35,
      pointerEvents: "none",
      zIndex: 0
    }} />
      </div>;
  };
  const button = (label, style, handler) => <button type="button" style={{
    ...styles.buttonBase,
    ...style
  }} onClick={handler}>
      {label}
    </button>;
  const dark = isDarkTheme();
  return <div style={{
    ...styles.panel,
    ...dark && ({
      background: "#000000",
      borderColor: "rgba(15, 23, 42, 0.9)",
      color: "#e5e7eb"
    })
  }}>
      <style>{`
        @keyframes ohlcvFadeIn {
          0% { opacity: 0; transform: translateY(-4px); }
          100% { opacity: 1; transform: translateY(0); }
        }
        .ohlcv-select:hover {
          border-color: #9ca3af;
        }
        .ohlcv-select:focus {
          outline: none;
          border-color: var(--mint-accent, #FF4C8B);
          box-shadow: 0 0 0 3px rgba(255, 76, 139, 0.1);
        }
      `}</style>
      <div style={{
    marginBottom: "1rem"
  }}>
        <h3 style={{
    margin: 0,
    color: "inherit"
  }}>Try it live</h3>
      </div>
      {!helperReady ? <div style={{
    fontSize: "0.9rem"
  }}>
          Loading GoldRush helper…
        </div> : <>
          <div style={{
    display: "grid",
    gap: "1rem",
    gridTemplateColumns: "repeat(2, 1fr)"
  }}>
            {fixedChain ? <label style={styles.label}>
                Chain
                <input style={{
    ...styles.input,
    backgroundColor: "var(--mint-bg-muted, #f1f5f9)",
    cursor: "default"
  }} type="text" value={fixedChain} readOnly />
              </label> : <label style={styles.label}>
                Chain
                <select className="ohlcv-select" style={styles.select} value={chainName} onChange={event => {
    const nextChain = event.target.value;
    setChainName(nextChain);
    const defaults = chainDefaults[nextChain] || ({});
    setAddress(addressType === "pair" ? defaults.pair || "" : defaults.token || "");
  }}>
                  {chainOptions.map(chain => <option key={chain} value={chain}>
                      {chain}
                    </option>)}
                </select>
              </label>}
            <label style={styles.label}>
              <span style={{
    display: "flex",
    alignItems: "center",
    gap: "0.5rem"
  }}>
                {addressType === "pair" ? "Pair Address" : "Token Address"}
                {pairOptions && customAddress && <a href="#" onClick={e => {
    e.preventDefault();
    setCustomAddress(false);
    setAddress(pairOptions[0]);
  }} style={{
    fontSize: "0.75rem",
    fontWeight: 400
  }}>
                    ← back to presets
                  </a>}
              </span>
              {pairOptions && !customAddress ? <select className="ohlcv-select" style={styles.select} value={address} onChange={event => {
    if (event.target.value === "__custom__") {
      setCustomAddress(true);
      setAddress("");
    } else {
      setAddress(event.target.value);
    }
  }}>
                  {pairOptions.map(pair => <option key={pair} value={pair}>
                      {pair}
                    </option>)}
                  <option value="__custom__">Custom address…</option>
                </select> : <input style={styles.input} type="text" value={address} onChange={event => setAddress(event.target.value)} placeholder={pairOptions ? "Enter a pair address" : "0x..."} />}
            </label>
          </div>
          <div style={{
    display: "grid",
    gap: "1rem",
    gridTemplateColumns: "repeat(2, 1fr)",
    marginTop: "1rem"
  }}>
            <label style={styles.label}>
              Interval
              <select className="ohlcv-select" style={styles.select} value={interval} onChange={event => setInterval(event.target.value)}>
                {intervalOptions.map(item => <option key={item} value={item}>
                    {item}
                  </option>)}
              </select>
            </label>
            <label style={styles.label}>
              Timeframe
              <select className="ohlcv-select" style={styles.select} value={timeframe} onChange={event => setTimeframe(event.target.value)}>
                {timeframeOptions.map(item => <option key={item} value={item}>
                    {item}
                  </option>)}
              </select>
            </label>
          </div>
          {formError && <p style={{
    color: "#dc2626",
    marginTop: "0.85rem"
  }}>
              {formError}
            </p>}
          <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.75rem",
    marginTop: "1.25rem"
  }}>
            {button(isQueryMode ? "Run Query" : "Connect & Subscribe", {
    background: "var(--mint-accent, #FF4C8B)",
    color: "#fff",
    opacity: status === "Streaming" || status === "Loading" ? 0.5 : 1,
    cursor: status === "Streaming" || status === "Loading" ? "not-allowed" : "pointer"
  }, status === "Streaming" || status === "Loading" ? undefined : handleConnect)}
            {!isQueryMode && button("Disconnect", status === "Streaming" ? {
    background: "var(--mint-accent, #FF4C8B)",
    color: "#fff"
  } : {
    background: "#e2e8f0",
    color: "#0f172a"
  }, handleDisconnect)}
            {button("Clear Results", {
    background: "#0f172a",
    color: "#e2e8f0"
  }, handleClear)}
          </div>
          <div style={{
    marginTop: "1.35rem"
  }}>
            <span style={styles.status}>Status:</span>{" "}
            <span style={{
    fontSize: styles.status.fontSize
  }}>
              {status || "Idle"}
            </span>
          </div>
          <div style={{
    marginTop: "1.25rem"
  }}>
            <div style={{
    fontWeight: 600,
    color: "inherit",
    fontSize: "1rem",
    marginBottom: "0.35rem"
  }}>
              {chartTitle || "Candlestick Chart"}
            </div>
            <TradingViewChart candles={candles} volumes={volumes} />
          </div>
          <div style={styles.log}>
            {!formattedPayload ? <span style={{
    color: "#94a3b8"
  }}>
                {isQueryMode ? "Run the query to see results…" : "Waiting for streaming events…"}
              </span> : <div style={{
    ...styles.logViewport,
    minHeight: primaryItemHeight || undefined,
    maxHeight: primaryItemHeight || undefined
  }}>
                <pre key="latest" style={{
    margin: 0,
    animation: "ohlcvFadeIn 340ms ease-out",
    backgroundColor: "transparent"
  }}>
                  {formattedPayload}
                </pre>
              </div>}
          </div>
        </>}
    </div>;
};

The OHLCV Pairs query returns historical Open, High, Low, Close prices and Volume for <b>one or many token pairs</b> at configurable intervals as a one-shot response. Use this when you need a historical fetch rather than a live stream - the request and response shapes are identical to the [OHLCV Pairs Stream](/api-reference/streaming-api/subscriptions/ohlcv-pairs-stream) subscription. The `pair_addresses` input is limited to a <b>maximum of 5 addresses</b> per query.

<CardGroup cols={2}>
  <Card title="Credit Cost"> 1 per call </Card>

  <Card title="Supported Chains">
    * `BASE_MAINNET`
    * `BSC_MAINNET`
    * `ETH_MAINNET`
    * `HYPERCORE_MAINNET`
    * `HYPEREVM_MAINNET`
    * `MEGAETH_MAINNET`
    * `MONAD_MAINNET`
    * `POLYGON_MAINNET`
    * `SOLANA_MAINNET`
  </Card>
</CardGroup>

<Tip>
  Estimate your monthly cost for this API using the [Pricing Calculator](/pricing-calculator?endpoint=%2Fapi-reference%2Fstreaming-api%2Fqueries%2Fohlcv-pairs-query).
</Tip>

<OhlcvChartSnippet addressType="pair" mode="query" />

## Parameters

<ParamField body="chain_name" type="enum" required>
  Blockchain network to filter events

  Type: [`ChainName`](/api-reference/streaming-api/types/chain-name)
</ParamField>

<ParamField body="pair_addresses" type="array<string>" required>
  Array of pair addresses on supported DEXes and chains to track
</ParamField>

<ParamField body="interval" type="enum" required>
  Time interval between each OHLCV candle

  Type: [`OhlcvTimeInterval`](/api-reference/streaming-api/types/ohlcv-time-interval)
</ParamField>

<ParamField body="timeframe" type="enum" required>
  How far back to fetch historical OHLCV data

  Type: [`OhlcvTimeFrame`](/api-reference/streaming-api/types/ohlcv-time-frame)
</ParamField>

<ParamField body="limit" type="int" optional>
  Maximum number of items returned per stream message to control payload size
</ParamField>

## Query

You can query the `ohlcvCandlesForPair` endpoint to fetch a historical batch of OHLCV candles in a single response.

```graphql theme={null}
query {
  ohlcvCandlesForPair(
    chain_name: BASE_MAINNET
    pair_addresses: [
      "0x9c087Eb773291e50CF6c6a90ef0F4500e349B903"
      "0x4b0Aaf3EBb163dd45F663b38b6d93f6093EBC2d3"
    ]
    interval: ONE_MINUTE
    timeframe: ONE_HOUR
    limit: 1000
  ) {
    pair_address
    base_token {
      contract_name
      contract_address
      contract_decimals
      contract_ticker_symbol
    }
    volume
    timeframe
    high
    quote_rate
    low
    quote_rate_usd
    quote_token {
      contract_name
      contract_address
      contract_decimals
      contract_ticker_symbol
    }
    interval
    id
    volume_usd
    close
    chain_name
    open
    timestamp
  }
}
```

## Response Format

Here's an example of the response data structure:

```json theme={null}
{
  "data": {
    "ohlcvCandlesForPair": [
      {
        "chain_name": "BASE_MAINNET",
        "pair_address": "0x4b0Aaf3EBb163dd45F663b38b6d93f6093EBC2d3",
        "interval": "ONE_MINUTE",
        "timeframe": "ONE_HOUR",
        "timestamp": "2025-05-29T19:53:00Z",
        "open": 4113151.389790023,
        "high": 4196665.022060752,
        "low": 4113151.389790023,
        "close": 4196665.022060752,
        "volume": 325.7615900713698,
        "volume_usd": 0.2079461510855417,
        "quote_rate": 4196665.022060752,
        "quote_rate_usd": 0.0006319231563715365,
        "quote_token": {
          "contract_name": "Wrapped Ether",
          "contract_symbol": "WETH",
          "contract_address": "0x4200000000000000000000000000000000000006",
          "contract_decimals": 18
        },
        "base_token": {
          "contract_name": "Toshi",
          "contract_symbol": "TOSHI",
          "contract_address": "0xac1bd2486aaf3b5c0fc3fd868558b082a531b2b4",
          "contract_decimals": 18
        }
      }
    ]
  }
}
```

### Field Descriptions

<ResponseField name="pair_address" type="string">
  The address of the primary DEX pool with the most liquidity for the token
</ResponseField>

<ResponseField name="base_token" type="object">
  Information about the queried token

  Type: [`TokenContractMetadata`](/api-reference/streaming-api/types/token-contract-metadata)

  <Expandable title="properties">
    <ResponseField name="contract_name" type="string">
      Name of the token contract
    </ResponseField>

    <ResponseField name="contract_address" type="string">
      Address of the token contract
    </ResponseField>

    <ResponseField name="contract_decimals" type="int">
      Number of decimal places for the token
    </ResponseField>

    <ResponseField name="contract_ticker_symbol" type="string">
      Ticker symbol of the token
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="volume" type="float">
  Trading volume during the interval
</ResponseField>

<ResponseField name="timeframe" type="enum">
  The requested timeframe

  Type: [`OhlcvTimeFrame`](/api-reference/streaming-api/types/ohlcv-time-frame)
</ResponseField>

<ResponseField name="high" type="float">
  Highest price during the interval
</ResponseField>

<ResponseField name="quote_rate" type="float">
  Exchange rate between base and quote tokens
</ResponseField>

<ResponseField name="low" type="float">
  Lowest price during the interval
</ResponseField>

<ResponseField name="quote_rate_usd" type="float">
  USD value of the quote rate
</ResponseField>

<ResponseField name="quote_token" type="object">
  Information about the paired token of the primary DEX pool

  Type: [`TokenContractMetadata`](/api-reference/streaming-api/types/token-contract-metadata)

  <Expandable title="properties">
    <ResponseField name="contract_name" type="string">
      Name of the token contract
    </ResponseField>

    <ResponseField name="contract_address" type="string">
      Address of the token contract
    </ResponseField>

    <ResponseField name="contract_decimals" type="int">
      Number of decimal places for the token
    </ResponseField>

    <ResponseField name="contract_ticker_symbol" type="string">
      Ticker symbol of the token
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="interval" type="enum">
  The candle interval

  Type: [`OhlcvTimeInterval`](/api-reference/streaming-api/types/ohlcv-time-interval)
</ResponseField>

<ResponseField name="id" type="string">
  Unique identifier for this candle
</ResponseField>

<ResponseField name="volume_usd" type="float">
  Trading volume in USD
</ResponseField>

<ResponseField name="close" type="float">
  Closing price for the interval
</ResponseField>

<ResponseField name="chain_name" type="enum">
  The blockchain network where the token exists

  Type: [`ChainName`](/api-reference/streaming-api/types/chain-name)
</ResponseField>

<ResponseField name="open" type="float">
  Opening price for the interval
</ResponseField>

<ResponseField name="timestamp" type="string">
  Candle timestamp (ISO-8601)
</ResponseField>
