---
source_url: https://www.pubnub.com/docs/sdks/c-core/api-reference/storage-and-playback
title: Message Persistence API for C-Core SDK
updated_at: 2026-06-19T11:36:47.359Z
sdk_name: PubNub C/C++ Core SDK
sdk_version: 7.2.3
---

> 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-Core SDK

PubNub C/C++ Core SDK, use the latest version: 7.2.3

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
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-).
:::

This function fetches historical messages from one or multiple channels. The `include_message_actions` flag also allows you to fetch message actions along with the messages.

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](https://www.pubnub.com/docs/sdks/objective-c/api-reference/misc#time).
* 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, or 25 messages per channel when querying multiple channels (up to 500 channels). If more messages match the time range, make iterative calls and adjust the `start` timetoken to page through the results.

### Method(s)

To fetch messages, use the following method(s) in the Objective-C SDK. This method uses the builder pattern; you can omit any optional arguments.

```c
enum pubnub_res pubnub_fetch_history(
    pubnub_t* pb,
    char const* channel,
    struct pubnub_fetch_history_options opt);
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| pb | pubnub_t* | Yes |  | Pointer to PubNub client context. Can't be NULL. |
| channel | const | Yes |  | The `string` with the `channel` name to fetch the `history`. |
| opt | struct | Yes |  | Options struct for configuring the fetch history request. |
| > max_per_channel | unsigned | Yes |  | Specifies the number of historical messages to return per channel. Maximum value is 100 for a single channel or 25 for multiple channels. |
| > include_meta | pubnub_tribool | Optional |  | Whether event metadata should be included in the response. |
| > include_message_type | pubnub_tribool | Optional |  | Indicates whether to retrieve messages with PubNub message type. Default is `YES`. For more information, refer to [Retrieving Messages](https://www.pubnub.com/docs/general/storage#retrieve-messages). |
| > include_custom_message_type | pubnub_tribool | 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#retrieve-messages). |
| > include_user_id | pubnub_tribool | Optional |  | Pass `YES` to receive the publisher uuid with each history message. Default is `YES`. |
| > include_message_actions | pubnub_tribool | Optional |  | Whether event actions should be included in the response. When `YES`, throws an exception if you call the method with more than one `channel`. |
| > reverse | pubnub_tribool | Optional |  | Setting to `YES` traverses the time line in reverse, starting with the oldest message first. |
| > start | char | Optional |  | Timetoken for oldest event starting from which next should be returned events. Value will be converted to required precision internally. |
| > end | char | Optional |  | Timetoken for latest event till which events should be pulled out. Value will be converted to required precision internally. |

### Sample code

```c
struct pubnub_fetch_history_options options = pubnub_fetch_history_defopts();
options.include_custom_message_type = true;
// Fetch history and check whether operation started or not.
if (pubnub_fetch_history(ctx, "my_channel1,my_channel2", options) != PNR_IN_PROGRESS) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

// Wait for `history` operation completion before trying to get a service response.
enum pubnub_res pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

pubnub_chamebl_t response = pubnub_get_fetch_history(ctx);
```

### Response

This method returns a `enum pubnub_res pbresult` on success:

```c
enum pubnub_res pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

pubnub_chamebl_t response = pubnub_get_fetch_history(ctx);
```

### Other examples

#### Fetch messages with metadata

```objectivec
struct pubnub_fetch_history_options options = pubnub_fetch_history_defopts();
options.include_meta = true;

// Fetch history and check whether operation started or not.
if (pubnub_fetch_history(ctx, "my_channel1,my_channel2", options) != PNR_IN_PROGRESS) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

// Wait for `history` operation completion before trying to get a service response. 
enum pubnub_res pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

pubnub_chamebl_t response = pubnub_get_fetch_history(ctx);
```

#### Fetch messages with actions

```objectivec
struct pubnub_fetch_history_options options = pubnub_fetch_history_defopts();
options.include_message_actions = true;

// Fetch history and check whether operation started or not.
if (pubnub_fetch_history(ctx, "my_channel1,my_channel2", options) != PNR_IN_PROGRESS) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

// Wait for `history` operation completion before trying to get a service response. 
enum pubnub_res pbresult = pubnub_await(ctx);
if (pbresult != PNR_OK) {
    printf("Failed to fetch history, error %d\n", pbresult);
    pubnub_free(ctx);
    return -1;
}

pubnub_chamebl_t response = pubnub_get_fetch_history(ctx);
```

## History

This function fetches historical messages of a channel. Message Persistence provides real-time access to an unlimited history for all messages published to PubNub. Stored messages are replicated across multiple availability zones in several geographical data center locations. Stored messages can be encrypted with AES-256 message encryption ensuring that they are not readable while stored on PubNub's network. It is possible to control how messages are returned and in what order, for example you can:

* Limit the number of messages to a specific quantity using the `4th argument` parameter.

### Method(s)

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

```c
enum pubnub_res pubnub_history (
  pubnub_t *p,
  const char *channel,
  const char *channel_group,
  unsigned count
)
```

| Parameter | Description |
| --- | --- |
| `p` *Type: pubnub_t* | Pointer to PubNub client context. Can't be NULL. |
| `channel` *Type: const char* | The `string` with the `channel` name to fetch the `history` |
| `count` *Type: unsigned | Maximum number of messages to get. If there are less than this available on the `channel`, you'll get less, but you can't get more. |
| `include_token` *Type: bool | If `true`, include the [timetoken](https://www.pubnub.com/docs/sdks/c-core/api-reference/misc#time) for every `message`. default: `false` |

### Sample code

Retrieve the last 100 messages on a channel:

```c
// Sync
enum pubnub_res res;

pubnub_history(pn, "history_channel", 10, false);
res = pubnub_await(pn);

if (PNR_OK == res) {
  puts("Got history! Messages:");
  for (;;) {
    const char *msg = pubnub_get(pn);
    if (NULL == msg) {
      break;
    }
    puts(msg);
  }
} else {
  printf("Getting history failed with code: %d\n", res);
}

// Callback
void get_last_10_messages(pubnub_t *pn) {
  enum pubnub_res res;
  const char *msg;

  pubnub_register_callback(pn, sample_callback);
  pubnub_history(pn, "history_channel", 10, false);
  await();
  res = pubnub_last_result(pn);

  if (PNR_OK == res) {
    puts("Got history!");
    for (;;) {
      msg = pubnub_get(pn);
      if (NULL == msg) {
        break;
      }
      puts(msg);
    }
  } else {
    printf("Getting history failed with code: %d\n", res);
  }
}
```

### Rest response from server

An array is returned on success. The `pubnub_history()` function returns a list of up to 100 messages, the timetoken of the first (oldest) message and the timetoken of the last (newest) message in the resulting set of messages. The output below demonstrates the format for a `pubnub_history()` response:

```javascript
[
    ["message1", "message2", "message3",... ],
    "Start Time Token",
    "End Time Token"
]
```

## Extended history

### Extended history options structure

Options for `extended` history.

### Method(s)

### Declaration

```c
struct pubnub_history_options {
    bool string_token;
    int count;
    bool reverse;
    char const* start;
    char const* end;
    bool include_token;
}
```

### Members

| Member | Type | Description |
| --- | --- | --- |
| `string_token` | bool | If `false`, the returned start and end `Timetoken` values will be returned as long ints. If `true`, the start and end `Timetoken` values will be returned as strings. Default is `false`. |
| `count` | int | The maximum number of messages to return per response. Has to be between 1 and 100 messages. Default is 100. |
| `reverse` | bool | Direction of time traversal. Default is false, which means timeline is traversed newest to oldest. |
| `start` | char const* | If provided (not NULL), lets you select a "start date," in `Timetoken` format. If not provided, it will default to current time. Page through results by providing a start OR end timetoken. Retrieve a slice of the time line by providing both a start AND end timetoken. start is `exclusive` – that is, the first item returned will be the one immediately after the start `Timetoken` value. Default is `NULL`. |
| `end` | char const* | If provided (not NULL), lets you select an "end date," in `Timetoken` format. If not provided, it will provide up to the number of messages defined in the "count" parameter. Page through results by providing a start OR end timetoken. Retrieve a slice of the time line by providing both a start AND end timetoken. End is `exclusive` – that is, if a message is associated exactly with the end `Timetoken`, it will be included in the result. Default is `NULL`. |
| `include_token` | bool | Pass true to receive a `timetoken` with each history message. Defaults to `false`. |

### Initialize extended history options

This returns the `default` options for publish V1 transactions. It's best to always call it to initialize the `pubnub_history_options()`, since it has several parameters.

### Method(s)

### Declaration

```c
struct pubnub_history_options pubnub_history_defopts(void)
```

### Parameters

This Method has no arguments.

### Sample code

```c
struct pubnub_history_options opts = pubnub_history_defopts();
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `struct pubnub_history_options` |  | The `default` options for here-now. |

### Extended history

The extended `history`. It is basically the same as the `pubnub_history()`, just adding a few options that will be sent.

### Method(s)

### Declaration

```c
enum pubnub_res pubnub_history_ex(
  pubnub_t* pb,
  char const* channel,
  struct pubnub_history_options opt
)
```

### Parameters

| Parameter | Description |
| --- | --- |
| `pb`Type: pubnub_t* | The Pubnub context |
| `channel`Type: char const* | The string with the channel name to get history for. |
| `opt`Type: struct pubnub_history_options | History options. |

### Sample code

```c
struct pubnub_history_options opt = pubnub_history_defopts();
opt.reverse = true;
pbresult = pubnub_history_ex(pn, "my_channel", opt);
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_STARTED | Success. |
|  | other | Indicates the type of error. |

## Message counts

Return the number of messages published on one or more channels since a given time.

### Message count per channel structure

Contains channel name (as char memory block, *aka* `Pascal string`) and message count for messages received on the channel. The message counts are the number of messages published on one or more channels since a given time.

Used to store information retrieved by `pubnub_message_counts()` transaction.

### Declaration

```c
struct pubnub_chan_msg_count {
    pubnub_chamebl_t channel;
    size_t message_count;
}
```

### Members

| Members | Type | Description |
| --- | --- | --- |
| `channel` | pubnub_chamebl_t | Channel name as a char memory block (aka `Pascal string`) |
| `message_count` | size_t | Number of messages for the channel |

### Count channels in message count response

Returns the number of members (key->value pairs) of JSON object `channels` in the response to a `message count` transaction.

### Declaration

```c
int pubnub_get_chan_msg_counts_size(pubnub_t* pb)
```

### Parameters

| Parameter | Description |
| --- | --- |
| `pb` *Type: pubnub_t* | The Pubnub context in which to parse the response |

### Sample code

```c
int n  = pubnub_get_chan_msg_counts_size(pb);
printf("There are %d channels in the message counts response\n", n);
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `int` | -1 | Error (transaction still in progress or other) |
|  | >= 0 | The number of members of the `channels` JSON object) |

