NIP-45: Event Counts
Counting by downloading everything is bad protocol hygiene
A social client often wants numbers: followers, reactions, replies, comments, posts in a list, notes that reference an event. Without NIP-45, the naive way to count is to request all matching events and count them locally. That wastes bandwidth, punishes relays and pushes developers toward private indexing services.
NIP-45 adds a relay-level answer for that problem. The client sends COUNT with a query id and ordinary NIP-01 filters. The relay returns a COUNT response with a numeric count, optionally marked as approximate. If the relay refuses, it closes the query with CLOSED.
The practical importance is not that every number becomes perfect. It is that clients get a shared, cheap way to ask relays for count-shaped answers without inventing an off-protocol API.
COUNT, approximate answers and mergeable HyperLogLog
The basic flow is short: ["COUNT", query_id, filters...] followed by ["COUNT", query_id, {"count": integer}]. Multiple filters are ORed together and aggregated into one result, matching the ordinary filter semantics from NIP-01.
NIP-45 allows probabilistic counts. A relay can return approximate: true when it does not want to pay the cost of an exact count. That sounds minor, but it is exactly the kind of honesty a distributed UI needs. A follower count pulled from several relays is often a network estimate, not a ledger.
The newer HyperLogLog section lets relays include a hex-encoded HLL value. Clients can merge HLL registers from several relays and estimate a cross-relay count while avoiding huge event downloads. That is a strong fit for reactions, comments and follower-style counts where the exact event list is less important than the scale.
Deprecated, restored and made more useful
NIP-45 arrived in January 2023 through Jonathan Staab's COUNT verb. It was later changed for multiple filters and approximate results, then deprecated in October 2023 and restored in July 2024. That back-and-forth is worth mentioning because count APIs sit at the edge between protocol convenience and relay burden.
In October 2025, Vitor Pamplona changed wording from subscription id to query id to reduce confusion. In February 2026, fiatjaf's PR #1561 added the HyperLogLog relay response. The direction is clear: keep the API small, but give clients a way to combine count estimates across relays.
The history also tells people not to treat NIP-45 as universal. If a client depends on counts, it still has to handle relays that do not support the verb, return approximate results or refuse expensive filters.
Counts live in the relay's index, not in the button label
A relay implementation needs indexed queries, refusal behavior and clear approximate semantics. A client implementation needs fallbacks: try COUNT on supporting relays, merge results if HLL is available, show approximate counts honestly and avoid treating missing support as zero.
The NDK and nostr-tools issue threads around NIP-45 support are useful because they show the standard's adoption pressure from developers. Rust-nostr advertises NIP-45 support in its supported-NIPs table. The presence of library support does not mean every relay can count every filter cheaply, but it does mean the shape is recognized by serious tooling.
A good UI labels these numbers with humility. A count from one relay is a count from one relay. A merged estimate is an estimate. A missing COUNT response is not proof that no events exist.
Numbers can look more certain than they are
A count in a UI feels authoritative, even when it comes from one relay, one index policy and one spam filter. NIP-45 helps reduce bandwidth, but it does not create a global truth oracle.
There is also a relay-cost risk. Counting arbitrary filters can be expensive. Relays need the right to refuse, approximate or restrict queries, and clients need to behave well when a relay does so.
Read NIP-45 in the wild
NIP-45 lets a relay return counts without sending every matching event. That sounds small until search, notifications, unread states and analytics start asking the same question repeatedly.
Counts can mislead when relay coverage is partial. A count is a count from somewhere, under specific filters and policies. Clients need to avoid turning it into a universal popularity number.
What changes when you actually use it
For you, NIP-45: Counting results is felt when an app either behaves predictably or suddenly loses context. The visible symptom may be a missing reply, a broken link, a strange reaction count, an empty result or a relay error that looks like the whole network failed. The official terms draft, relay, COUNT, REQ, {"count": <integer>}, approximate are where that visible behavior begins, so the source is not background material; it is the place where the product promise gets its limits.
What changes for builders and operators
For builders, NIP-45: Counting results is compatibility discipline. Implement draft, relay, COUNT, REQ, {"count": <integer>}, approximate against more than one relay and more than one library, then test malformed, missing and duplicated data. Core standards fail most painfully when the happy path looks fine and the second client exposes the shortcut.
What the official file makes concrete
The official file is organized around Motivation, Filters and return values, HyperLogLog, Algorithm, offset computation, Rationale, Common filters, Attack vectors. Inspect draft, relay, COUNT, REQ, {"count": <integer>}, approximate, {"count": <integer>, "approximate": <true|false>}, CLOSED because these are the pieces most likely to surface as product behavior. Read it beside NIP-65 before treating it as isolated.
NIP-45: Counting results is a shared contract between independent software. The smallest field can become user-visible when two clients disagree about it.
Where it breaks
The failure mode in NIP-45: Counting results is often indirect. Nobody complains about draft, relay, COUNT, REQ, {"count": <integer>}, approximate; they complain that the feed is wrong, the reply vanished or the relay behaved strangely. Use the official file to diagnose the hidden cause instead of patching only the visual symptom.
Where this appears outside the markdown
In the ecosystem, NIP-45: Counting results is not something most people choose directly. It is the invisible grammar behind clients, relays, crawlers, search tools and archives. When a product team treats draft, relay, COUNT, REQ, {"count": <integer>}, approximate as implementation detail only, the mistake eventually reaches the surface as missing history, bad threading or state that cannot be reconstructed after a client switch.
The nearby-standard trap
The nearby-standard trap in NIP-45: Counting results is assuming the base layer solves the higher-level feature. This NIP may define the common grammar, but publishing, wallets, moderation, media or groups still need their own constraints. Read NIP-65 to see where the base contract ends and the product-specific promise begins.
Language that keeps the feature honest
Good product copy for NIP-45: Counting results does not say "the protocol handles it" and move on. It explains the visible consequence: what was sent, what was accepted, what was rejected, what is still loading and what another relay or client may show differently.
What this page does not promise
NIP-45: Counting results does not promise a finished social product. It gives software a shared grammar. Feed design, moderation, ranking, notifications, storage duration and recovery remain separate product decisions. That distinction matters because a client can be technically compatible and still give you a weak experience if it hides relay errors, drops context or treats optional fields as if every app understood them.
Read it as a field test
Start NIP-45: Counting results with the visible product symptom, then trace it back to draft, relay, COUNT, REQ, {"count": <integer>}, approximate. That order keeps the article grounded: you see why the field exists, which relay or client behavior depends on it, and where adjacent standards change the story. A core NIP is strong only when it explains both the normal path and the awkward edge case.
Where the standard earns trust
The source links give you places to test the interpretation in public: nips.nostr.com NIP-45, NDK NIP-45 issue, nostr-tools NIP-45 issue, rust-nostr supported NIPs. Use those links to move from the spec to live libraries, mirrors, pull requests, guides or products.
Official NIP-45 source is the anchor for exact wording, and NIP-45 commit history shows how that wording moved over time. The strongest secondary clues here are nips.nostr.com NIP-45, NDK NIP-45 issue, nostr-tools NIP-45 issue. Treat this evidence chain as part of the article, not as footnotes. A NIP page becomes useful when you can move from claim to source to working behavior without guessing.
Keep the chain visible for NIP-45: Counting results: first the human promise, then draft, relay, COUNT, REQ, {"count": <integer>}, approximate, then the implementation record, then the real-world failure case. That order keeps NIP-45 useful without turning it into marketing copy or protocol trivia.
Three questions to carry forward
- Can two independent clients read the same
draft,relay,COUNT,REQwithout a hidden compatibility rule? - Does the UI explain relay rejection, missing context or state replacement without blaming the whole network?
- Which adjacent standard, especially NIP-65, changes the behavior once the base event leaves the happy path?
What to verify before you rely on it
- Find
draft,relay,COUNT,REQ,{"count": <integer>}in the official file and check where the UI exposes the same concept. - Read NIP-65 as context before treating NIP-45 as a complete product story.
- Open at least one implementation, mirror, pull request or library source from the source links before trusting that the idea is mature.
- Test the unhappy path: missing relays, stale metadata, invalid signatures, blocked events, expired state, revoked permissions or unavailable media.
- Write the user-facing copy in plain language. If a standard changes authority, privacy, money, moderation or recovery, say that before the click.
Direct sources
Use these sources for NIP-45: Counting results in that order: Official NIP-45 source for the current wording; NIP-45 commit history for the change record; nips.nostr.com NIP-45, NDK NIP-45 issue, nostr-tools NIP-45 issue for public context. The article gives you the consequence in plain language, but the source trail is where exact fields, status notes, unresolved debates and implementation proof stay checkable.





