A WebSocket echo server that's actually up β€” plus something echo servers can't do

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.

The 10-second echo test

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 part echo servers can't do: stream your data

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}
OptionWhat it does
intervalms between records (100–10000, default 1000)
jitteradds 0–jitter random ms per record β€” realistic, irregular pacing
limitrecords per cycle (max 100)
repeatloop 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.

Wire it into a UI

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.

Prefer EventSource? The same feed speaks SSE

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 β†’

Testing a protected socket

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"

Honest comparison

echo.websocket.org (Ably)postman-echo /rawMockbird
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 limitsnone documentednone documented5 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.

Create yours

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.