Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dexpaprika.com/llms.txt

Use this file to discover all available pages before exploring further.

DexPaprika streams parsed pool reserves over Server-Sent Events. Every time a swap or liquidity change lands in a pool you subscribed to, you receive a reserve_update event containing the current reserve and delta for each token in the pool, the current USD price, the USD value of the change, and the block number. No polling. No log decoding. No competitor offers this as a push feed.

The DexPaprika differentiator

DexPaprika is the only provider that streams parsed pool reserves as push events. Every block, every pool, every token: current reserve, delta since last block, current USD price, USD value of the change. No polling. No log decoding. No DEX-specific contract integrations. Just connect and consume.

What you get

Every time a swap, add-liquidity, or remove-liquidity transaction lands in a pool you subscribed to, DexPaprika emits a reserve_update event containing:
  • The chain and pool the change happened in
  • The block number the change was observed in
  • For each token in the pool:
    • Current reserve (raw on-chain integer)
    • Delta since the previous block (raw on-chain integer)
    • Current USD price
    • Current reserve value in USD
    • USD value of this block’s delta
  • Total reserve value of the pool in USD
  • Total USD delta across all tokens this block
That last value, total_delta_usd, tells you in one number how much liquidity moved through the pool this block. Positive means net inflow, negative means net outflow. No reduction, no aggregation, no waiting.

Why this is unique

Real-time pool reserves are easy to describe and hard to ship. Every other approach in the market makes you do most of the work.

How competitors handle this today

Subgraphs index DEX events into queryable schemas, and are most commonly consumed as polling GraphQL queries. Push subscriptions exist in some gateway configurations but aren’t the default consumption pattern most teams build against. Latency in the typical setup is bounded below by your poll interval. For 1,000 pools and 1-second polling that is 60,000 requests per minute, every minute, forever.
These providers stream raw blockchain events. You receive a Sync(uint112, uint112) log for Uniswap V2, a Swap(...) log with internal balance math for Uniswap V3, an entirely different shape for Curve, Balancer, Solana DEXes. To turn that into “pool X now has Y USDC and Z WETH” you write a contract-by-contract decoder, maintain it across forks, handle reorgs, and reconcile reserve state from emitted deltas. That is tens of thousands of lines of code per DEX integration before you ever see a single USD-denominated reserve value.
A handful of providers offer token-price websockets. They push “USDC is 1.00"inrealtime.Toourknowledge,theydontsurfacepoollevelreservedynamicsaspushevents:youcanseethatUSDCspricemoved,butnotthatpoolXjustshed1.00" in real time. To our knowledge, they don't surface pool-level reserve dynamics as push events: you can see that USDC's price moved, but not that pool X just shed 4M in WETH liquidity this block.
Aggregator APIs are quote-driven, not stream-driven. You request a swap quote, you get pool depth as a side effect. You cannot subscribe.
These are query-and-dashboard platforms. Their freshness is measured in minutes. Real-time is not the product.

How DexPaprika handles it

We do the contract decoding, the cross-DEX reserve reconciliation, the price attachment, and the USD denomination on our side. You get a single SSE stream. The events look the same whether the underlying pool is Uniswap V2, V3, V4, Curve, Balancer, Aerodrome, Raydium, or Orca.
ProviderReserves push eventsBlock-level granularityUSD-denominated deltasMulti-DEX abstractionZero log decoding
DexPaprika StreamingYesYesYesYesYes
Subgraphs (The Graph)No (poll)YesSometimesPer-subgraphNo
Alchemy / Infura / QuickNodeRaw logs onlyYesNoNoNo
BirdeyeToken prices onlyToken-levelNoN/AN/A
Dune / DefillamaNo (dashboards)Minute-scaleYesYesYes
GeckoTerminalNoN/AN/AN/AN/A
BitqueryGraphQL subscriptions on raw eventsYesLimitedLimitedPartial
DexPaprika is the only row that says yes across all five.

The two subscription methods

The /sse/reserves endpoint accepts two method values. Pick the one that matches what you are actually trying to monitor.

pool_reserves

Subscribe to one specific pool. You receive an event every time a swap or liquidity change lands in that pool’s smart contract. Use this when you care about a particular pair on a particular DEX (a Uniswap V3 USDC/WETH pool, a Curve 3pool position, a Raydium AMM you provide liquidity to).

token_reserves

Subscribe to one specific token. You receive an event every time any pool containing that token sees a reserve change. Use this when you care about a token’s entire on-chain ecosystem at once: total liquidity across all pools, where volume is concentrated, where it’s draining.

When to pick which

Pick pool_reserves if you are: an LP watching your specific position, an arbitrage bot focused on a single venue, a slippage estimator for a particular trade route, a Uniswap V4 hook integrator. Pick token_reserves if you are: a treasury monitoring a stablecoin you issue, a token issuer watching liquidity migration across venues, an analytics platform aggregating TVL per token, a risk system watching for sudden drains anywhere a token trades. You can run both subscriptions on the same connection. A multi-asset POST stream can mix pool_reserves and token_reserves entries freely.

