If you've ever followed a WebSocket tutorial, you've met echo.websocket.org.
Kaazing shut it down in 2021. Lob stood up the community replacement everyone then
pasted into GitHub threads β echo.websocket.events β and that one is
now dead too (checked July 2026: the handshake fails). The original
address was later revived by Ably, and it's a fine pure echo server β but an echo
server only tests that your socket opens. Your actual app code handles structured events: tickers, notifications,
order updates, chat messages.
Every Mockbird project ships a wss:// endpoint that does both jobs β
echo for the plumbing, and a mock realtime feed of your own
records for the real thing. No signup, no backend.
npx wscat -c "wss://HOST/m/demo/ws"
> hello
< hello
Text and binary frames both come straight back. The connection greets you with one
{"type":"welcome"} frame listing what it can do β add ?quiet=1
if your test asserts byte-exact echo from the first frame.
The demo project has 30 seeded products. Ask for them as a live feed:
npx wscat -c "wss://HOST/m/demo/ws?subscribe=products&interval=500&limit=5"
< {"type":"subscribed","resource":"products","count":5,"interval":500,"repeat":false}
< {"type":"record","resource":"products","seq":1,"of":5,"data":{"id":1,"name":"β¦","price":β¦}}
< β¦
< {"type":"complete","resource":"products","count":5,"repeat":false}
Or drive it with commands on an open connection β subscribe, switch resources, stop, all without reconnecting:
> {"subscribe":"orders","interval":800,"repeat":true,"jitter":400}
> {"unsubscribe":true}
> {"ping":1}
| Option | What it does |
|---|---|
interval | ms between records (100β10000, default 1000) |
jitter | adds 0βjitter random ms per record β realistic, irregular pacing |
limit | records per cycle (max 100) |
repeat | loop forever β a feed that never runs dry while you build the UI |
Because the feed is your project's own records, the events match your schema exactly β the same data your REST and GraphQL endpoints serve, imported from your db.json, CSV or OpenAPI spec if that's how you created it.
A notifications badge, a price ticker, an order dashboard β this is all the client code a prototype needs:
const ws = new WebSocket("wss://HOST/m/demo/ws?subscribe=products&interval=1000&repeat=1&quiet=1");
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "record") renderRow(msg.data); // your schema, one record at a time
};
ws.onclose = () => scheduleReconnect(); // sessions close after 5 min β test this path free
That last line is a feature: real connections drop. Mockbird sessions end after
5 minutes with a clean close, so your reconnect logic gets exercised without you
pulling a network cable. Use jitter to make sure your UI doesn't assume
a metronome.
Not every UI needs a socket. If your app uses Server-Sent Events,
the same feed is at GET /m/<project>/sse (text/event-stream,
CORS on) β same subscribe / interval / limit /
repeat / jitter options, plus SSE's own superpower: every event
carries an id:, so when EventSource auto-reconnects it sends
Last-Event-ID and the stream resumes where it left off β
exactly the behavior your reconnect handling needs to be tested against, and something a
plain echo server can never exercise:
curl -N "https://HOST/m/demo/sse?subscribe=products&interval=500&limit=3"
const es = new EventSource("https://HOST/m/demo/sse?subscribe=products&interval=500");
es.addEventListener("record", (e) => {
const { seq, of, data } = JSON.parse(e.data); // data = the record itself
render(seq, of, data);
});
es.addEventListener("complete", () => es.close()); // omit with &repeat=1
Bare /sse (no subscribe) emits timed tick
events β the "is my EventSource plumbing wired up?" signal every SSE tutorial needs a
live URL for. Docs β
If your real WS endpoint requires auth, flip the project to
protected mode and the handshake demands a JWT β passed as
?token=, because the browser WebSocket API can't send an
Authorization header (a constraint your production code has to deal with
anyway β practice against it here):
TOKEN=$(curl -s -X POST https://HOST/m/YOUR_PROJECT/auth/login \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","password":"anything"}' | jq -r .token)
npx wscat -c "wss://HOST/m/YOUR_PROJECT/ws?token=$TOKEN"
| echo.websocket.org (Ably) | postman-echo /raw | Mockbird | |
|---|---|---|---|
| Pure echo | β (banner frame first) | β | β (?quiet=1 for byte-exact) |
| Stream your own schema as events | β | β | β ?subscribe= any resource |
| Pacing control (interval / jitter / repeat) | β | β | β |
| Auth testing | β | β | β JWT protected mode |
| Same data over REST + GraphQL + SSE too | β | β | β one project, every protocol |
| Session limits | none documented | none documented | 5 min / 1000 msgs per connection |
Where the others win: Ably's revived echo.websocket.org is a
purpose-built, no-frills echo tool (and open
source if you want to self-host) β if pure echo forever is all you need, it's great. Mockbird's session caps exist because it's a shared free service;
reconnect logic makes them invisible.
curl -s -X POST https://HOST/api/projects \
-H 'content-type: application/json' \
-d '{"name":"my-api","preset":"ecommerce"}'
β¦or use the one-click dashboard link β no terminal
required. The response's baseUrl + /ws is your socket. Full
reference in the WebSocket docs; pair it with
SSE streaming if your fallback path uses
EventSource.