---
source_url: https://www.pubnub.com/docs/sdks/unity/api-reference/storage-and-playback
title: Message Persistence API for Unity SDK
updated_at: 2026-05-21T15:47:49.008Z
sdk_name: PubNub Unity SDK
sdk_version: v9.3.0
---

> 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


# Message Persistence API for Unity SDK

PubNub Unity SDK, use the latest version: v9.3.0

Message Persistence gives you real-time access to the history of messages published to PubNub. Each message is timestamped to the nearest 10 nanoseconds and stored across multiple availability zones in several geographic locations. You can encrypt stored messages with AES-256 so they are not readable on PubNub’s network. For details, see [Message Persistence](https://www.pubnub.com/docs/general/storage).

You control how long messages are stored through your account’s retention policy. Options include: 1 day, 7 days, 30 days, 3 months, 6 months, 1 year, or Unlimited.

You can retrieve the following:

* Messages
* Message reactions
* Files (using the File Sharing API)

## Fetch history

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Fetch historical messages from one or more channels. Use `includeMessageActions` to include message actions.

It's possible to control how messages are returned and in what order.

* If you specify only the `start` parameter (without `end`), you receive messages older than the `start` timetoken.
* If you specify only the `end` parameter (without `start`), you receive messages from that `end` timetoken and newer.
* If you specify both `start` and `end`, you receive messages between those timetokens (inclusive of `end`).

This function returns up to 100 messages on a single channel, or 25 per channel on up to 500 channels. To page, iteratively update the `start` timetoken.

### Method(s)

Use the following method(s) in the Unity SDK:

```csharp
pubnub.FetchHistory()
    .Channels(string[])
    .IncludeMeta(bool)
    .IncludeMessageType(bool)
    .IncludeCustomMessageType(bool)
    .IncludeUUID(bool)
    .IncludeMessageActions(bool)
    .Reverse(bool)
    .Start(int)
    .End(int)
    .MaximumPerChannel(int)
    .QueryParam(Dictionary<string, object>)
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| Channels | string[] | Yes |  | Specifies `channel` to return history messages from. Maximum of 500 channels are allowed. |
| IncludeMeta | bool | Optional |  | Whether meta (passed when Publishing the message) should be included in response or not. |
| IncludeMessageType | bool | Optional |  | Pass `true` to receive the message type with each history message. Default is `true`. |
| IncludeCustomMessageType | bool | Optional |  | Indicates whether to retrieve messages with the custom message type. For more information, refer to [Retrieving Messages](https://www.pubnub.com/docs/general/storage#retrieving-messages). |
| IncludeUUID | bool | Optional |  | Pass `true` to receive the publisher `uuid` with each history message. Default is `true`. |
| IncludeMessageActions | bool | Optional |  | The flag denoting to retrieve history messages with `message` actions. If `true`, the method is limited to one channel and 25 messages only. Default is `false`. |
| Reverse | bool | Optional |  | Setting to `true` will traverse the time line in reverse starting with the oldest message first. |
| Start | long | Optional |  | timetoken delimiting the `start` of time slice (exclusive) to pull messages from. |
| End | long | Optional |  | timetoken delimiting the `end` of time slice (inclusive) to pull messages from. |
| MaximumPerChannel | int | Optional |  | Specifies the number of historical messages to return. Default and maximum is 100 for a single channel, 25 for multiple channels, and 25 if `IncludeMessageActions` is `true`. |
| QueryParam | Dictionary<string, | Optional |  | QueryParam accepts a Dictionary object, the keys and values are passed as the query string parameters of the URL called by the API. |
| Execute | System.Action | Yes |  | `System.Action` of type `PNFetchHistoryResult`. |
| ExecuteAsync | None | Optional |  | Returns `Task<PNResult<PNFetchHistoryResult>>`. |

:::note Truncated response
If truncated, a `more` property will be returned with additional parameters. Make iterative calls adjusting parameters.
:::

### Sample code

:::tip Reference code
This example is a self-contained code snippet ready to be run. It includes necessary imports and executes methods with console logging. Use it as a reference when working with other examples in this document.
:::

Retrieve the last message on a channel:

```csharp
using PubnubApi.Unity;
using UnityEngine;

public class FetchLastMessageExample : MonoBehaviour {
	// Reference to a pubnub manager previously setup in Unity Editor
	// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
	[SerializeField] private PNManagerBehaviour pubnubManager;

	// An editor-serialized string for the channel ID
	[SerializeField] private string channelId = "my_channel";

	private async void Start() {
		// Getting a reference to the Pubnub instance
		var pubnub = pubnubManager.pubnub;

		// Note that you can also initialize Pubnub instance for Unity directly from code:
		/*
		PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
		{
			SubscribeKey = "demo",
			PublishKey = "demo",
		};
		Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);
		*/

		// Fetching the last message from the specified channel
		var fetchHistoryResponse = await pubnub.FetchHistory()
			.Channels(new string[] { channelId })
			.IncludeMeta(true)
			.MaximumPerChannel(1)  // Changed to 1 to specifically get the last message
			.IncludeCustomMessageType(true)
			.ExecuteAsync();

		// Checking the result and status of the fetch operation
		var fetchResult = fetchHistoryResponse.Result;
		var status = fetchHistoryResponse.Status;

		if (status.Error) {
			Debug.LogError($"Error fetching history: {status.ErrorData.Information}");
		} else if (fetchResult != null && fetchResult.Messages.ContainsKey(channelId)) {
			var messages = fetchResult.Messages[channelId];
			if (messages.Count > 0) {
				var lastMessage = messages[0];
				Debug.Log($"Last message: {lastMessage.Entry}, Timetoken: {lastMessage.Timetoken}");
			} else {
				Debug.Log("No messages found.");
			}
		}
	}
}
```

### Returns

The `FetchHistory()` operation returns a `PNFetchHistoryResult` that contains the following properties :

| Property Name | Type | Description |
| --- | --- | --- |
| Messages | `Dictionary<string, List<PNHistoryItemResult>>` | List of messages. |
| More | MoreInfo | Pagination information. |

The `Messages` has the following properties:

| Property Name | Type | Description |
| --- | --- | --- |
| → Channel Name | string | Name of the channel for which `FetchHistory()` has been executed. |
| → → timetoken | long | `timetoken` associated with the message. |
| → → Entry | object | Payload of the message. |
| → → Meta | object | Metadata associated with the message. |
| → → Uuid | string | `UUID` associated with the message. |
| → → MessageType | string | `MessageType` associated with the message. |
| → → CustomMessageType | string | The custom message type associated with the message. |
| → → Actions | object | Message Actions associated with the message. |

The `More` has following properties:

| Property Name | Type | Description |
| --- | --- | --- |
| → → Start | long | timetoken denoting the start of the requested range. |
| → → End | long | timetoken denoting the end of the requested range. |
| → → Limit | int | Number of messages returned in response. |

```json
{
    "Messages":
        {
            "my_channel":
            [{
                "Timetoken":15717278253295153,
                "Entry":"sample message",
                "Meta":"",
                "Uuid":"user-1",
                "MessageType":null,
                "Actions":null
            }]
        },
    "More":null
}
```

### Other examples

#### Retrieve the last 25 messages on a channel synchronously

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

pubnub.FetchHistory()
    .Channels(new string[] { "my_channel" })
    .IncludeMeta(true)
    .MaximumPerChannel(25)
    .Execute(new PNFetchHistoryResultExt((result, status) =>
    {

    }));
```

## Delete messages from history

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Removes the messages from the history of a specific channel.

:::note Required setting
Enable Delete-From-History in key settings and initialize with a secret key.
:::

### Method(s)

To `Delete Messages from History` you can use the following method(s) in the Unity SDK.

```csharp
pubnub.DeleteMessages()
    .Channel(string)
    .Start(long)
    .End(long)
    .QueryParam(Dictionary<string,object>)
```

| Parameter | Description |
| --- | --- |
| `Channel` *Type: string | Specifies `channel` messages to be deleted from history. |
| `Start`Type: long | timetoken delimiting the `Start` of time slice (inclusive) to delete messages from. |
| `End`Type: long | `timetoken` delimiting the End of time slice (exclusive) to delete messages from. |
| `QueryParam`Type: Dictionary`<string, object>` | Dictionary `object` to pass name/value pairs as query `string` params with PubNub URL request for debug purpose. |
| AsyncType: PNCallback | PNCallback of type PNDeleteMessageResult. |
| `Execute` *Type: `System.Action` | `System.Action` of type `PNDeleteMessageResult`. |
| `ExecuteAsync`Type: None | Returns `Task<PNResult<PNDeleteMessageResult>>`. |

### Sample code

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

PNResult<PNDeleteMessageResult> delMsgResponse = await pubnub.DeleteMessages()
    .Channel("history_channel")
    .Start(15088506076921021)
    .End(15088532035597390)
    .ExecuteAsync();

PNDeleteMessageResult delMsgResult = delMsgResponse.Result;
PNStatus status = delMsgResponse.Status;

if (status != null && status.Error)
{
    //Check for any error
    Debug.Log(status.ErrorData.Information);
}
else if (delMsgResult != null)
{
    //Expect empty object
    Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(delMsgResult));
}
```

### Returns

The `DeleteMessages()` operation returns a `PNResult<PNDeleteMessageResult>` which returns empty `PNDeleteMessageResult` object.

### Other examples

#### Delete messages sent in a particular timeframe

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

pubnub.DeleteMessages()
    .Channel("history_channel")
    .Start(15088506076921021)
    .End(15088532035597390)
    .Execute(new PNDeleteMessageResultExt(
        (result, status) => {
            if (status != null && status.Error) {
                //Check for any error
                Debug.Log(status.ErrorData.Information);
            }
            else if (result != null) {
                //Expect empty object
                Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(result));
            }
        }
    ));
```

