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:
- A free evmquery API key from app.evmquery.com/onboarding?plan=free.
requestsin Python (or nativefetchin Node).
No additional packages, no ABI files, no RPC credentials.
pip install requests # that's itThe REST endpoint:
POST https://api.evmquery.com/api/v1/queryx-api-key: YOUR_KEYContent-Type: application/jsonRead 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 osimport 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 readThe 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:
- Divide by
2^96to get the normalized square root. - Square it to get the raw price ratio (WETH raw units per USDC raw unit).
- Adjust for the decimal difference between the two tokens (USDC has 6 decimals, WETH has 18).
- 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_usdcRunning 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 osimport requestsfrom 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 conversionQ96 = Decimal(2**96)sqrt_price = Decimal(sqrt_price_x96) / Q96price_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: 25248715ETH price: $1730.39Tick: 201774Liquidity: 4,396,230,040,359,746,608Fee 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 precisionconst 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^2const 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 osimport 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)# 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640schema.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
- ERC-20 Balance Scanner in TypeScript — REST API patterns for multi-address, multi-chain balance reads
- Multicall3: Batch EVM Contract Reads — when to reach for Multicall3 vs the evmquery list expression
- Blockchain Monitoring in Python with evmquery — poll pool state on an interval and trigger alerts
- evmquery’s free tools — try the ones built for DeFi reads



