---
source_url: https://www.pubnub.com/docs/sdks/c-sharp/api-reference/storage-and-playback
title: Message Persistence API for C# SDK
updated_at: 2026-06-08T11:40:39.121Z
sdk_name: PubNub C# SDK
sdk_version: 8.2.1
---

> 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 C# SDK

PubNub C# SDK, use the latest version: 8.2.1

Install:

```bash
dotnet add package PubNub@8.2.1
```

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)

:::tip Request execution
Use `try`/`catch` when working with the C# SDK.
If a request has invalid parameters (for example, a missing required field), the SDK throws an exception. If the request reaches the server but fails (server error or network issue), the error details are available in the returned `status`.
```csharp
try
{
    PNResult<PNPublishResult> publishResponse = await pubnub.Publish()
        .Message("Why do Java developers wear glasses? Because they can't C#.")
        .Channel("my_channel")
        .ExecuteAsync();
    PNStatus status = publishResponse.Status;
    Console.WriteLine("Server status code : " + status.StatusCode.ToString());
}
catch (Exception ex)
{
    Console.WriteLine($"Request can't be executed due to error: {ex.Message}");
}
```
:::

## Fetch history

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

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

You can 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 retrieve messages between those timetokens (inclusive of the `end` value).

You can receive up to 100 messages for a single channel. For multiple channels (up to 500), you can receive up to 25 messages per channel. If more messages match the time range, make iterative calls and adjust the `start` timetoken to page through the results.

### Method(s)

Use the following method(s) in the C# 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 |  | Channels to fetch history messages from (up to 500). |
| IncludeMeta | bool | Optional |  | Whether to include the meta object (if provided at publish time) in the response. |
| IncludeMessageType | bool | Optional |  | Whether to include message type. Default is `true`. |
| IncludeCustomMessageType | bool | Optional |  | Whether to retrieve messages with the custom message type. See [Retrieving Messages](https://www.pubnub.com/docs/general/storage#retrieving-messages). |
| IncludeUUID | bool | Optional |  | Whether to receive the publisher `uuid`. Default is `true`. |
| IncludeMessageActions | bool | Optional |  | Retrieve history messages with message actions. If `true`, limited to one channel and 25 messages. Default is `false`. |
| Reverse | bool | Optional |  | Traverse from oldest to newest when set to `true`. |
| Start | long | Optional |  | Timetoken delimiting the start (exclusive) of the time slice. |
| End | long | Optional |  | Timetoken delimiting the end (inclusive) of the time slice. |
| MaximumPerChannel | int | Optional |  | Number of historical messages to return. Default and maximum are 100 for a single channel, 25 for multiple channels, and 25 if `IncludeMessageActions` is `true`. |
| QueryParam | Dictionary<string, | Optional |  | Pass name/value pairs as query string parameters. Useful for adding custom debug information or additional parameters required by specific PubNub features. |
| Execute | PNCallback | Yes |  | `PNCallback` of type `PNFetchHistoryResult`. |
| ExecuteAsync | None | Optional |  | Returns `PNResult<PNFetchHistoryResult>`. |

:::note Truncated response
If truncated, a `more` property is returned; 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;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
try
{
    // Fetch historical messages
    PNResult<PNFetchHistoryResult> fetchHistoryResponse = await pubnub.FetchHistory()
        .Channels(new string[] { "my_channel" })
        .IncludeMeta(true)
        .IncludeCustomMessageType(true)
        .MaximumPerChannel(25)
        .ExecuteAsync();
            
    PNFetchHistoryResult fetchHistoryResult = fetchHistoryResponse.Result;
    PNStatus status = fetchHistoryResponse.Status;

    if (!status.Error && fetchHistoryResult != null)
    {
        Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fetchHistoryResult));
    }
    else
    {
        Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(status));
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Request cannot be executed due to error: {ex.Message}");
}
```

### Returns

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

### Other examples

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

```csharp
using PubnubApi;

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

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

    }));