#### Delete specific message from history

To delete a specific message, pass the `publish timetoken` (received from a successful publish) in the `End` parameter and `timetoken +/- 1` in the `Start` parameter. For example, if `15526611838554310` is the `publish timetoken`, pass `15526611838554309` in `Start` and `15526611838554310` in `End` parameters respectively as shown in the following code snippet.

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

PNResult<PNDeleteMessageResult> delMsgResponse = await pubnub.DeleteMessages()
    .Channel("history_channel")
    .Start(15526611838554309)
    .End(15526611838554310)
    .ExecuteAsync();

PNDeleteMessageResult delMsgResult = delMsgResponse.Result;
PNStatus status = delMsgResponse.Status;

if (status != null && status.Error)
{
    //Check for any error
    Debug.Log(status.ErrorData.Information);
}
else if (delMsgResult != null)
{
    //Expect empty object
    Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(delMsgResult));
}
```

## Message counts

:::warning Requires Message Persistence
This method requires that Message Persistence is [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/).
:::

Return the number of messages published since the given time; the count is messages with timetoken ≥ the provided value.

:::note Unlimited message retention
For keys with unlimited message retention enabled, this method considers only messages published in the last 30 days.
:::

### Method(s)

You can use the following method(s) in the Unity SDK:

```csharp
pubnub.MessageCounts()
    .Channels(string[])
    .ChannelsTimetoken(long[])
    .QueryParam(Dictionary<string, object>)
