Unsubscribe from a stream

DELETE https://yourZulipDomain.zulipchat.com/api/v1/users/me/subscriptions

Unsubscribe yourself or other users from one or more streams.

In addition to managing the current user's subscriptions, this endpoint can be used to remove other users from streams. This is possible in 3 situations:

  • Organization administrators can remove any user from any stream.
  • Users can remove a bot that they own from any stream that the user can access.
  • Users can unsubscribe any user from a stream if they have access to the stream and are a member of the user group specified by the can_remove_subscribers_group for the stream.

Changes: Before Zulip 8.0 (feature level 208), if a user specified by the principals parameter was a deactivated user, or did not exist, then an HTTP status code of 403 was returned with code: "UNAUTHORIZED_PRINCIPAL" in the error response. As of this feature level, an HTTP status code of 400 is returned with code: "BAD_REQUEST" in the error response for these cases.

Before Zulip 8.0 (feature level 197), the can_remove_subscribers_group setting was named can_remove_subscribers_group_id.

Before Zulip 7.0 (feature level 161), the can_remove_subscribers_group_id for all streams was always the system group for organization administrators.

Before Zulip 6.0 (feature level 145), users had no special privileges for managing bots that they own.

Usage examples

#!/usr/bin/env python3

import zulip

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

# Unsubscribe from the stream "new stream"
result = client.remove_subscriptions(
    ["new stream"],
# Unsubscribe another user from the stream "new stream"
result = client.remove_subscriptions(
    ["new stream"],

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);

    // Unsubscribe from the stream "Denmark"
    const meParams = {
        subscriptions: JSON.stringify(["Denmark"]),
    console.log(await client.users.me.subscriptions.remove(meParams));

    const user_id = 7;
    // Unsubscribe Zoe from the stream "Denmark"
    const zoeParams = {
        subscriptions: JSON.stringify(["Denmark"]),
        principals: JSON.stringify([user_id]),
    console.log(await client.users.me.subscriptions.remove(zoeParams));

Note: Unsubscribing another user from a stream requires administrative privileges.

curl -sSX DELETE https://yourZulipDomain.zulipchat.com/api/v1/users/me/subscriptions \
    --data-urlencode 'subscriptions=["Verona", "Denmark"]'

You may specify the principals parameter like so:

curl -sSX DELETE https://yourZulipDomain.zulipchat.com/api/v1/users/me/subscriptions \
    --data-urlencode 'subscriptions=["Verona", "Denmark"]' \
    --data-urlencode 'principals=["ZOE@zulip.com"]'


subscriptions (string)[] required

Example: ["Verona", "Denmark"]

A list of stream names to unsubscribe from. This parameter is called streams in our Python API.

principals (string)[] | (integer)[] optional

Example: ["ZOE@zulip.com"]

A list of user IDs (preferred) or Zulip API email addresses of the users to be subscribed to or unsubscribed from the streams specified in the subscriptions parameter. If not provided, then the requesting user/bot is subscribed.

Changes: The integer format is new in Zulip 3.0 (feature level 9).


Return values

  • not_removed: (string)[]

    A list of the names of streams that the user is already unsubscribed from, and hence doesn't need to be unsubscribed.

  • removed: (string)[]

    A list of the names of streams which were unsubscribed from as a result of the query.

Example response(s)

Changes: As of Zulip 7.0 (feature level 167), if any parameters sent in the request are not supported by this endpoint, a successful JSON response will include an ignored_parameters_unsupported array.

A typical successful JSON response may look like:

    "msg": "",
    "not_removed": [],
    "removed": [
        "new stream"
    "result": "success"

A typical failed JSON response for when the target stream does not exist:

    "code": "STREAM_DOES_NOT_EXIST",
    "msg": "Stream 'nonexistent_stream' does not exist",
    "result": "error",
    "stream": "nonexistent_stream"