### Message counts transaction

Starts the transaction `pubnub_message_counts` on the context `@p` pb for the list of channels `@p` channel for message counts starting from `@p` timetoken, which can be a single timetoken or a comma-separated list of timetokens (corresponding to the `channel` list).

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

### Declaration

```c
enum pubnub_res pubnub_message_counts(
  pubnub_t* pb,
  char const* channel,
  char const* timetoken
)
```

### Parameters

| Parameter | Description |
| --- | --- |
| `pb` *Type: pubnub_t* | The PubNub context to use for the transaction. |
| `channel` *Type: char const* | The string with the channel name (or comma-delimited list of channel names) to fetch message counts for. |
| `timetoken` *Type: char const* | A single token, or a comma-separated list of tokens. If single, it is used for all channels. If a list, it must have the same number of elements as `channel` (each used in-order). |

### Sample code

```c
/** Obtain message count for
        channel 'one' since 15517035062228243 and
        channel 'two' since 15517035052228243. */
pbres = pubnub_message_counts(pb, "one,two", "15517035062228243,15517035052228243");
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `enum pubnub_res` | PNR_STARTED | Started |
|  | PNR_OK | Finished with a success (can only happen in the sync interface) |
|  | other | Failed, Indicates the type of error |

### Example: single token for all channels

```c
/** Obtain message count for channels
        'one' and 'two' since 15517035062228243 */
