Get messages

Fetch message history from a Zulip server.


This GET /api/v1/messages endpoint is the primary way to fetch message history from a Zulip server. It is useful both for Zulip clients (e.g. the web, desktop, mobile, and terminal clients) as well as bots, API clients, backup scripts, etc.

By specifying a narrow filter, you can use this endpoint to fetch the messages matching any search query that is supported by Zulip's powerful full-text search backend.

When a narrow is not specified, it can be used to fetch a user's message history. (We recommend paginating to 1000 messages at a time.)

In either case, you specify an anchor message (or ask the server to calculate the first unread message for you and use that as the anchor), as well as a number of messages before and after the anchor message. The server returns those messages, sorted by message ID, as well as some metadata that makes it easy for a client to determine whether there are more messages matching the query that were not returned due to the num_before and num_after limits.

We recommend using num_before <= 1000 and num_after <= 1000 to avoid generating very large HTTP responses. A maximum of 5000 messages can be obtained per request; attempting to exceed this will result in an error.

Usage examples

#!/usr/bin/env python3

import zulip

# Pass the path to your zuliprc file here.
client = zulip.Client(config_file="~/zuliprc")

# Get the 100 last messages sent by "" to the stream "Verona"
request: Dict[str, Any] = {
    "anchor": "newest",
    "num_before": 100,
    "num_after": 0,
    "narrow": [
        {"operator": "sender", "operand": ""},
        {"operator": "stream", "operand": "Verona"},
result = client.get_messages(request)

More examples and documentation can be found here.

const zulipInit = require("zulip-js");

// Pass the path to your zuliprc file here.
const config = { zuliprc: "zuliprc" };

(async () => {
    const client = await zulipInit(config);

    const readParams = {
        anchor: "newest",
        num_before: 100,
        num_after: 0,
        narrow: [
            {operator: "sender", operand: ""},
            {operator: "stream", operand: "Verona"},

    // Get the 100 last messages sent by "" to the stream "Verona"
    console.log(await client.messages.retrieve(readParams));

curl -sSX GET -G \
    --data-urlencode anchor=42 \
    --data-urlencode num_before=4 \
    --data-urlencode num_after=8 \
    --data-urlencode 'narrow=[{"operand": "Denmark", "operator": "stream"}]'


anchor string | integer optional

Example: 42

Integer message ID to anchor fetching of new messages. Supports special string values for when the client wants the server to compute the anchor to use:

  • newest: The most recent message.
  • oldest: The oldest message.
  • first_unread: The oldest unread message matching the query, if any; otherwise, the most recent message.

The special values of 'newest' and 'oldest' are also supported for anchoring the query at the most recent or oldest messages.

Changes: String values are new in Zulip 3.0 (feature level 1). The first_unread functionality was supported in Zulip 2.1.x and older by not sending anchor and using use_first_unread_anchor.

In Zulip 2.1.x and older, oldest can be emulated with anchor=0, and newest with anchor=10000000000000000 (that specific large value works around a bug in Zulip 2.1.x and older in the found_newest return value).

num_before integer required

Example: 4

The number of messages with IDs less than the anchor to retrieve.

num_after integer required

Example: 8

The number of messages with IDs greater than the anchor to retrieve.

narrow (object)[] optional

Example: [{"operand": "Denmark", "operator": "stream"}]

The narrow where you want to fetch the messages from. See how to construct a narrow.

client_gravatar boolean optional

Example: true

Whether the client supports computing gravatars URLs. If enabled, avatar_url will be included in the response only if there is a Zulip avatar, and will be null for users who are using gravatar as their avatar. This option significantly reduces the compressed size of user data, since gravatar URLs are long, random strings and thus do not compress well. The client_gravatar field is set to true if clients can compute their own gravatars.

Defaults to false.

apply_markdown boolean optional

Example: false

If true, message content is returned in the rendered HTML format. If false, message content is returned in the raw Markdown-format text that user entered.

Defaults to true.

use_first_unread_anchor boolean optional Deprecated

Example: true

Legacy way to specify anchor="first_unread" in Zulip 2.1.x and older.

Whether to use the (computed by the server) first unread message matching the narrow as the anchor. Mutually exclusive with anchor.

Changes: Deprecated in Zulip 3.0, replaced by anchor="first_unread" instead.

Defaults to false.


Return values

When a request is successful, this endpoint returns a dictionary containing the following (in addition to the msg and result keys present in all Zulip API responses).

  • anchor: integer The same anchor specified in the request (or the computed one, if use_first_unread_anchor is true).

  • found_newest: boolean Whether the messages list includes the very newest messages matching the narrow (used by clients that paginate their requests to decide whether there are more messages to fetch).

  • found_oldest: boolean Whether the messages list includes the very oldest messages matching the narrow (used by clients that paginate their requests to decide whether there are more messages to fetch).

  • found_anchor: boolean Whether the anchor message is included in the response. If the message with the ID specified in the request does not exist or did not match the narrow, this will be false.

  • history_limited: boolean Whether the message history was limited due to plan restrictions. This flag is set to true only when the oldest messages(found_oldest) matching the narrow is fetched.

  • messages: (object)[] an array of message objects, each containing the following fields:

    • avatar_url: string The URL of the user's avatar. Can be null only if client_gravatar was passed, which means that the user has not uploaded an avatar in Zulip, and the client should compute the gravatar URL by hashing the user's email address itself for this user.

    • client: string A Zulip "client" string, describing what Zulip client sent the message.

    • content: string The content/body of the message.

    • content_type: string The HTTP content_type for the message content. This will be text/html or text/x-markdown, depending on whether apply_markdown was set.

    • display_recipient: string | (object)[] Data on the recipient of the message; either the name of a stream or a dictionary containing basic data on the users who received the message.

    • id: integer The unique message ID. Messages should always be displayed sorted by ID.

    • is_me_message: boolean Whether the message is a /me status message

    • reactions: (object)[] Data on any reactions to the message.

      • emoji_code: string A unique identifier, defining the specific emoji codepoint requested, within the namespace of the reaction_type.

        For example, for unicode_emoji, this will be an encoding of the Unicode codepoint.

      • emoji_name: string Name of the emoji.

      • reaction_type: string One of the following values:

        • unicode_emoji: Unicode emoji (emoji_code will be its Unicode codepoint).
        • realm_emoji: Custom emoji. (emoji_code will be its ID).
        • zulip_extra_emoji: Special emoji included with Zulip. Exists to namespace the zulip emoji.
      • user_id: integer The ID of the user who added the reaction.

        Changes: New in Zulip 3.0 (feature level 2). The user object is deprecated and will be removed in the future.

      • user: object Dictionary with data on the user who added the reaction, including the user ID as the id field. Note: In the events API, this user dictionary confusing had the user ID in a field called user_id instead. We recommend ignoring fields other than the user ID. Deprecated and to be removed in a future release once core clients have migrated to use the user_id field.

        • id: integer ID of the user.

        • email: string Email of the user.

        • full_name: string Full name of the user.

        • is_mirror_dummy: boolean Whether the user is a mirror dummy.

    • recipient_id: integer A unique ID for the set of users receiving the message (either a stream or group of users). Useful primarily for hashing.

    • sender_email: string The Zulip display email address of the message's sender.

    • sender_full_name: string The full name of the message's sender.

    • sender_id: integer The user ID of the message's sender.

    • sender_realm_str: string A string identifier for the realm the sender is in. Unique only within the context of a given Zulip server.

      E.g. on, this will be example.

    • stream_id: integer Only present for stream messages; the ID of the stream.

    • subject: string The topic of the message. Currently always "" for private messages, though this could change if Zulip adds support for topics in private message conversations.

      The field name is a legacy holdover from when topics were called "subjects" and will eventually change.

    • topic_links: (object)[] Data on any links to be included in the topic line (these are generated by custom linkification filters that match content in the message's topic.)

      Changes: This field contained a list of urls before Zulip 4.0 (feature level 46).

      New in Zulip 3.0 (feature level 1): Previously, this field was called subject_links; clients are recommended to rename subject_links to topic_links if present for compatibility with older Zulip servers.

      • text: string The original link text present in the topic.

      • url: string The expanded target url which the link points to.

    • submessages: (string)[] Data used for certain experimental Zulip integrations.

    • timestamp: integer The UNIX timestamp for when the message was sent, in UTC seconds.

    • type: string The type of the message: stream or private.

    • flags: (string)[] The user's message flags for the message.

    • last_edit_timestamp: integer The UNIX timestamp for when the message was last edited, in UTC seconds.

    • match_content: string Only present if keyword search was included among the narrow parameters. HTML content of a queried message that matches the narrow, with <span class="highlight"> elements wrapping the matches for the search keywords.

    • match_subject: string Only present if keyword search was included among the narrow parameters. HTML-escaped topic of a queried message that matches the narrow, with <span class="highlight"> elements wrapping the matches for the search keywords.

Example response

A typical successful JSON response may look like:

    "anchor": 21,
    "found_anchor": true,
    "found_newest": true,
    "messages": [
            "avatar_url": "",
            "client": "populate_db",
            "content": "<p>Security experts agree that relational algorithms are an interesting new topic in the field of networking, and scholars concur.</p>",
            "content_type": "text/html",
            "display_recipient": [
                    "email": "",
                    "full_name": "King Hamlet",
                    "id": 4,
                    "is_mirror_dummy": false
                    "email": "",
                    "full_name": "Iago",
                    "id": 5,
                    "is_mirror_dummy": false
                    "email": "",
                    "full_name": "Prospero from The Tempest",
                    "id": 8,
                    "is_mirror_dummy": false
            "flags": [
            "id": 16,
            "is_me_message": false,
            "reactions": [],
            "recipient_id": 27,
            "sender_email": "",
            "sender_full_name": "King Hamlet",
            "sender_id": 4,
            "sender_realm_str": "zulip",
            "subject": "",
            "submessages": [],
            "timestamp": 1527921326,
            "topic_links": [],
            "type": "private"
            "avatar_url": "",
            "client": "populate_db",
            "content": "<p>Wait, is this from the frontend js code or backend python code</p>",
            "content_type": "text/html",
            "display_recipient": "Verona",
            "flags": [
            "id": 21,
            "is_me_message": false,
            "reactions": [],
            "recipient_id": 20,
            "sender_email": "",
            "sender_full_name": "King Hamlet",
            "sender_id": 4,
            "sender_realm_str": "zulip",
            "stream_id": 5,
            "subject": "Verona3",
            "submessages": [],
            "timestamp": 1527939746,
            "topic_links": [],
            "type": "stream"
    "msg": "",
    "result": "success"