---
source_url: https://www.pubnub.com/docs/chat/sdks/messages/unread-counts
title: Unread messages (deprecated)
updated_at: 2026-06-24T11:04:27.932Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Unread messages (deprecated)

:::warning Use Chat SDKs
This documentation is deprecated. Use any of our dedicated [Chat SDKs](https://www.pubnub.com/docs/chat/overview) to quickly implement chat functionality in your application.
:::

PubNub allows you to get a count of messages in channels that were published after a specific timestamp. When users return to their applications, the app can indicate which channels have new messages and also display counts of unread messages in each channel. You can build this feature easily so your users never miss a message and get the best chat experience.

There are two primary ways you can store a user's last-read timetokens:

* Store timetoken cursors locally within the app
* Store timetoken cursors on PubNub using App Context

:::note User ID / UUID
User ID is also referred to as **UUID/uuid** in some APIs and server responses but **holds the value** of the **userId** parameter you [set during initialization](https://www.pubnub.com/docs/general/setup/users-and-devices#set-the-user-id).
:::

## Store cursors locally

With the first option, you'll need to store the timetoken of the last message that was read by the user in a channel. If the user uses multiple channels, you'll need to store multiple timetokens, one per channel.

## Store cursors using App Context

With the second option, you can store these timetokens inside of PubNub. The App Context feature enables you to persist membership associations between users and channels, and any custom fields that might be useful for your app. Refer to [Channel Metadata](https://www.pubnub.com/docs/general/metadata/channel-metadata) for more information on channel memberships.

### Set channel cursors

Use the [setMemberships](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#set-channel-memberships) method to store timetokens as custom fields on each user-channel membership on PubNub.

#### JavaScript

```js
pubnub.objects.setMemberships({
      channels: [{
          id: "channel-1",
          custom: {
            lastReadTimetoken: "15518041524300120"
          },
          {
            id: "channel-2",
            custom: {
              lastReadTimetoken: "15518041524300251"
            }
          }]
      });
```

#### Swift

```swift
import PubNubSDK

// Define custom data structure
struct CustomFields: Codable {
  let lastReadTimetoken: String
}

// Create memberships with custom fields
let newMembershipChannel1 = PubNubMembershipMetadataBase(
  uuidMetadataId: "my_user",
  channelMetadataId: "channel-1",
  custom: ["lastReadTimetoken": "15518041524300120"]
)

let newMembershipChannel2 = PubNubMembershipMetadataBase(
  uuidMetadataId: "my_user",
  channelMetadataId: "channel-2",
  custom: ["lastReadTimetoken": "15518041524300251"]
)

pubnub.setMemberships(
  uuid: "my_user",
  channels: [newMembershipChannel1, newMembershipChannel2]
) { result in
  switch result {
  case let .success(response):
    print("The next page used for pagination: \(String(describing: response.next))")
  case let .failure(error):
    print("Update Memberships request failed with error: \(error.localizedDescription)")
  }
}
```

#### Objective-C

```objectivec
NSArray<NSDictionary *> *channels = @[
  @{ @"channel": @"my_channel_1", @"custom": @{ @"lastReadTimetoken": @"15518041524300120" } },
  @{ @"channel": @"my_channel_2", @"custom": @{ @"lastReadTimetoken": @"15518041524300251" } }
];

self.client.objects().setMemberships()
    .uuid(@"uuid")
    .channels(channels)
    .includeFields(PNMembershipCustomField | PNMembershipChannelField)
    .performWithCompletion(^(PNManageMembershipsStatus *status) {
        if (!status.isError) {
            /**
             * UUID's memberships successfully set.
             * Result object has following information:
             *   status.data.memberships - List of UUID's existing memberships.
             *   status.data.next - Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
             *   status.data.prev - Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data.
             *   status.data.totalCount - Total number of UUID's memberships.
             */
        } else {
            /**
             * Handle UUID's memberships set error. Check 'category' property to find out possible
             * issue because of which request did fail.
             *
             * Request can be resent using: [status retry]
             */
        }
    });
```

#### C#

```csharp
List<PNMembership> setMembershipChannelMetadataIdList = new List<PNMembership>();
if (!string.IsNullOrEmpty(seMembershipChannelMetaId))
{
    setMembershipChannelMetadataIdList.Add(new PNMembership() { Channel = "my_channel_1", Custom = new Dictionary<string, object>() { { "lastReadTimetoken", "15518041524300120" } } });
    setMembershipChannelMetadataIdList.Add(new PNMembership() { Channel = "my_channel_2", Custom = new Dictionary<string, object>() { { "lastReadTimetoken", "15518041524300251" } } });
}

PNResult<PNMembershipsResult> setMembershipsResponse = await pubnub.SetMemberships()
    .Uuid("my-userId")
    .Channels(setMembershipChannelMetadataIdList)
    .Include(new PNMembershipField[] { PNMembershipField.CUSTOM, PNMembershipField.CHANNEL, PNMembershipField.CHANNEL_CUSTOM })
    .IncludeCount(true)
    .ExecuteAsync();
PNMembershipsResult setMembershipsResult = setMembershipsResponse.Result;
PNStatus status = setMembershipsResponse.Status;
```

### Get channel cursors

When the user returns to the app, use the [getMemberships](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#get-channel-memberships) method to fetch channel memberships for the user along with its custom fields.

#### JavaScript

```js
pubnub.objects.getMemberships({
  include: {
    customFields: true
  }
});
```

#### Swift

```swift
pubnub.fetchMemberships(uuid: "my_user") { result in
  switch result {
  case let .success(response):
    print("The channel memberships for the User ID \(response.memberships)")
    if let nextPage = response.next {
      print("The next page used for pagination: \(nextPage)")
    }
  case let .failure(error):
    print("Fetch Memberships request failed with error: \(error.localized### Description)")
  }
}
```

#### Objective-C

```objectivec
self.client.objects().memberships()
    .includeFields(PNMembershipCustomField)
    .performWithCompletion(^(PNFetchMembershipsResult *result, PNErrorStatus *status) {
    });
```

#### C#

```csharp
PNResult<PNGetMembershipsResult> getMembershipsResponse = await pubnub.GetMemberships()
    .Include(new PNMembershipField[] { PNMembershipField.CUSTOM })
    .IncludeCount(true)
    .Page(new PNPageObject() { Next = "", Prev = "" })
    .ExecuteAsync();
PNGetMembershipsResult getMembershipsResult = getMembershipsResponse.Result;
PNStatus status = getMembershipsResponse.Status;
```

The timetokens can then be passed to the [messageCounts](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#message-counts) method.

## Get counts of unread messages

The [messageCounts](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#message-counts) method returns a count of unread messages in one or more channels. The count returned is the number of messages in Message Persistence with a timetoken value greater than or equal to the passed value in the `timetoken` parameter. For more details on retrieving counts of unread messages, refer to [Get Message Counts](https://www.pubnub.com/docs/general/storage#get-message-counts).

### JavaScript

```js
pubnub.messageCounts({
  channels: ['ch-1', 'ch-2'],
  channelTimetokens: ['15518041524300251'],
}, (status, results) => {
  // handle status, response
  console.log(status);
  console.log(results);
});
```

### Swift

```swift
pubnub.messageCounts(
  channels: [
    "ch-1": "15526611838554309",
    "ch-2": "15526611838554310"
  ],
  channelTimetokens: ["15518041524300251"]
) { result in
  switch result {
  case let .success(response):
    print("Successful Message Count Response: \(response)")
  case let .failure(error):
    print("Failed Message Count Response: \(error.localizedDescription)")
  }
}
```

### Java

```java
Long lastHourTimetoken = (Calendar.getInstance().getTimeInMillis() - TimeUnit.HOURS.toMillis(1)) * 10000L;

pubnub.messageCounts()
    .channels(Arrays.asList("ch-1", "ch-2"))
    .channelsTimetoken(Arrays.asList(lastHourTimetoken))
    .async(result -> {
        result.onSuccess(res -> {
            for (Map.Entry<String, Long> messageCountEntry : res.getChannels().entrySet()) {
                messageCountEntry.getKey(); // the channel name
                messageCountEntry.getValue(); // number of messages for that channel
            }
        }).onFailure(exception -> {
            exception.printStackTrace();
        });
    });
```

### Unity

```csharp
List<string> channelList = new List<string>();
channelList.Add("ch-1");
channelList.Add("ch-2");

pubnub.MessageCounts().Channels(channelList).ChannelsTimetoken(new List<long>{15518041524300251}).Async((result, status) => {
        if (!status.Error) {
            if((result.Channels != null)){
              Debug.Log(string.Format("MessageCounts, {0}", result.Channels.Count));
              foreach(KeyValuePair<string, int> kvp in result.Channels){
                Debug.Log(string.Format("==kvp.Key {0}, kvp.Value {1} ", kvp.Key, kvp.Value));
              }
            }
        } else {
            Debug.Log(status.Error);
            Debug.Log(status.ErrorData.Info);
        }
});
```