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.
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:
{
// 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:
["REQ", "", { "search": "orange" }, { "kinds": [1, 2], "search": "purple" }]
3 Relay Behavior
A relay that receives a search filter follows these rules:
| Rule | Level | Detail |
|---|---|---|
| interpret | SHOULD | Interpret the query to the best of its ability and return events that match it. |
| match content | SHOULD | Match the query against the content field of events. |
| match other fields | MAY | Match against other fields when that makes sense for a specific kind. |
| rank | SHOULD | Return results in descending order by quality of match, as the implementation defines it. Not by created_at. |
| limit | SHOULD | Apply the limit filter after sorting by matching score. |
| ignore extensions | SHOULD | Ignore key:value extensions it does not support. See §5. |
| exclude spam | SHOULD | Exclude spam from search results by default, if the relay supports some form of spam filtering. |
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.
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_nipsfield to learn if a relay supports thesearchfilter. - 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.
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.
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:
| Extension | Effect |
|---|---|
| include:spam | Turn 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. |
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.
-- 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/falseextension was added, then reverted three days later. It is not part of the spec. - 20252025-12Added the
relaytag to the header: this NIP is a relay feature. - 20242024-06Defined the order of results: descending by match quality, with
limitapplied after scoring. - 20242024-03Added the classification extensions
language,sentiment, andnsfw; added thedomainextension for NIP-05 domains. - 20232023-01Rewrote the spec:
keywordsbecame the free-textsearchfield, and the title became "Search Capability". - 20232023-01Added the
supported_nipsdiscovery hint and the client-side filtering hint days after the origin. - Origin2023-01-17Artur Brugeman opened the NIP as "Keywords Filter".
Original Proposal - Credits
| Author | Artur Brugeman |
| First commit | 2023-01-17 — “NIP-50: Keywords Filter” |
| Status | draft optional — a relay feature; clients must expect relays without it |
| Source | 50.md on GitHub - full change history |