Warning: You are viewing an old version (0.8) of this documentation. We recommend you view the latest version 1.2.
Realtime Client Library API

History

The Realtime client library provides message and presence event history for channels. Channel history can be used to return continuous message history up to the exact point a realtime channel was attached, and combines both instantaneous “live” history as well as the longer term persisted history. If persisted history is enabled for the channel, then messages will typically be stored for 24 – 72 hours on disk. If persisted history is not enabled, Ably retains the last two minutes of instantaneous “live” message history in memory.

Getting started

The Ably Realtime client library provides a straightforward API to retrieve “paginated”/api/realtime-sdk/history#paginated-result message or presence event history. Each page of history, by default, contains up to 100 messages. Message ordering, by default, is from most recent to oldest.

var realtime = new Ably.Realtime('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM');
var channel = realtime.channels.get('big-dip-dew');
channel.publish('example', 'message data', function(err) {
  channel.history(function(err, resultPage) {
    var lastMessage = resultPage.items[0];
    alert('Last message: ' + lastMessage.id + ' - ' + lastMessage.data);
  });
});
var realtime = new Ably.Realtime('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM');
var channel = realtime.channels.get('big-dip-dew');
channel.publish('example', 'message data', function(err) {
  channel.history(function(err, resultPage) {
    var lastMessage = resultPage.items[0];
    console.log('Last message: ' + lastMessage.id + ' - ' + lastMessage.data);
  });
});
realtime = Ably::Realtime.new('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM')
channel = realtime.channels.get('big-dip-dew')
channel.publish 'example', 'message data' do
  channel.history do |result_page|
    last_message = result_page.items.last
    puts "Last message: #{last_message.message.id} - #{last_message.data}")
  end
end
AblyRealtime realtime = new AblyRealtime("xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM");
Channel channel = realtime.channels.get("big-dip-dew");
channel.publish("example", "message data", new CompletionListener() {
  @Override
  public void onError(ErrorInfo reason) {
    System.out.println("Unable to publish message; err = " + reason.message);
  }
  @Override
  public void onSuccess() {
    PaginatedResult<Message> resultPage = channel.history(null);
    Message lastMessage = resultPage.items[0];
    System.out.println("Last message: " + lastMessage.id + " - " + lastMessage.data);
  }
});
AblyRealtime realtime = new AblyRealtime("xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM");
IRealtimeChannel channel = realtime.Channels.Get("big-dip-dew");
channel.Publish("example", "message data", async (success, error) =>
{
    PaginatedResult<Message> resultPage = await channel.HistoryAsync(null);
    Message lastMessage = resultPage.Items[0];
    Console.WriteLine("Last message: " + lastMessage.Id + " - " + lastMessage.Data);
});
ARTRealtime *realtime = [[ARTRealtime alloc] initWithKey:@"xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM"];
ARTRealtimeChannel *channel = [realtime.channels get:@"RANDOM_CHANNEL_NAME"];
[channel publish:@"example" data:@"message data" callback:^(ARTErrorInfo *error) {
    if (error) {
        NSLog(@"Unable to publish message; err = %@", error.message);
        return;
    }
    [channel history:^(ARTPaginatedResult<ARTMessage *> *resultPage, ARTErrorInfo *error) {
        ARTMessage *lastMessage = resultPage.items[0];
        NSLog(@"Last message: %@ - %@", lastMessage.id,lastMessage.data);
    }];
}];
let realtime = ARTRealtime(key: "xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM")
let channel = realtime.channels.get("big-dip-dew")
channel.publish("example", data: "message data") { error in
    if let error = error {
        print("Unable to publish message; err = \(error.message)")
        return
    }
    channel.history { resultPage, error in
        let lastMessage = resultPage!.items[0] as! ARTMessage
        print("Last message: \(lastMessage.id) - \(lastMessage.data)")
    }
}

Channel & Presence history

Both the Channel and Presence objects provide history. The Channel object provides the history of “Message”/api/realtime-sdk/history#message objects published on the channel, whereas the Presence object provides presence event history of that channel i.e. members entering, updating or leaving the channel as “PresenceMessage”/api/realtime-sdk/history#presence-message objects.

Enabling persistent history

By default, persisted history on channels is disabled and messages are only stored by the Ably service for two minutes in memory. If persisted history is enabled for the channel, then messages will typically be stored for 24 – 72 hours on disk.