```

## Delete messages from history

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

Remove messages from the history of a specific channel.

:::note Required setting
To accept delete-from-history requests, enable the `Delete-From-History` setting for your key in the Admin Portal and [initialize the SDK](https://www.pubnub.com/docs/sdks/c-sharp/api-reference/configuration) with a secret key.
:::

### Method(s)

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

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

| Parameter | Description |
| --- | --- |
| `Channel` *Type: string | Channel to delete messages from. |
| `Start`Type: long | Timetoken delimiting the start of the time slice (inclusive) to delete messages from. |
| `End`Type: long | Timetoken delimiting the end of the time slice (exclusive) to delete messages from. |
| `QueryParam`Type: Dictionary`<string, object>` | Pass name/value pairs as query string parameters. Useful for adding custom debug information or additional parameters required by specific PubNub features. |
| AsyncType: PNCallback | PNCallback of type PNDeleteMessageResult. This parameter is deprecated and will be removed in a future version. Please use the `ExecuteAsync` parameter instead. |
| `Execute` *Type: PNCallback | `PNCallback` of type `PNDeleteMessageResult`. |
| `ExecuteAsync`Type: None | Returns `PNResult<PNDeleteMessageResult>`. |

### Sample code

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
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
    Console.WriteLine(status.ErrorData.Information);
}
else if (delMsgResult != null)
{
    //Expect empty object
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(delMsgResult));
}
```

### Returns

The `DeleteMessages()` operation returns a `PNResult<PNDeleteMessageResult>` that contains an empty `PNDeleteMessageResult` object.

### Other examples

#### Delete messages sent in a particular timeframe

```csharp
using PubnubApi;

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

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

#### Delete specific message from history

To delete a specific message, set `End` to the message’s publish timetoken and set `Start` to that timetoken minus 1. 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;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
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
    Console.WriteLine(status.ErrorData.Information);
}
else if (delMsgResult != null)
{
    //Expect empty object
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(delMsgResult));
}
```

## Message counts

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

Return the number of messages published since a given time. The count is messages with a timetoken greater than or equal to `ChannelsTimetoken`.

:::note Unlimited message retention
For keys with unlimited message retention enabled, only messages from the last 30 days are counted.
:::

### Method(s)

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

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

| Parameter | Description |
| --- | --- |
| `Channels` *Type: string[] | Channels to fetch the message count for. |
| `ChannelsTimetoken` *Type: long[] | Array of `timetokens`, in the same order as the channels list. Specify a single `timetoken` to apply it to all channels; otherwise, lengths must match or the function returns a `PNStatus` error. |
| `QueryParam`Type: Dictionary`<string, object>` | Pass name/value pairs as query string parameters. Useful for adding custom debug information or additional parameters required by specific PubNub features. |
| AsyncType: PNCallback | PNCallback of type PNMessageCountResult. This parameter is deprecated and will be removed in a future version. Please use the `ExecuteAsync` parameter instead. |
| `Execute` *Type: PNCallback | PNCallback of type `PNMessageCountResult`. |
| `ExecuteAsync`Type: None | Returns `PNResult<PNMessageCountResult>`. |

### Sample code

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
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
    Console.WriteLine(status.ErrorData.Information);
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(msgCountResult));
}
```

### Returns

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

| 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` with message counts. `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;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
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
            Console.WriteLine(status.ErrorData.Information);
        }
        else
        {
            Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(result));
        }
    }));
```

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

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
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
    Console.WriteLine(status.ErrorData.Information);
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(msgCountResult));
}
```

## History (deprecated)

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

:::warning Deprecated
This method is deprecated and will be removed in a future version. Please use the `fetchHistory()` method instead.
:::

Fetch historical messages for a channel.

Control how messages are returned and in what order:

For example, you can:

* Search from the newest end of the timeline (default: `Reverse` = `false`).
* Search from the oldest end of the timeline by setting `Reverse` to `true`.
* Page through results by providing a `Start` or `End` timetoken.
* Retrieve a slice of the timeline by providing both a `Start` and `End` timetoken.
* Limit the number of messages using the `Count` parameter.

:::tip Start & End parameter usage clarity
If you specify only `Start` (without `End`), you receive messages older than and up to that `Start` timetoken. If you specify only `End` (without `Start`), you receive messages that match that `End` timetoken and newer. If you specify both `Start` and `End`, you receive messages between those timetokens (inclusive of `End`). You still receive up to 100 messages even if more match. Make iterative calls to history, adjusting `Start`, to page through the full result set.
:::

#### Method(s) (deprecated)

To run `History` you can use the following method(s) in the C# SDK:

```csharp
pubnub.History()
        .Channel(string)
        .IncludeMeta(bool)
        .Reverse(bool)
        .IncludeTimetoken(bool)
        .Start(long)
        .End(long)
        .count(int)
        .QueryParam(Dictionary<string,object>)