```

| Parameter | Description |
| --- | --- |
| `Channels` *Type: string[] | The `channels` to fetch the message count |
| `ChannelsTimetoken` *Type: long[] | Array of `timetokens`, in order of the channels list. Specify a single `timetoken` to apply it to all channels. Otherwise, the list of `timetokens` must be the same length as the list of channels, or the function returns a `PNStatus` with an error flag. |
| `QueryParam`Type: Dictionary`<string, object>` | Dictionary `object` to pass name/value pairs as query `string` params with PubNub URL request for debug purpose. |
| AsyncType: PNCallback | PNCallback of type PNMessageCountResult. |
| `Execute` *Type: `System.Action` | `System.Action` of type `PNMessageCountResult`. |
| `ExecuteAsync`Type: None | Returns `Task<PNResult<PNMessageCountResult>>`. |

### Sample code

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

PNResult<PNMessageCountResult> msgCountResponse = await pubnub.MessageCounts()
    .Channels(new string[] { "message_count_channel" })
    .ChannelsTimetoken(new long[] { 15088506076921021 })
    .ExecuteAsync();

PNMessageCountResult msgCountResult = msgCountResponse.Result;
PNStatus status = msgCountResponse.Status;

if (status != null && status.Error)
{
    //Check for any error
    Debug.Log(status.ErrorData.Information);
}
else
{
    Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(msgCountResult));
}
```

### Returns

The operation returns a `PNResult<PNMessageCountResult>` which contains the following operations:

| Property Name | Type | Description |
| --- | --- | --- |
| `Result` | PNMessageCountResult | Returns a `PNMessageCountResult` object. |
| `Status` | PNStatus | Returns a `PNStatus` object |

`PNMessageCountResult` contains the following properties:

| Property Name | Type | Description |
| --- | --- | --- |
| `Channels` | `Dictionary<string, long>` | Collection of `channels` along with the messages count. `channels` without messages have a count of 0. `channels` with 10,000 messages or more have a count of 10000. |

### Other examples

#### Retrieve count of messages for a single channel

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

pubnub.MessageCounts()
    .Channels(new string[] { "message_count_channel" })
    .ChannelsTimetoken(new long[] { 15088506076921021 })
    .Execute(new PNMessageCountResultExt(
    (result, status) => {
        if (status != null && status.Error)
        {
            //Check for any error
            Debug.Log(status.ErrorData.Information);
        }
        else
        {
            Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(result));
        }
    }));
```

#### Retrieve count of messages using different timetokens for each channel

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

PNResult<PNMessageCountResult> msgCountResponse = await pubnub.MessageCounts()
    .Channels(new string[] { "message_count_channel", "message_count_channel2" })
    .ChannelsTimetoken(new long[] { 15088506076921021, 15088506076921131 })
    .ExecuteAsync();

PNMessageCountResult msgCountResult = msgCountResponse.Result;
PNStatus status = msgCountResponse.Status;

if (status != null && status.Error)
{
    //Check for any error
    Debug.Log(status.ErrorData.Information);
}
else
{
    Debug.Log(pubnub.JsonPluggableLibrary.SerializeToJsonString(msgCountResult));
}
```

## Terms in this document

* **Channel** - A pathway for sending and receiving messages between devices, created automatically when you first use it, that can handle any number of users and messages for different communication needs, like 1-1 text chats, group conversations, and other data streaming.
* **Message** - A unit of data transmitted between clients or between a client and a server in PubNub, containing information such as text, binary data, or structured data formats like JSON. Messages are sent over channels and can be tracked for delivery and read status.
* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
* **Timetoken** - A unique identifier for each message that represents the number of 100-nanosecond intervals since January 1, 1970, for example, 16200000000000000.