Every message that is persisted to or retrieved from disk counts as an extra message towards your monthly quote. For example, for a channel that has persistence enabled, if a message is published, two messages will be deducted from your monthly quota. If the message is later retrieved from history, another message will be deducted from your monthly quota.

To enable history on a channel, it is necessary to add a channel rule in the settings of your application dashboard. See the documentation on channel rules for further information on what they are and how to configure them.

Continuous history

It is possible to obtain message history that is continuous with the realtime messages received on an attached channel, in the backwards direction from the point of attachment. When a Channel instance is attached, it’s automatically populated by the Ably service with the serial number of the last published message on the channel. As such, using this serial number, the client library is able to make a history request to the Ably service for all messages received since the channel was attached. Any new messages therefore are received in realtime via the attached channel, and any historical messages are accessible via the history method.

In order to benefit from this functionality, the untilAttach option can be used when making history requests on attached channels. If the channel is not yet attached, this will result in an error.

var realtime = new Ably.Realtime('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM');
var channel = realtime.channels.get('big-dip-dew');
channel.attach(function(err) {
  channel.history({ untilAttach: true}, function(err, resultPage) {
    var lastMessage = resultPage.items[0];
    alert('Last message before attach: ' + lastMessage.data);
  });
});
var realtime = new Ably.Realtime('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM');
var channel = realtime.channels.get('big-dip-dew');
channel.attach(function(err) {
  channel.history({ untilAttach: true}, function(err, resultPage) {
    var lastMessage = resultPage.items[0];
    alert('Last message before attach: ' + lastMessage.data);
  });
});
realtime = Ably::Realtime.new('xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM')
channel = realtime.channels.get('big-dip-dew')
channel.attach do
  channel.history(until_attach: true) do |result_page|
    last_message = result_page.items.last
    puts "Last message before attach: #{last_message.data}")
  end
end
AblyRealtime realtime = new AblyRealtime("xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM");
Channel channel = realtime.channels.get("big-dip-dew");
channel.attach();
channel.on(ChannelState.attached, new ChannelStateListener() {
  @Override
  public void onChannelStateChanged(ChannelState state, ErrorInfo reason) {
    Param[] options = new Param[]{ new Param("untilAttach", "true") };
    PaginatedResult<Message> resultPage = channel.history(options);
    Message lastMessage = resultPage.items[0];
    System.out.println("Last message before attach: " + lastMessage.data);
  }
});
AblyRealtime realtime = new AblyRealtime("xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM");
IRealtimeChannel channel = realtime.Channels.Get("big-dip-dew");
await channel.AttachAsync();
PaginatedResult<Message> resultPage = await channel.HistoryAsync(untilAttach: true);
Message lastMessage = resultPage.Items[0];
Console.WriteLine("Last message before attach: " + lastMessage.data);
ARTRealtime *realtime = [[ARTRealtime alloc] initWithKey:@"xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM"];
ARTRealtimeChannel *channel = [realtime.channels get:@"RANDOM_CHANNEL_NAME"];
[channel attach];
[channel on:ARTChannelEventAttached callback:^(ARTErrorInfo *error) {
    ARTRealtimeHistoryQuery *query = [[ARTRealtimeHistoryQuery alloc] init];
    query.untilAttach = YES;
    [channel history:query callback:^(ARTPaginatedResult<ARTMessage *> *resultPage, ARTErrorInfo *error) {
        ARTMessage *lastMessage = resultPage.items[0];
        NSLog(@"Last message: %@ - %@", lastMessage.id,lastMessage.data);
    } error:nil];
}];
let realtime = ARTRealtime(key: "xVLyHw.ed5skQ:e2MQm0QbZOSHyE-4pjaWcT3uFf5CyjxZOqKuX9BDhMM")
let channel = realtime.channels.get("big-dip-dew")
channel.attach()
channel.on(.attached) { error in
    let query = ARTRealtimeHistoryQuery()
    query.untilAttach = true
    try! channel.history(query) { resultPage, error in
        let lastMessage = resultPage!.items[0] as! ARTMessage
        print("Last message before attach: \(lastMessage.id) - \(lastMessage.data)")
    }
}

API Reference

View the History API Reference.


Need help?

If you need any help with your implementation or if you have encountered any problems, do get in touch. You can also quickly find answers from our knowledge base, and blog.