Uniswap V3 Pool Data Without the SDK: Price, Liquidity, and Fees in One REST Call

Read sqrtPriceX96, tick, liquidity, and fee tier from any Uniswap V3 pool with a single POST request. No SDK, no ABI files, no RPC provider required.

evmquery team··7 min read
Share
Uniswap V3 pool data via REST API — sqrtPriceX96 to ETH price in one call

Reading one value from a Uniswap V3 pool requires the @uniswap/v3-sdk, a web3 provider, an ABI import, and four constructor calls before you see a number. That is 300 KB of SDK, an RPC subscription, and about twenty lines of setup — to call slot0() on a contract that is sitting there, entirely readable.

evmquery reduces that to a single POST request. You send a pool address and a CEL expression; you get a decoded value back in milliseconds. No ABI, no provider, no SDK.

TL;DR

POST {"chain": "evm_ethereum", "schema": {"contracts": {"pool": {"address": "0x88e..."}}}, "expression": "pool.slot0().sqrtPriceX96"} to https://api.evmquery.com/api/v1/query with your API key. Convert the returned integer to a human price in four lines of Python. Free tier: no monthly cap.

What Uniswap V3 actually stores

Every Uniswap V3 pool is a single contract with a handful of public getters. The ones you usually care about:

Method Returns Meaning
slot0() struct sqrtPriceX96, current tick, protocol and LP fees
liquidity() uint128 Active in-range liquidity at the current tick
fee() uint24 Pool fee in hundredths of a basis point (500 = 0.05%)
token0() / token1() address The two tokens, sorted by address (lower = token0)

sqrtPriceX96 is the canonical price representation in Uniswap V3. It encodes sqrt(token1/token0) * 2^96 as a 160-bit integer. Everything downstream — price charts, position managers, liquidation monitors — derives from this one field.

The classic SDK approach instantiates a Pool object that fetches slot0, liquidity, and both token contracts, then exposes derived properties like token0Price. For a script that just needs the current ETH price or a pool’s liquidity depth, that is significant overhead.

Setup

Two things to get started:

  1. A free evmquery API key from app.evmquery.com/onboarding?plan=free.
  2. requests in Python (or native fetch in Node).

No additional packages, no ABI files, no RPC credentials.

pip install requests # that's it

The REST endpoint:

POST https://api.evmquery.com/api/v1/query
x-api-key: YOUR_KEY
Content-Type: application/json

Read sqrtPriceX96 in one call

The USDC/WETH 0.05% pool on Ethereum mainnet lives at 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640. It is the deepest single pool for the ETH/USD rate on-chain.

import os
import requests
resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {
"pool": {"address": "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"}
}
},
"expression": "pool.slot0().sqrtPriceX96",
},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
print(data["result"]["value"]) # e.g. "1904617700832983991655699751327701"
print(data["meta"]["blockNumber"]) # block at which this was read

The dot notation pool.slot0().sqrtPriceX96 accesses the sqrtPriceX96 field of the struct returned by slot0(). evmquery resolves the ABI, executes the call, and returns the decoded integer as a string.

Struct field access

Any method that returns a Solidity struct exposes its fields via dot notation in CEL expressions. pool.slot0().tick and pool.slot0().sqrtPriceX96 are both valid on the same slot0 return value.

Interpreting sqrtPriceX96: price math

sqrtPriceX96 is not a price you can display directly. It encodes sqrt(token1_units / token0_units) * 2^96. To recover the human-readable ETH/USD price:

  1. Divide by 2^96 to get the normalized square root.
  2. Square it to get the raw price ratio (WETH raw units per USDC raw unit).
  3. Adjust for the decimal difference between the two tokens (USDC has 6 decimals, WETH has 18).
  4. Invert to express price as USD per ETH.
from decimal import Decimal, getcontext
getcontext().prec = 50 # enough precision for Q96 math
def sqrtpricex96_to_eth_usd(
sqrt_price_x96: str,
token0_decimals: int = 6, # USDC
token1_decimals: int = 18, # WETH
) -> Decimal:
Q96 = Decimal(2**96)
sqrt_price = Decimal(sqrt_price_x96) / Q96
price_raw = sqrt_price * sqrt_price # WETH units per USDC unit
# Adjust for token decimals
price_weth_per_usdc = price_raw * Decimal(10**token0_decimals) / Decimal(10**token1_decimals)
# Invert: USD per ETH
return Decimal(1) / price_weth_per_usdc

Running this against the live data returns approximately $1,730 per ETH, consistent with the current market price.

token0 and token1 order matters

Uniswap V3 sorts tokens by address. For the USDC/WETH pool, USDC (0xA0b8…) has a lower address than WETH (0xC02a…), so USDC is token0 and WETH is token1. Swap the decimal arguments if you are working with a pool where the higher-value token is token0 (e.g. a WETH/USDT pool where WETH sorts lower).

Pull all metrics in one call

CEL list literals let you batch multiple contract calls into a single API request. One round trip, four values:

