Historical replay
Every event the full-depth stream pushes is also written to an hourly archive. Pull an hour, replay it through the same code that consumes the live socket, and you have a backtest. Free, no key.
https://replay.shrine.trade/pump/YYYY/MM/DD/HH.jsonl.zst
The hour is UTC. A file appears a few minutes after its hour ends, and the archive runs from the day it was switched on; earlier hours do not exist.
Browse the bucket
The archive lives in a public Cloudflare R2 bucket. Object storage has no directory listing, so the bucket keeps its own: https://replay.shrine.trade/pump/index.json lists every hour it holds, oldest first, and is rewritten after each upload.
{ "base": "https://replay.shrine.trade/pump", "pattern": "<base>/<hour>.jsonl.zst", "count": 312, "hours": ["2026/09/11/07", "2026/09/11/08", "…"] }
Join base, an entry of hours and .jsonl.zst to get a file. To fetch everything, walk hours; to fill a gap, look for the missing entry.
Format
One JSON object per line, zstd compressed. Each line is exactly the live stream event, with one extra field: localTimestamp, the millisecond our server saw the transaction, which the live stream does not carry. Everything else, signature, block, timestamp, action, protocol, mint, breakdown, tradersInvolved, reserves and the launch authorities, is as documented on the stream page.
An hour is a few hundred megabytes compressed and a couple of gigabytes unpacked. Stream it rather than loading it whole.
Copy this
- JavaScript
- Python
- Shell
// Node 22.15+ has zstd in zlib. Older Node: npm install fzstd.
import { createZstdDecompress } from "node:zlib";
import { createInterface } from "node:readline";
import { Readable } from "node:stream";
const url = "https://replay.shrine.trade/pump/2026/09/11/07.jsonl.zst";
const res = await fetch(url);
const lines = createInterface({ input: Readable.fromWeb(res.body).pipe(createZstdDecompress()) });
let buys = 0;
for await (const line of lines) {
const e = JSON.parse(line);
if (e.action === "buy" && e.protocol === "PUMPFUN") buys++;
}
console.log("pump.fun buys that hour:", buys);
import io, json, requests, zstandard # pip install zstandard requests
url = "https://replay.shrine.trade/pump/2026/09/11/07.jsonl.zst"
with requests.get(url, stream=True) as r:
reader = zstandard.ZstdDecompressor().stream_reader(r.raw)
buys = 0
for line in io.TextIOWrapper(reader, encoding="utf-8"):
e = json.loads(line)
if e["action"] == "buy" and e["protocol"] == "PUMPFUN":
buys += 1
print("pump.fun buys that hour:", buys)
curl -s https://replay.shrine.trade/pump/2026/09/11/07.jsonl.zst | zstd -d | head -3
Notes
- Same shape as live. Code written against
subscribe_streamruns unchanged on the archive; feed it lines instead of socket events. - Filter early. Every transaction on every launchpad is in there. Keep the protocols and actions you care about and drop the rest while streaming.
- Ordering. Lines are in the order events were seen, which is block order with the occasional neighbour swapped. Sort by
blockthensignaturewhen it matters. - Gaps. A restart of the collector can lose a minute or two inside an hour. There is no marker; compare
blocknumbers if you need to know.