GRPC

Every swap, as a typed message.

dexploit.v1.SwapStream is a server-streaming RPC: one .proto file, a generated client in whichever language you work in, and eleven filter fields evaluated on our side so you receive only the trades you asked for. It runs over HTTP/2, so concurrent RPCs share one connection and the protocol's own flow control.

Endpoint
grpc.dexploit.dev:443
Filter fields
11
Venues decoded
10
Stream buffer
1024 events
Pipelinelive · public demo stream

Validator to binary frame.

Connecting
0 frames this session
Inspector
Hover or focus a node to hold the flow and read what is passing through it. Every value comes from the last swap this page received.
feed
connecting
frames this session
0
events decoded
read live on /products
last venue
The contract

The schema is the product.

This is swap_stream.proto as published. Field numbers and types are the wire format itself. Compile this and your client knows every field, and its type, before a byte arrives.

swap_stream.protoproto3 · dexploit.v1
syntax = "proto3";
package dexploit.v1;
 
service SwapStream {
// Live tail. Streams every new swap matching the filter.
rpc StreamLive(StreamLiveRequest) returns (stream UnifiedSwap);
 
// Reserved for v1.1: the handler returns UNIMPLEMENTED today.
rpc StreamFromCursor(StreamFromCursorRequest) returns (stream UnifiedSwap);
}
 
message StreamLiveRequest {
SwapFilter filter = 1;
}
 
message SwapFilter {
repeated string tokens = 1; // mints, base58; empty = all
repeated string traders = 2;
repeated string pools = 3;
repeated string dexes = 4; // "pumpfun", "raydium_clmm", ...
optional uint64 min_sol = 5; // lamports
optional uint64 max_sol = 6;
optional uint64 min_token_amount = 7;
optional uint64 max_token_amount = 8;
optional bool is_buy = 9;
optional string signature = 10;
repeated string wallet_tags = 11; // Pro tier
}
 
message UnifiedSwap {
string signature = 1;
uint64 slot = 2;
int64 timestamp = 3; // unix epoch seconds
string dex = 4;
string trader = 5;
string token_mint = 6;
string pool_address = 7;
bool is_buy = 8;
uint64 amount_in = 9;
uint64 amount_out = 10;
uint64 sol_amount = 11; // lamports
uint64 token_amount = 12; // raw token units
optional double price_per_token = 13; // SOL per token
repeated string trader_tags = 14;
uint32 ix_index = 15; // per-leg ordinal
}
Server reflection
$ grpcurl -H 'x-api-key: ohlcv_live_sk_...' \
    grpc.dexploit.dev:443 list
dexploit.v1.PriceStream
dexploit.v1.SwapStream

Reflection is enabled, so the service set resolves without the file. grpcurl will describe any message in it too.

The events this message carries
wss://ws.dexploit.dev/ws/swapsconnecting

A browser cannot open an HTTP/2 gRPC stream, so this tape is the JSON WebSocket feed, live. Same decoder, same trades. Over gRPC each of these rows arrives as one UnifiedSwap message instead.

Generate a client from that file
Rust
tonic-build
Go
protoc-gen-go-grpc
Python
grpc_tools.protoc
TypeScript
@grpc/proto-loader
Wire size

The same swap, two encodings.

A UnifiedSwap encoded to the schema above, beside the WebSocket frame for the same fill. Both figures are measurements: the frame length as the upstream socket sent it, and the protobuf length produced in your browser. Toggle a field to see what it costs on each wire.

Specimen · captured 2026-09-06
The recordPump.fun · slot 444,983,571 · 3xvEXx…phByR5
WebSocket · JSON frame519 B

the 15 selected fields serialised as JSON

gRPC · UnifiedSwap message282 B

encoded to the published proto3 schema in this browser, just now

−46%

fewer bytes on the wire for this swap, computed from the two measurements above

Fields · toggle to see each one's cost
protojson

Protobuf omits a field at its default (a false is_buy, a zero ix_index, an empty tag list), which is why a sell costs fewer bytes than a buy. Decoding is not free on either side; what changes is how many bytes cross the wire, and whether your client learns the field types from a schema or from the payload.

Surface

Two services, three RPCs, one message.

