Documentation

Broadcast with blaastr

blaastr is a write-only nostr relay. Send it a signed event from a subscribed pubkey and it is published to every relay currently online (about a thousand, from profilestr), plus the author's own write relays. A subscription costs 21,000 sats per year and belongs to a pubkey: there are no API keys, and paying needs no sign-in.

Quick start

  • Subscribe your npub on the home page.
  • Add wss://blaastr.com to your client's relay list (write enabled).
  • Post as usual. That's it.

Websocket relay

wss://blaastr.com speaks NIP-01. It accepts EVENT and answers every event with an OK. It stores nothing, so REQ is answered with CLOSED.

import { Relay } from "nostr-tools/relay";
import { finalizeEvent } from "nostr-tools/pure";

const relay = await Relay.connect("wss://blaastr.com");
const event = finalizeEvent(
  { kind: 1, created_at: Math.floor(Date.now() / 1000), tags: [], content: "hello, every relay" },
  secretKey,
);
await relay.publish(event); // resolves on OK true, throws with the reason on OK false
relay.close();

Raw protocol:

→ ["EVENT", {"id":"…","pubkey":"…","created_at":1761270000,"kind":1,"tags":[],"content":"hello","sig":"…"}]
← ["OK", "<event id>", true, ""]

→ ["REQ", "sub1", {"kinds":[1]}]
← ["CLOSED", "sub1", "unsupported: this relay is write-only"]

Relay info (NIP-11): curl -H "Accept: application/nostr+json" https://blaastr.com

HTTP publish

For servers and scripts, POST /api/event takes the same ["EVENT", {…}] message and returns the OK message as JSON. No key is needed: the signature identifies the author, and CORS is open.

curl -X POST https://blaastr.com/api/event \
  -H "Content-Type: application/json" \
  -d '["EVENT", {"id":"…","pubkey":"…","created_at":1761270000,"kind":1,"tags":[],"content":"hello","sig":"…"}]'
# → ["OK","<event id>",true,""]
const res = await fetch("https://blaastr.com/api/event", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(["EVENT", signedEvent]),
});
const [, id, ok, reason] = await res.json();
if (!ok) throw new Error(reason);

Responses

The same OK messages come back over the websocket and HTTP. true means the event was queued; publishing to relays happens in the background, usually within seconds.

CaseOK message
Accepted and queued["OK","<id>",true,""]
Already published (last 30 days)["OK","<id>",true,"duplicate: already published"]
Author not subscribed or expired["OK","<id>",false,"restricted: no active subscription for this pubkey, …"]
Bad id or signature["OK","<id>",false,"invalid: bad id or signature"]
Over 1 MB["OK","<id>",false,"invalid: event too large"]
Ephemeral kind (20000–29999)["OK","<id>",false,"blocked: ephemeral events are not broadcast"]
Disallowed kind (1064)["OK","<id>",false,"blocked: disallowed event kind"]
Scheduled (?publish_at=)["OK","<id>",true,"scheduled: <publish_at>"]
Over 30 events/min["OK","<id>",false,"rate-limited: …"]
Over 1,000 events/day["OK","<id>",false,"rate-limited: daily limit reached"]
Upstream outage["OK","<id>",false,"error: broadcast service unavailable, try again later"]

Over HTTP, a body that isn't ["EVENT", {…}] gets a 400.

Scheduling

Add ?publish_at=<unix time> to POST /api/event or POST /api/events to send it later, up to 30 days ahead. A time in the past means right away. Date the event's created_at at the same time so clients show it then.

curl -X POST "https://blaastr.com/api/event?publish_at=1761300000"   -H "Content-Type: application/json" -d '["EVENT", {…}]'
# → ["OK","<id>",true,"scheduled: 1761300000"]

It counts toward the daily limit of the day you schedule it. Until it goes out you can follow or cancel it from your account; cancelling gives the limit back. A publish_at more than 30 days ahead gets a 400.

Batches

POST /api/events takes a JSON array of 1 to 50 signed events (bare events, not EVENT messages, up to 5 MB in all) and answers with one OK message per event, in the same order. Events can be from different authors; each gets the same checks as a single publish.

curl -X POST https://blaastr.com/api/events -H "Content-Type: application/json"   -d '[{"id":"…","kind":1,…}, {"id":"…","kind":7,…}]'
# → [["OK","<id 1>",true,""], ["OK","<id 2>",false,"invalid: bad id or signature"]]

Your account

Log in with a nostr extension (NIP-07) or a remote signer app (NIP-46) to manage your own key. You sign a one-time login message; your key never leaves your signer. From your account you can:

  • See every broadcast from the last 7 days and what each relay said (accepted, rejected and why, no reply, unreachable).
  • Reblast an event to the relays that haven't taken it, including ones that joined since. Reblasts count toward the daily limit.
  • Schedule a note, and cancel scheduled events before they go out.
  • See your usage per day and what's left of today's 1,000.
  • Set a webhook to get a signed POST when each broadcast settles (broadcast.done or broadcast.incomplete).

The relays we publish to, and which are being skipped, are listed on Relays.

Subscription status

GET /api/subscription/:pubkey accepts an npub or hex key. It's public, so apps can check before offering blaastr to a user.

curl https://blaastr.com/api/subscription/npub1…
{
  "pubkey": "82341f88…",       // hex
  "active": true,
  "expiresAt": 1792800000,      // unix seconds, null if never subscribed
  "eventsSent": 412,
  "lastEventAt": 1761270000     // null if none yet
}

Paying from your app

You can take users through payment without leaving your app:

// 1. Create a one-year invoice for any pubkey
POST https://blaastr.com/api/invoice
{"pubkey": "npub1…"}
→ {"paymentHash":"…","pubkey":"…","bolt11":"lnbc…","amountSats":21000,"expiresAt":1761270900,"paid":false}

// 2. Pay bolt11 with any lightning wallet (WebLN, NWC, QR code…)

// 3. Poll until paid (every few seconds)
GET https://blaastr.com/api/invoice/<paymentHash>
→ {…, "paid": true}

Asking for an invoice while an unpaid one for the same pubkey has more than 5 minutes left returns that one. Payments are credited even if nobody polls. Each payment adds a year to the time left.

Limits and rules

  • Only events signed by a subscribed pubkey are broadcast. Anyone may submit them.
  • 30 events per minute and 1,000 events plus reblasts per UTC day per pubkey.
  • Events up to 1 MB. Many relays have their own, smaller limits and may reject large events.
  • Ephemeral events (kinds 20000–29999, including NWC and AUTH) are not broadcast.
  • The same event id is only broadcast once per 30 days.
  • Relays that refuse our events (paid, auth-required, rate-limiting, unreachable) are skipped automatically, so “every relay” means every relay that accepts events from us.