import os
import requests
from decimal import Decimal, getcontext
getcontext().prec = 50
EVMQUERY_API = "https://api.evmquery.com/api/v1/query"
POOL = "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640" # USDC/WETH 0.05%
resp = requests.post(
EVMQUERY_API,
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {"pool": {"address": POOL}}
},
"expression": "[pool.slot0().sqrtPriceX96, pool.slot0().tick, pool.liquidity(), pool.fee()]",
},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
sqrt_price_x96, tick, liquidity, fee = data["result"]["value"]
block = data["meta"]["blockNumber"]
# Price conversion
Q96 = Decimal(2**96)
sqrt_price = Decimal(sqrt_price_x96) / Q96
price_weth_per_usdc = (sqrt_price**2) * Decimal(10**6) / Decimal(10**18)
eth_price_usd = Decimal(1) / price_weth_per_usdc
print(f"Block: {block}")
print(f"ETH price: ${float(eth_price_usd):.2f}")
print(f"Tick: {tick}")
print(f"Liquidity: {int(liquidity):,}")
print(f"Fee tier: {int(fee) / 10_000:.2f}%")

Sample output:

Block: 25248715
ETH price: $1730.39
Tick: 201774
Liquidity: 4,396,230,040,359,746,608
Fee tier: 0.05%

The meta.totalCalls field in the response will show 3 (slot0 is counted twice, once for sqrtPriceX96 and once for tick), but it executes in a single HTTP round trip via on-chain batching.

For developers building TypeScript services, the same request works identically with fetch:

const resp = await fetch("https://api.evmquery.com/api/v1/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.EVMQUERY_API_KEY!,
},
body: JSON.stringify({
chain: "evm_ethereum",
schema: {
contracts: { pool: { address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640" } },
},
expression: "[pool.slot0().sqrtPriceX96, pool.slot0().tick, pool.liquidity(), pool.fee()]",
}),
});
const { result, meta } = await resp.json();
const [sqrtPriceX96, tick, liquidity, fee] = result.value;
// sqrtPriceX96 is a large integer string; parse with BigInt for precision
const Q96 = 2n ** 96n;
const sqrtBig = BigInt(sqrtPriceX96);
// price_raw = sqrtBig^2 / Q96^2 (WETH units per USDC unit)
// eth_price_usd numerator = Q96^2 * 10^12, denominator = sqrtBig^2
const ethPriceUsd = Number((Q96 * Q96 * 10n ** 12n) / (sqrtBig * sqrtBig));
console.log(`ETH price: $${ethPriceUsd.toFixed(2)} at block ${meta.blockNumber}`);
console.log(`Fee tier: ${Number(fee) / 10_000}%`);

The BigInt path (2n ** 96n) avoids floating-point precision loss on large sqrtPriceX96 values. This matters for pools with extreme price ratios.

If you are building production pipelines that regularly scan EVM contract state, the evmquery TypeScript REST API guide covers retry logic, pagination, and cross-chain queries.

Discover a pool address from its token pair

You do not need to look up pool addresses manually. The Uniswap V3 Factory exposes getPool(token0, token1, fee) and evmquery can call it with parameterized context variables:

import os
import requests
resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {
"factory": {"address": "0x1F98431c8aD98523631AE4a59f267346ea31F984"}
},
"context": {
"token0": "sol_address",
"token1": "sol_address",
"fee": "sol_int",
},
},
"context": {
"token0": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # USDC
"token1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", # WETH
"fee": 500, # 0.05%
},
"expression": "factory.getPool(token0, token1, fee)",
},
timeout=10,
)
resp.raise_for_status()
pool_address = resp.json()["result"]["value"]
print(pool_address)
# 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640

schema.context declares the type of each variable (sol_address, sol_int); the context object carries the runtime values. You can swap token addresses and fee tier (500, 3000, or 10000) to look up any pool at runtime — no hardcoded addresses in your code.

Multiple fee tiers

USDC/WETH has active pools at three fee tiers: 500 (0.05%), 3000 (0.3%), and 10000 (1%). The 0.05% pool typically carries 10-20x more liquidity. You can look up all three addresses with three Factory calls, then query the deepest one based on liquidity().

Supported chains

The same query structure works across all three supported chains. Swap the chain field and the pool address:

Chain chain value Uniswap V3 Factory
Ethereum evm_ethereum 0x1F98431c8aD98523631AE4a59f267346ea31F984
Base evm_base 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
BNB Chain evm_bnb_mainnet 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7

The expression and schema structure are identical across chains, which makes cross-chain price comparison straightforward. See the free tools for more DeFi-focused examples.

Comparing fee tiers: one request per pool

To compare the same pair across fee tiers, run two queries with different pool addresses and compare the liquidity() return values. Alternatively, Multicall3-style batching via evmquery list expressions lets you pack both reads into a single request if you know both pool addresses in advance.

Next steps

Share

Read your first Uniswap V3 pool in two minutes

A generous free tier — no credit card required. Grab an API key and run the Python snippet below.