RPCs
  • rpcSwapStream.StreamLiveLive UnifiedSwap tail, filtered server-side
  • rpcSwapStream.StreamFromCursorReserved for v1.1: returns UNIMPLEMENTED
  • rpcPriceStream.SubscribeDexTransaction price updates, per protocol
SwapFilter · evaluated server-side
  • tokens / traders / poolsrepeated string

    Base58 addresses. An empty list means every one of them, not none

  • dexesrepeated string

    Venue ids in snake_case: pumpfun, pumpswap, raydium_clmm, orca

  • min_sol / max_soloptional uint64

    Lamports, not SOL. 1 SOL is 1000000000

  • min_token_amount / max_token_amountoptional uint64

    Raw base units, before any decimals adjustment

  • is_buyoptional bool

    Omit the field entirely to receive both directions

  • signatureoptional string

    Exact match, for pinning one transaction while debugging

  • wallet_tagsrepeated string

    smart_money, sniper, whale, insider. Pro tier; below it the server drops the filter and says so in initial metadata

UnifiedSwap · every field
  • signaturestring

    Base58 transaction signature, not unique on its own

  • slotuint64

    Solana slot the trade landed in

  • timestampint64

    Unix epoch seconds, signed so it round-trips the internal i64

  • dexstring

    Venue id, snake_case, same vocabulary the filter takes

  • traderstring

    Signer wallet

  • token_mintstring

    The non-SOL side of the trade

  • pool_addressstring

    Pool or bonding-curve account

  • is_buybool

    True when the token was bought

  • amount_in / amount_outuint64

    Raw atomic units as the instruction carried them

  • sol_amountuint64

    Lamports: divide by 1e9 for SOL

  • token_amountuint64

    Base units: divide by 10^decimals

  • price_per_tokenoptional double

    SOL per token, already decimal-adjusted. Absent when pool reserves were unavailable

  • trader_tagsrepeated string

    Wallet tags carried on the trader, same vocabulary as the filter

  • ix_indexuint32

    Per-leg ordinal inside the transaction; 0 means legacy or unknown

Worth knowing

Four things that will bite you.

signature

One transaction can carry several swaps. Router and aggregator trades emit a UnifiedSwap per leg, all sharing a signature and a slot. ix_index is the per-leg ordinal, so key on the pair. Dedupe on the signature alone and you drop legs silently.

uint64

sol_amount is lamports and token_amount is raw base units; only price_per_token is decimal-adjusted. A uint64 also does not fit a JavaScript number, so generated clients hand it back as a string or a Long depending on your loader options.

backpressure

Every stream gets a 1024-event server-side buffer. Fall further behind and the server cancels the stream: RESOURCE_EXHAUSTED: client too slow; buffer overflow. A tab left in the background for a few seconds during a busy window is enough, so treat reconnecting as normal operation.

keepalive

Set keepalive_time_ms, keepalive_timeout_ms and keepalive_permit_without_calls on the channel. Without them a half-open TCP connection is indistinguishable from a quiet market: the stream looks alive and delivers nothing.

Schema

The published contract, field for field.

Codegen against this and the names will match. SwapFilter is where the eleven server-side filters live.

service SwapStream

Two RPCs. StreamLive is the MVP surface; StreamFromCursor is defined in the proto and returns UNIMPLEMENTED until v1.1.

rpc StreamLive(StreamLiveRequest) returns (stream UnifiedSwap)

message SwapFilter · 11 fields

Tokens, traders, pools and venues as repeated strings; size and amount bounds; direction; a signature lookup; and wallet tags. Applied before the bytes are sent.

tokens=1 traders=2 pools=3 dexes=4 min_sol=5 max_sol=6 min_token_amount=7 max_token_amount=8 is_buy=9 signature=10 wallet_tags=11

message UnifiedSwap · 15 fields

One record shape for every venue, with trader tags attached and a per-leg ordinal so multi-leg transactions keep their execution order.

signature slot timestamp dex trader token_mint pool_address is_buy amount_in amount_out sol_amount token_amount price_per_token trader_tags ix_index
Plans

Rate limits, stated per second.

Proentry
$199/mo
Rate
500 RPS
Connections
50 concurrent
History
Full history
Support
Priority
Enterprise
Custom
Rate
Negotiated
Connections
Negotiated
History
Full history
Support
24/7

This transport starts on Pro ($199/mo). Not included on Starter or Developer.

Limits are per second. The plans have no monthly request quota, so none is quoted here.