What an event looks like

Live capture from the production stream:
{
  "chain": "ethereum",
  "pool_id": "0xe0554a476a092703abdb3ef35c80e0d76d32939f",
  "block": 25098911,
  "tokens": [
    {
      "token_id": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "reserve": 1897184450989,
      "delta": -4915987385,
      "price_usd": 1.0000070290769338,
      "reserve_usd": 1897197.78,
      "delta_usd": -4916.02
    },
    {
      "token_id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "reserve": 1575557282142409643389,
      "delta": 2180365462384895407,
      "price_usd": 2255.21,
      "reserve_usd": 3553216.99,
      "delta_usd": 4917.19
    }
  ],
  "total_reserve_usd": 5450414.77,
  "total_delta_usd": 1.16
}
event: reserve_update
Reading this top to bottom:
  • The change happened on Ethereum, in pool 0xe055...939f (Uniswap V3 USDC/WETH), at block 25098911.
  • Token 0 is USDC. Its reserve dropped by 4,915,987,385 raw units this block. USDC has 6 decimals, so that is −$4,916.
  • Token 1 is WETH. Its reserve rose by 2,180,365,462,384,895,407 raw units. WETH has 18 decimals, so that is +2.18 WETH, which at current 2,255ETHequals+2,255 ETH equals **+4,917**.
  • Net dollar delta for the pool this block: ~$1.16. Which is what total_delta_usd says.
What you just witnessed is a swap: someone bought $4,916 worth of WETH with USDC. You can see it block-by-block, without reading a single contract event log.

Use cases by audience

The same feature, three different reasons to care.

For DeFi power users

If you provide liquidity, trade, or hold positions on DEXes, reserves streaming changes what you can know about your own money.

Real-time impermanent loss

Watch your LP position’s IL accrue block by block. Every reserve_update for your pool gives you the new constant-product or concentrated-liquidity state. Plug it into an IL calculator and your dashboard tells you what you are losing right now, not what you lost an hour ago.

Slippage estimation before you swap

Get the exact reserves of the pool you are about to trade through, as of the current block. Compute expected slippage against the actual current state. Decide whether to route, split, or wait.

Liquidity migration alerts

Watch a token’s entire pool ecosystem (method=token_reserves). When a major LP yanks liquidity from venue A and adds to venue B, you see both events within seconds.

Rug-pull early warning

Sudden, large, one-sided reserve drains on a small pool. Exactly the signal you want before everyone else notices. Wire total_delta_usd < -threshold into a webhook and you have it.

For quant, MEV, and arbitrage builders

You need fresh, deterministic, low-latency pool state across many venues. Polling-based APIs do not work for you. Raw log streams force you to be your own DEX integration team.

Block-level granularity

Every event carries the block it was observed in. Two events with the same block happened in the same block. Two events with different block values are strictly ordered. You can reconstruct per-block pool state without ambiguity.

Cross-DEX uniformity

Uniswap V2, V3, V4, Curve, Balancer, Aerodrome, Raydium, Orca, PancakeSwap, SushiSwap. They all emit the same reserve_update shape. One parser, one schema, one mental model. The contract-by-contract decoding lives on our side.

USD-denominated deltas, pre-attached

price_usd is attached to every token’s reserve at observation time. delta_usd tells you the dollar value of this block’s change. You do not maintain your own price feed to compute it.

Production-grade throughput

The streaming infrastructure is built for high concurrency. Production load testing has demonstrated comfortable headroom under hundreds of simultaneous connections, with linear throughput scaling under realistic workloads.

For analytics and dashboard builders

You build the products people open in the morning to see what is happening on-chain. Your users expect numbers that are accurate to the block, not to the minute.

Real-time TVL

Sum total_reserve_usd across all pools you track. The number is current to the block. No background ETL job. No “data is fresh as of 2 minutes ago” disclaimer.

Real-time DEX volume attribution

Aggregate abs(total_delta_usd) per pool, per DEX, per chain, over your time window of choice. You get DEX volume as a live stream, not a delayed batch.

Pool depth heatmaps

Maintain a per-pool reserves snapshot in memory, updated by every event. Render heatmaps that update live as liquidity moves across pools, chains, and DEXes.

Bridge flow visibility

When a bridge contract sits as a counterparty in a pool, its reserve changes show you cross-chain flows the moment they settle on the destination chain. No “is the bridge healthy” guessing.

Get started

Three runnable examples. Each connects to the live production stream, no API key required.

A single pool with cURL

