Chapter 50 - Relay Ops
50
Nostr Implementation Possibility - Hex № 50

Search Capability

A free-text search field for REQ filters, so a client can ask a relay for events that match a human query.

1 Overview

The NIP-01 filter is structured. It matches exact ids, exact pubkeys, exact kinds, and exact tag values. It cannot answer a question like "best nostr apps". Many use cases need that kind of general search.

This NIP adds one field to the filter object: search. The field carries a human-readable query string. The relay interprets the query and returns matching events. How the relay matches is up to the relay. Search algorithms differ between event kinds and between implementations. This NIP defines only the extensible frame around them.

The frame has two parts. First, rules for how relays and clients handle the search field. Second, an extension syntax: key:value pairs inside the query string that refine the search.

Hint!

Search is a relay feature, not an event format. This NIP defines no new kinds, no new tags, and no new messages. It only extends the filter object from NIP-01.

2 The search Filter Field

A client adds the search field to any filter inside a REQ message:

Filter object - new field
{
  // other fields on filter object...
  "search": <string>
}

The value is a query in human-readable form, for example "best nostr apps". It is not a regular expression and not a structured language. The relay decides what "matches" means.

The search field combines with the normal filter fields. A client may include kinds, ids, and any other field to restrict the results to particular events. A client may also send several search filters in one REQ:

Two search filters in one REQ
["REQ", "", { "search": "orange" }, { "kinds": [1, 2], "search": "purple" }]

3 Relay Behavior

A relay that receives a search filter follows these rules:

Relay rules for search filters
RuleLevelDetail
interpretSHOULDInterpret the query to the best of its ability and return events that match it.
match contentSHOULDMatch the query against the content field of events.
match other fieldsMAYMatch against other fields when that makes sense for a specific kind.
rankSHOULDReturn results in descending order by quality of match, as the implementation defines it. Not by created_at.
limitSHOULDApply the limit filter after sorting by matching score.
ignore extensionsSHOULDIgnore key:value extensions it does not support. See §5.
exclude spamSHOULDExclude spam from search results by default, if the relay supports some form of spam filtering.
Warning!

Search results break the normal ordering rule. A plain NIP-01 query returns the newest events first. A search query returns the best matches first. Do not assume the first result is the most recent one.

Query "search" string Extensions key:value pairs Match content field Rank score, then limit
Fig. 50-1 — Search pipeline. The relay parses extensions, matches content, ranks by score, then applies limit.

4 Client Behavior

Not every relay implements search. A relay that does lists the number 50 in the supported_nips field of its NIP-11 information document. Clients follow these rules:

  • Clients SHOULD use the supported_nips field to learn if a relay supports the search filter.
  • Clients MAY send search filters to any relay, if they are prepared to filter out extraneous responses from relays that do not support this NIP.
  • Clients SHOULD query several relays that support this NIP. This compensates for different implementation details between relays.
  • Clients MAY verify that returned events match the query, in a way that suits the client's use case.
  • Clients MAY stop querying relays that have low precision.
Hint!

A relay that does not know this NIP ignores the unknown search field. It then matches the rest of the filter. An almost empty filter matches almost everything. Expect a flood of unrelated events from such relays, and filter them out on your side.

Client Relay 1 Relay 2 Relay 3 REQ + search ranked results, merged low precision? stop asking
Fig. 50-2 — Fan-out and verify. The client queries several search relays, merges results, and drops relays with low precision.

5 Extensions

A query string may contain key:value pairs: two words separated by a colon. Each pair is an extension. Extensions refine the search. A relay SHOULD ignore extensions it does not support. A relay MAY support these extensions:

Query extensions
ExtensionEffect
include:spamTurn off spam filtering, if the relay enabled it by default.
domain:<domain>Include only events from users whose valid NIP-05 domain matches the domain.
language:<code>Include only events of the specified language. The value is a two-letter ISO 639-1 language code.
sentiment:<value>Include only events of a specific sentiment. The value is negative, neutral, or positive.
nsfw:<true/false>Include or exclude nsfw events. The default is true: nsfw events are included.
Tech Note

The extension syntax is ambiguous. Any two words separated by a colon count as an extension. The spec gives no escape rule for a literal colon in the query text. A search for re:invent may vanish as an unsupported extension on one relay and match text on another. There is also no way to list which extensions a relay supports; a relay silently drops the ones it does not know.

6 Worked Example

The session below shows one search round trip against a relay that supports this NIP. Orange lines go to the relay. Blue lines come from the relay. The results arrive best match first, not newest first.

Session transcript - wss://search.example.com
-- check support first (NIP-11, over HTTP) --
GET https://search.example.com  =>  {"supported_nips": [1, 11, 50, ...], ...}

-- plain search, restricted to kind-1 notes --
→ ["REQ", "find", {"kinds":[1], "search":"best nostr apps", "limit":3}]
← ["EVENT", "find", {"id":"7d1c02aa…", "kind":1,
     "content":"My list of the best nostr apps in 2026 …", …}]
← ["EVENT", "find", {"id":"e0937f21…", "kind":1,
     "content":"Which nostr apps do you rate best?", …}]
← ["EVENT", "find", {"id":"41bc55d0…", "kind":1,
     "content":"App roundup: notes, zaps, search …", …}]
← ["EOSE", "find"]
→ ["CLOSE", "find"]

-- search with extensions: one domain, English, no spam filter --
→ ["REQ", "find2", {"search":"best nostr apps domain:example.com language:en include:spam"}]
← ["EVENT", "find2", {"id":"9ac311be…", "pubkey":"22a12a12…", …}]
← ["EOSE", "find2"]

The client sent the second query with three extensions. A relay that supports none of them SHOULD run the search as if the query were only best nostr apps.

7 Version History

NIP-50 started as a "keywords" filter and became a free-text search frame within its first ten days. Notable changes, newest first:

  • 20262026-06An autocomplete:true/false extension was added, then reverted three days later. It is not part of the spec.
  • 20252025-12Added the relay tag to the header: this NIP is a relay feature.
  • 20242024-06Defined the order of results: descending by match quality, with limit applied after scoring.
  • 20242024-03Added the classification extensions language, sentiment, and nsfw; added the domain extension for NIP-05 domains.
  • 20232023-01Rewrote the spec: keywords became the free-text search field, and the title became "Search Capability".
  • 20232023-01Added the supported_nips discovery hint and the client-side filtering hint days after the origin.
  • Origin2023-01-17Artur Brugeman opened the NIP as "Keywords Filter".

Original Proposal - Credits

AuthorArtur Brugeman
First commit2023-01-17 — “NIP-50: Keywords Filter”
Statusdraft optional — a relay feature; clients must expect relays without it
Source50.md on GitHub - full change history

8 See Also

  • NIP-01 — the filter object and the REQ message that search extends.
  • NIP-11 — the relay information document; its supported_nips field advertises search support.
  • NIP-05 — DNS-based identifiers; the domain: extension filters by a user's valid NIP-05 domain.