```

| Parameter | Description |
| --- | --- |
| `Channel` *Type: string | Channel to return history messages from. |
| `IncludeMeta`Type: bool | Whether to include the meta object (if provided at publish time) in the response. |
| `Reverse`Type: bool | Traverse from oldest to newest when set to `true`. |
| `IncludeTimetoken`Type: bool | Whether to include message `timetokens` in the response. |
| `Start`Type: long | Timetoken delimiting the start (exclusive) of the time slice. |
| `End`Type: long | Timetoken delimiting the end (inclusive) of the time slice. |
| `Count`Type: int | Number of historical messages to return. |
| `QueryParam`Type: Dictionary`<string, object>` | Pass name/value pairs as query string parameters. Useful for adding custom debug information or additional parameters required by specific PubNub features. |
| AsyncType: PNCallback | PNCallback of type PNHistoryResult. This parameter is deprecated and will be removed in a future version. Please use the `ExecuteAsync` parameter instead. |
| `Execute` *Type: PNCallback | `PNCallback` of type `PNHistoryResult`. |
| `ExecuteAsync`Type: None | Returns `PNResult<PNHistoryResult>`. |

:::tip Using the reverse parameter
Messages are always returned sorted in ascending time direction from history regardless of `Reverse`. The `Reverse` direction matters when you have more than 100 (or `Count`, if it's set) messages in the time interval, in which case `Reverse` determines the end of the time interval from which it should start retrieving the messages.
:::

#### Sample code (deprecated)

Retrieve the last 100 messages on a channel:

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
PNResult<PNHistoryResult> historyResponse = await pubnub.History()
    .Channel("history_channel") // where to fetch history from
    .Count(100) // how many items to fetch
    .ExecuteAsync();
```

#### Returns (deprecated)

The `History()` operation returns a `PNResult<PNHistoryResult>` which contains the following properties:

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

`PNHistoryResult` which contains the following properties:

