Quickstart
From zero to a live subscriber in about five minutes. You will create a stream, publish a batch to it, subscribe and watch it arrive, then disconnect and resume from a cursor without losing anything.
Get an API key
Every request except the health and version endpoints needs a key in the
Authorization header. For this walkthrough you can use the public demo
key, which is read-only and rate limited, or your own key from the dashboard once you
have an account.
# your own key, from the dashboard
export RUNNEV_API_KEY="rnv_live_00112233445566778899aabbccddeeff"
# check connectivity - no auth required
curl https://runnev.dev/v1/health
# {"status":"ok","uptime_s":1843302}
1. Create a stream
Streams are identified by a base62 id. Let the server assign one:
curl https://runnev.dev/v1/streams \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"quickstart","retention_seconds":3600}'
{
"id": "RqrkHXc9bOQs9VlDT836jx",
"name": "quickstart",
"created_at": "2026-08-04T11:00:00Z",
"retention_seconds": 3600,
"max_bytes": 268435456,
"cursor": 0,
"bytes_stored": 0,
"mode": "json"
}
Keep that id; every other call uses it. Export it for convenience:
export STREAM="RqrkHXc9bOQs9VlDT836jx"
2. Publish a batch
The primary write path puts the sequence number in the URL. Start at 1.
Choosing the sequence yourself is what makes publishes idempotent, which the
Publishing page covers in depth.
curl https://runnev.dev/v1/streams/$STREAM/1 \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{"events":[{"type":"hello","msg":"first batch"}]}'
{"stream_id":"RqrkHXc9bOQs9VlDT836jx","seq":1,"accepted":1,"cursor":1,"duplicate":false}
3. Subscribe
Open a second terminal and hold a subscription open. The -N flag tells
curl not to buffer, so you see batches as they arrive. By default a subscription
starts at the head, so publish again from the first terminal and watch it show up.
curl -N "https://runnev.dev/v1/streams/$STREAM?cursor=0" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $RUNNEV_API_KEY"
: runnev
id: 1
event: batch
data: {"seq":1,"ts":"2026-08-04T11:00:12.418Z","events":[{"type":"hello","msg":"first batch"}]}
: keepalive
The line beginning with a colon is a comment frame. Runnev sends one every 15 seconds when the stream is idle so that intermediaries and clients can tell a healthy quiet connection from a dead one.
4. Resume from a cursor
Stop the subscriber with Ctrl-C. Publish a couple more batches while it is down:
curl https://runnev.dev/v1/streams/$STREAM/2 \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"events":[{"type":"tick","n":2}]}'
curl https://runnev.dev/v1/streams/$STREAM/3 \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"events":[{"type":"tick","n":3}]}'
Now reconnect starting after sequence 1. You get 2 and 3 immediately, then live:
curl -N "https://runnev.dev/v1/streams/$STREAM?cursor=1" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $RUNNEV_API_KEY"
That is the whole loop: publish at a sequence, read from a cursor, resume from the cursor after any disconnection. The SDKs do the reconnection for you.
The same thing with an SDK
import { Runnev } from "@runnev/client";
const runnev = new Runnev({ apiKey: process.env.RUNNEV_API_KEY });
const stream = await runnev.createStream({ name: "quickstart", retentionSeconds: 3600 });
await runnev.publish(stream.id, 1, [{ type: "hello", msg: "first batch" }]);
for await (const batch of runnev.subscribe(stream.id, { cursor: 0 })) {
console.log(batch.seq, batch.events);
}
import os
from runnev import Runnev
runnev = Runnev(api_key=os.environ["RUNNEV_API_KEY"])
stream = runnev.create_stream(name="quickstart", retention_seconds=3600)
runnev.publish(stream.id, 1, [{"type": "hello", "msg": "first batch"}])
for batch in runnev.subscribe(stream.id, cursor=0):
print(batch.seq, batch.events)
Next steps
- Authentication - key formats and failure modes.
- Publishing - sequences, idempotency, raw mode, batching.
- Subscribing - the SSE wire format and long-lived connections.