Construct a narrow

A narrow is a set of filters for Zulip messages, that can be based on many different factors (like sender, channel, topic, search keywords, etc.). Narrows are used in various places in the Zulip API (most importantly, in the API for fetching messages).

It is simplest to explain the algorithm for encoding a search as a narrow using a single example. Consider the following search query (written as it would be entered in the Zulip web app's search box). It filters for messages sent to channel announce, not sent by iago@zulip.com, and containing the words cool and sunglasses:

channel:announce -sender:iago@zulip.com cool sunglasses

This query would be JSON-encoded for use in the Zulip API using JSON as a list of simple objects, as follows:

[
    {
        "operator": "channel",
        "operand": "announce"
    },
    {
        "operator": "sender",
        "operand": "iago@zulip.com",
        "negated": true
    },
    {
        "operator": "search",
        "operand": "cool sunglasses"
    }
]

The Zulip help center article on searching for messages documents the majority of the search/narrow options supported by the Zulip API.

Note that many narrows, including all that lack a channel or channels operator, search the current user's personal message history. See searching shared history for details.

Clients should note that the is:unread filter takes advantage of the fact that there is a database index for unread messages, which can be an important optimization when fetching messages in certain cases (e.g., when adding the read flag to a user's personal messages).

Note: When the value of realm_empty_topic_display_name found in the POST /register response is used as an operand for the "topic" operator in the narrow, it is interpreted as an empty string.

Changes

  • In Zulip 12.0 (feature level 489), support was added for a new filter, channels:archived, which returns messages the current user received in channels that have been archived.

  • In Zulip 12.0 (feature level 446), add the mentions operator, matching messages that contain a direct personal mention of the specified user.

  • In Zulip 10.0 (feature level 366), support was added for a new is:muted operator combination, matching messages in topics and channels that the user has muted.

  • Before Zulip 10.0 (feature level 334), empty string was not a valid topic name for channel messages.

  • In Zulip 9.0 (feature level 271), support was added for a new filter operator, with, which uses a message ID for its operand, and is designed for creating permanent links to topics.

  • In Zulip 9.0 (feature level 265), support was added for a new is:followed filter, matching messages in topics that the current user is following.

  • In Zulip 9.0 (feature level 250), support was added for two filters related to stream messages: channel and channels. The channel operator is an alias for the stream operator. The channels operator is an alias for the streams operator. Both channel and channels return the same exact results as stream and streams respectively.

  • In Zulip 9.0 (feature level 249), support was added for a new filter, has:reaction, which returns messages that have at least one emoji reaction.

  • In Zulip 7.0 (feature level 177), support was added for three filters related to direct messages: is:dm, dm and dm-including. The dm operator replaced and deprecated the pm-with operator. The is:dm filter replaced and deprecated the is:private filter. The dm-including operator replaced and deprecated the group-pm-with operator.

    • The dm-including and group-pm-with operators return slightly different results. For example, dm-including:1234 returns all direct messages (1-on-1 and group) that include the current user and the user with the unique user ID of 1234. On the other hand, group-pm-with:1234 returned only group direct messages that included the current user and the user with the unique user ID of 1234.

    • Both dm and is:dm are aliases of pm-with and is:private respectively, and return the same exact results that the deprecated filters did.

Narrows that use IDs

Message IDs

The id and with operators use message IDs for their operands. The message ID operand for these two operators may be encoded as either a number or a string.

  • id:12345: Search for only the message with ID 12345.
  • with:12345: Search for the conversation that contains the message with ID 12345.

The id operator returns the message with the specified ID if it exists, and if it can be accessed by the user.

The with operator is designed to be used for permanent links to topics, which means they should continue to work when the topic is moved or resolved. If the message with the specified ID exists, and can be accessed by the user, then it will return messages with the channel/topic/dm operators corresponding to the current conversation containing that message, replacing any such operators included in the original narrow query.

If no such message exists, or the message ID represents a message that is inaccessible to the user, this operator will be ignored (rather than throwing an error) if the remaining operators uniquely identify a conversation (i.e., they contain channel and topic terms or dm term). This behavior is intended to provide the best possible experience for links to private channels with protected history.

Changes: Prior to Zulip 8.0 (feature level 194), the message ID operand for the id operator needed to be encoded as a string.

[
    {
        "operator": "id",
        "operand": 12345
    }
]

Channel and user IDs

There are a few additional narrow/search options (new in Zulip 2.1) that use either channel IDs or user IDs that are not documented in the help center because they are primarily useful to API clients:

  • channel:1234: Search messages sent to the channel with ID 1234.
  • sender:1234: Search messages sent by user ID 1234.
  • dm:1234: Search the direct message conversation between you and user ID 1234.
  • dm:1234,5678: Search the direct message conversation between you, user ID 1234, and user ID 5678.
  • dm-including:1234: Search all direct messages (1-on-1 and group) that include you and user ID 1234.

A user ID can be found by viewing a user's profile in the web or desktop apps. A channel ID can be found when browsing channels in the web or desktop apps.

The operands for these search options must be encoded either as an integer ID or a JSON list of integer IDs. For example, to query messages sent by a user 1234 to a direct message thread with yourself, user 1234, and user 5678, the correct JSON-encoded query is:

[
    {
        "operator": "dm",
        "operand": [1234, 5678]
    },
    {
        "operator": "sender",
        "operand": 1234
    }
]

Narrow terms that don't filter messages

The help center documents the near operator for searching for messages by ID and the date operator for searching for messages by date. But neither operator has an effect on filtering messages when sent to the server, and should be omitted when constructing the set of filters for a narrow. These terms may appear in a URL used or generated by the Zulip web app.

The near operator

The near operator is designed to be used for permanent links to messages, which means they should continue to work when the message is moved (to another channel and/or topic), or when a topic is resolved. The value of its operand is a message ID.

A permanent link to a message will have a URL with only three narrow terms: a channel term, a topic term and a near term. When this is the case, clients should use the near term's message ID to find where the message is now, and check if the current user has access to that message. The message may either be in the client's current locally fetched message data, or can be fetched by passing the message ID in the message_ids parameter to GET /messages.

Otherwise, when the near operator is used to search for messages, clients should instead use the message ID for value of the anchor parameter in endpoints that also accept a narrow parameter; see GET /messages and POST /messages/flags/narrow.

The date operator

When the date operator is used to search for messages, or is part of a URL fragment, clients should instead use the special date value for the anchor parameter, and use the value of the operand to set the anchor_date parameter when fetching messages. Unlike the near operator, the date operator is only supported by the GET /messages endpoint.

The value of the date operator's operand should be an ISO 8601 date-only string (e.g., "2025-12-01"), which is a valid value for the anchor_date parameter. But, when possible, clients should convert that calendar date to the current user's local timezone's midnight, and set the value of the anchor_date parameter as a full datetime string (e.g., "2025-12-01T00:00:00+01:00").