pbres = pubnub_message_counts(pb, "one,two", "15517035062228243");
```

### Read all channel message counts

On input, `@p` `io_count` is the number of allocated `counters per channel` (array dimension of `@p` `chan_msg_counters`). On output (`@p` `io_count`), the number of counters per channel written. If there are more channel message counters in the answer than there are in the allocated array, the extras are not written. To determine how many elements there are (and allocate accordingly), use `pubnub_get_chan_msg_counts_size()`.

This is an alternative to `pubnub_get_message_counts()`, useful for exploratory usage to obtain all channel message counters that are in the report, without keeping track of requested channels.

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

### Declaration

```c
int pubnub_get_chan_msg_counts(
  pubnub_t* pb,
  size_t* io_count,
  struct pubnub_chan_msg_count* chan_msg_counters
)
```

### Parameters

| Parameter | Description |
| --- | --- |
| `pb` *Type: pubnub_t* | The Pubnub context to use for transaction |
| `io_count` *Type: size_t* | On input, the number of allocated `counters per channel` (array dimension of `@p` `chan_msg_counters`). On output, number of counters per channel written to `@p` `chan_msg_counters`. |
| `chan_msg_counters` *Type: struct pubnub_chan_msg_count* | An array of channel message counters, allocated by the caller, filled in by this function. |

### Sample code

```c
struct pubnub_chan_msg_count chan_msg_count[CHAN_COUNT];
size_t count = CHAN_COUNT;
int rslt = pubnub_get_chan_msg_counts(pb, &count, chan_msg_count);
if (0 == rslt) {
    size_t i;
    for (i = 0; i < count; ++i) {
        printf("  Channel    = '%.*s'\n", (int)chan_msg_count[i].channel.size, chan_msg_count[i].channel.ptr);
        printf("  Message count  = %z'\n", chan_msg_count[i].message_count);
    }
}
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `int` | 0 | Success (even if there are more channel message counts than is possible to write to `@p` `chan_msg_counters`) |
|  | -1 | Error, messages not read (transaction still in progress, format error...) |