Subscribe to a Uniswap V3 USDC/WETH pool on Ethereum.
curl -N "https://streaming.dexpaprika.com/sse/reserves?method=pool_reserves&chain=ethereum&address=0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"
You will see events arrive each time someone trades through this pool:
data: {"chain":"ethereum","pool_id":"0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640","block":25098912,"tokens":[...],"total_reserve_usd":5042113.41,"total_delta_usd":-128.44}
event: reserve_update
Use Ctrl-C to disconnect.

A whole token’s ecosystem with Python

Subscribe to every pool containing USDC across Ethereum. This will emit a lot of events.
import json
import requests

url = "https://streaming.dexpaprika.com/sse/reserves"
params = {
    "method": "token_reserves",
    "chain": "ethereum",
    "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",  # USDC
}

with requests.get(url, params=params, stream=True) as response:
    response.raise_for_status()

    # Buffer one SSE message at a time, then dispatch. The wire emits
    # `data:` before `event:`, so grouping is mandatory: a line-by-line
    # parser would never see the event type.
    msg_lines = []
    for raw in response.iter_lines(decode_unicode=True):
        if raw:
            msg_lines.append(raw)
            continue

        event_type = "message"
        data_str = None
        for ml in msg_lines:
            if ml.startswith("event:"):
                event_type = ml.split(":", 1)[1].strip()
            elif ml.startswith("data:"):
                data_str = ml[5:].lstrip()
        msg_lines = []

        if event_type != "reserve_update" or data_str is None:
            continue

        event = json.loads(data_str)
        pool = event["pool_id"]
        block = event["block"]
        net = event["total_delta_usd"]
        print(f"block {block}  pool {pool[:10]}...  net {net:+,.2f} USD")
Sample output:
block 25098905  pool 0x168e9b3c...  net +2.24 USD
block 25098905  pool 0x0f91b8bb...  net +0.14 USD
block 25098905  pool 0xff4ce5aa...  net +0.04 USD
block 25098905  pool 0xe0554a47...  net -0.03 USD
...
Each line is a separate pool that contains USDC, reporting its block-level reserve delta.

Multiple assets at once with JavaScript

A POST stream that mixes one specific pool and one whole token. Single connection, both feeds.
async function subscribe() {
  const response = await fetch("https://streaming.dexpaprika.com/sse/reserves", {
    method: "POST",
    headers: {
      "Accept": "text/event-stream",
      "Content-Type": "application/json",
    },
    body: JSON.stringify([
      {
        chain: "ethereum",
        address: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
        method: "pool_reserves",
      },
      {
        chain: "ethereum",
        address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        method: "token_reserves",
      },
    ]),
  });

  const decoder = new TextDecoder();
  let buffer = "";

  for await (const chunk of response.body) {
    buffer += decoder.decode(chunk, { stream: true });

    // SSE messages are separated by a blank line.
    const messages = buffer.split("\n\n");
    buffer = messages.pop() ?? "";

    for (const message of messages) {
      const lines = message.split("\n");
      const eventLine = lines.find((l) => l.startsWith("event:"));
      const dataLine = lines.find((l) => l.startsWith("data:"));
      if (!dataLine) continue;

      // Four event types may arrive: `reserve_update`, `ping`, `warning`, `error`.
      // Filter for the one we care about; unrecognised events should be no-ops.
      const eventType = eventLine ? eventLine.slice(6).trim() : "message";
      if (eventType !== "reserve_update") continue;

      const payload = JSON.parse(dataLine.slice(5).trim());
      handleReserveUpdate(payload);
    }
  }
}

function handleReserveUpdate(event) {
  console.log(`pool ${event.pool_id}  block ${event.block}  Δ ${event.total_delta_usd.toFixed(2)} USD`);
}

subscribe();
Two notes on the JavaScript example:
  • The browser EventSource API does not support POST requests, so multi-asset subscriptions on the browser require fetch + ReadableStream like above. Single-asset GET subscriptions work with EventSource directly.
  • The total_delta_usd field is a regular JavaScript number, safe for floating-point math. The raw reserve and delta fields can be very large integers; parse them with BigInt if you need exact precision.

Limits to know about

LimitValueBehaviour when exceeded
Pools/tokens per POST connection25POST /sse/reserves rejects the request with {"message": "too many subscriptions"}
Concurrent SSE streams per IP10The 11th connection returns 429 Too Many Requests with {"message": "ip stream limit exceeded"}
Ping interval15 sA ping event arrives if no reserve activity has been pushed for that long. Use it as a connection-liveness signal
The server may also push a warning event in-band (for example, when you connect to a deprecated path). Treat unknown event types as no-ops so new server-side additions never crash your handler.

What’s next

Streaming reference

Schema, parameters, error codes for the GET and POST /sse/reserves endpoints.

Token price streaming

DexPaprika also streams token prices over the same SSE transport. Use both together for full pool-state coverage.

Live dashboard demo

A working frontend that connects to the streaming API. Source-available, deployable, a good starting reference.

Discord community

The team is in Discord. Bring use cases, edge cases, and feature requests.