The most underused weapon in a security or compliance team's stack is pre-confirmation alerting — knowing about a transaction in the mempool before it lands in a block. This post shows how to wire it up against the 1F real-time stream API in ten minutes.
Why pre-confirmation matters
A confirmed-tx alert tells you what happened. A pending-tx alert tells you what's about to happen — with 12 seconds of lead time on Ethereum, 3 seconds on BSC, sub-second on Arbitrum and Optimism.
That gap is the difference between:
- Freezing a CEX deposit before the customer can withdraw (ten-second window vs. zero)
- Alerting the destination chain of a bridge before laundering completes
- Front-running a sandwich attack on your own DEX trade
- Detecting an exploiter's first move the moment they publish the contract call
The subscription model
1F's mempool stream sits on top of the pendingTransactions WebSocket subscription on every EVM chain we ingest. You give it a filter; the server pushes JSON frames as matching pending txs arrive.
# 1. Create the subscription
SID=$(curl -s -X POST https://1f.ai/api/stream/subscribe \
-H "X-API-Key: $KEY" -H "content-type: application/json" \
-d '{
"chains": ["bsc"],
"tokens": ["0x2170ed0880ac9a755fd29b2688956bd959f933f8"],
"protocols": ["uniswapV2", "pancake"],
"includePending": true,
"includeConfirmed": false
}' | jq -r .subscriptionId)
# 2. Open WebSocket
websocat "wss://1f.ai/api/stream/ws?subscriptionId=$SID&apiKey=$KEY"
You'll get one frame per matching pending tx:
{
"type": "pending",
"chain": "bsc",
"txHash": "0x14cb…0e13f",
"from": "0x9bbcf…34fa4",
"to": "0x10ed4…56024e",
"valueWei": 1000000,
"decodedProtocol": "uniswapV2.swap",
"seenAtUtc": "2026-04-25T15:32:42Z"
}
Filter by token (catches swap-internal references)
A naive filter matches only when tx.to == tokenContract. That misses 99% of real activity
because most token movement is inside a router call. The 1F filter does a calldata
substring scan for the bare hex address — Uniswap/Pancake-style routers embed tokenIn and
tokenOut as 20-byte words inline, so the filter catches them.
Set tokens: ["0x...weth", "0x...usdt"] and you'll get every pending swap that touches WETH
or USDT, regardless of which router or aggregator it goes through.
Filter by protocol family
Use protocols: ["uniswapV2", "pancake", "curve", "aave"] to subset by decoder family. The
/api/decoders endpoint lists every decoded protocol. Family-prefix matches
work — "pancake" matches pancake.v3.exactInputSingle, pancake.router02.swapExactETHForTokens,
and so on.
Cross-chain bridge filter
The killer use case: alert me when an attacker bridges from Ethereum to BSC. Set
originChain: "ethereum" and the filter fires on confirmed BSC events whose source-side bridge
tx was on Ethereum (joined deterministically by message-id across LayerZero, Wormhole, CCTP,
Stargate, Hop, Across, Synapse, deBridge, native bridges).
{
"chains": ["bsc"],
"originChain": "ethereum",
"addresses": ["0xVictim1", "0xVictim2"]
}
Plan-tier rate caps
| Plan | Events/sec |
|---|---|
| Free | 5 |
| Starter ($50/mo) | 50 |
| Pro ($200/mo) | 500 |
| Enterprise | unlimited |
Over-cap clients get {"type":"throttled","dropped":N} heartbeats every 5 seconds instead of
disconnect — the client knows it's hitting the cap and can either upgrade or narrow the
filter. See Pricing.
Mempool visibility
The depth of mempool 1F sees depends on the upstream RPC endpoint configured per chain. Public endpoints sample aggressively. For production-grade visibility on a busy chain (BSC, Polygon, Solana mempool), wire in a paid endpoint via the per-chain env override:
ChainGraph__Chains__bsc__wssUrl=wss://bsc.nodereal.io/ws/v1/YOUR_KEY
The endpoint surface and filter semantics are identical regardless of upstream — only the visibility ceiling changes.
Code sample (Node.js)
import WebSocket from 'ws';
const SID = await fetch('https://1f.ai/api/stream/subscribe', {
method: 'POST',
headers: { 'X-API-Key': process.env.ONEF_KEY, 'content-type': 'application/json' },
body: JSON.stringify({
chains: ['ethereum', 'bsc', 'polygon'],
tokens: ['0xdac17f958d2ee523a2206206994597c13d831ec7'], // USDT
includePending: true
})
}).then(r => r.json()).then(d => d.subscriptionId);
const ws = new WebSocket(`wss://1f.ai/api/stream/ws?subscriptionId=${SID}&apiKey=${process.env.ONEF_KEY}`);
ws.on('message', (msg) => {
const ev = JSON.parse(msg);
if (ev.type === 'pending') {
console.log(`pending USDT touch on ${ev.chain}: ${ev.txHash} ${ev.decodedProtocol}`);
// → fire alert / freeze deposit / page on-call
}
});
Try it
Sign up at /auth?signup=1 — free tier covers up to 5 events/sec with no
credit card required. Same code works against the demo key
(X-API-Key: dev-test-key-123) for quick docs samples.
Related: Investigating a bridge exploit in 5 minutes, Alchemy vs QuickNode vs 1F, OFAC sanctions screening.