### Example: Make sure we have room for all the channel message counters

```c
int n  = pubnub_get_chan_msg_counts_size(pb);
if (n > 0) {
    struct pubnub_chan_msg_count *pchmsc = malloc(n * sizeof *pchmsc);
    if (pchmsc != NULL) {
        size_t count = n;
        int rslt = pubnub_get_chan_msg_counts(pb, &count, pchmsc);
        if (0 == rslt) {
            printf("Got %z counters, should be the same as %d\n", count, n);
        }
        free(pchmsc);
    }
}
```

### Read counters for channels

Reads message counters for the given channels (in `@p` channel) from the PubNub response to `pubnub_message_counts()`.

Array dimension for `@p` `o_count` is the number of channels from the comma-separated channel list `@p` channel and it (`@p` `o_count` array) is allocated by the caller.

Message counts order in `@p` `o_count` corresponds to channel order in `@p` channel list. If a requested channel is not found in the PubNub response, the corresponding `@p` `o_count` entry has a negative value.

If the response contains a channel name not present in the request list, that is not considered an error.

This is an alternative to `pubnub_get_chan_msg_count()`, useful if you already maintain the list of channels and just want the message counters.

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

### Declaration

```c
int pubnub_get_message_counts(
  pubnub_t* pb,
  char const*channel,
  int* o_count
)
```

### Parameters

| Parameter | Description |
| --- | --- |
| `pb` *Type: pubnub_t* | The Pubnub context to use for transaction |
| `channel` *Type: char const* | A comma-separated list of channels to read message counters from |
| `o_count` *Type: int* | An array of message counters, allocated by the caller, filled in by this function. |

### Sample code

```c
int msg_count[2];
int rslt = pubnub_get_message_counts(pb, "one,two", msg_count);
if (0 == rslt) {
    printf("message counts: one -> %d, two -> %d\n", msg_count[0], msg_count[1]);
}
```

### Returns

| Type | Value | Description |
| --- | --- | --- |
| `int` | 0 | Success (even if there are channel message counts int `@p` channel that are not there in the response or vice-versa) |
|  | -1 | Error, messages not read (transaction still in progress, format error...) |

## Terms in this document

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