| Property Name | Type | Description |
| --- | --- | --- |
| `Messages` | List`<PNHistoryItemResult>` | List of messages of type `PNHistoryItemResult`. See [PNHistoryItemResult](#pnhistoryitemresult-deprecated) for more details. |
| `StartTimetoken` | long | Start `timetoken`. |
| `EndTimetoken` | long | End `timetoken`. |

##### PNHistoryItemResult (deprecated)

| Property Name | Type | Description |
| --- | --- | --- |
| `Timetoken` | long | `Timetoken` of the message. |
| `Entry` | object | Message. |

#### Other examples (deprecated)

##### Retrieve the last 100 messages on a channel synchronously (deprecated)

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
pubnub.History()
    .Channel("history_channel") // where to fetch history from
    .Count(100) // how many items to fetch
    .Execute(new PNHistoryResultExt(
        (result, status) => {
        }
    ));
```

##### Use history() to retrieve the three oldest messages by retrieving from the time line in reverse (deprecated)

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
PNResult<PNHistoryResult> historyResponse = await pubnub.History()
    .Channel("my_channel") // where to fetch history from
    .Count(3) // how many items to fetch
    .Reverse(true) // should go in reverse?
    .ExecuteAsync();
```

##### Response (deprecated)

```json
{
    "Messages":[
        {
            "Timetoken": 0,
            "Entry": "Pub1"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub2"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub3"
        }
    ],
    "StartTimeToken": 13406746729185766,
    "EndTimeToken": 13406746780720711
}
```

##### Use history() to retrieve messages newer than a given timetoken by paging from oldest message to newest message starting at a single point in time (exclusive) (deprecated)

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
PNResult<PNHistoryResult> historyResponse = await pubnub.History()
    .Channel("my_channel") // where to fetch history from
    .Start(13847168620721752L) // first timestamp
    .Reverse(true) // should go in reverse?
    .ExecuteAsync();
```

##### Response (deprecated)

```json
{
    "Messages":[
        {
            "Timetoken": 0,
            "Entry": "Pub3"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub4"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub5"
        }
    ],
    "StartTimeToken": 13406746780720711,
    "EndTimeToken": 13406746845892666
}
```

##### Use history() to retrieve messages until a given timetoken by paging from newest message to oldest message until a specific end point in time (inclusive) (deprecated)

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
PNResult<PNHistoryResult> historyResponse = await pubnub.History()
    .Channel("my_channel") // where to fetch history from
    .Count(100) // how many items to fetch
    .Start(-1) // first timestamp
    .End(13847168819178600L) // last timestamp
    .Reverse(true) // should go in reverse?
    .ExecuteAsync();
```

##### Response (deprecated)

```json
{
    "Messages":[
        {
            "Timetoken": 0,
            "Entry": "Pub3"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub4"
        },
        {
            "Timetoken": 0,
            "Entry": "Pub5"
        }
    ],
    "StartTimeToken": 13406746780720711,
    "EndTimeToken": 13406746845892666
}
```

##### History paging example (deprecated)

:::note Usage
You can call the method by passing 0 or a valid **timetoken** as the argument.
:::

```csharp
using PubnubApi;

public class PubnubRecursiveHistoryFetcher {
    private static Pubnub pubnub;

    public abstract class CallbackSkeleton {
        public abstract void HandleResponse(PNHistoryResult result);
    }

    public PubnubRecursiveHistoryFetcher() {
        // NOTICE: for demo/demo pub/sub keys Message Persistence is disabled,
        // so use your pub/sub keys instead
        PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"));
        pnConfiguration.SubscribeKey = "demo";
        pubnub = new Pubnub(pnConfiguration);
    }

    static public void Main() {
        PubnubRecursiveHistoryFetcher fetcher = new PubnubRecursiveHistoryFetcher();
        GetAllMessages(new DemoCallbackSkeleton());
    }

    public static void GetAllMessages(CallbackSkeleton callback) {
        GetAllMessages(-1L, callback);
    }

    public static void GetAllMessages(long startTimestamp, CallbackSkeleton callback) {
        CountdownEvent latch = new CountdownEvent(1);

        pubnub.History()
            .Channel("history_channel") // where to fetch history from
            .Count(100) // how many items to fetch
            .Start(startTimestamp) // first timestamp
            .Execute(new DemoHistoryResult(callback));
    }

    public class DemoHistoryResult : PNCallback<PNHistoryResult> {
        CallbackSkeleton internalCallback;
        public DemoHistoryResult(CallbackSkeleton callback) {
            this.internalCallback = callback;
        }
        public override void OnResponse(PNHistoryResult result, PNStatus status) {
            if (!status.Error && result != null && result.Messages != null && result.Messages.Count > 0) {
                Console.WriteLine(result.Messages.Count);
                Console.WriteLine("start:" + result.StartTimeToken.ToString());
                Console.WriteLine("end:" + result.EndTimeToken.ToString());

                internalCallback.HandleResponse(result);
                GetAllMessages(result.EndTimeToken, this.internalCallback);
            }
        }
    };

    public class DemoCallbackSkeleton : CallbackSkeleton {
        public override void HandleResponse(PNHistoryResult result) {
            //Handle the result
        }
    }
}
```

##### Include timetoken in history response (deprecated)

```csharp
using PubnubApi;

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

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
PNResult<PNHistoryResult> historyResponse = await pubnub.History()
    .Channel("history_channel") // where to fetch history from
    .Count(100) // how many items to fetch
    .IncludeTimetoken(true) // include timetoken with each entry
    .ExecuteAsync();
```

## 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.