# Channel Pagination

The channel query endpoint allows you to paginate the list of messages, watchers, and members for one channel. To make sure that you are able to retrieve a consistent list of messages, pagination does not work with simple offset/limit parameters but instead, it relies on passing the ID of the messages from the previous page.

For example: say that you fetched the first 100 messages from a channel and want to load the next 100. To do this you need to make a channel query request and pass the ID of the oldest message if you are paginating in descending order or the ID of the newest message if paginating in ascending order.

Use the `id_lt` parameter to retrieve messages older than the provided ID and `id_gt` to retrieve messages newer than the provided ID.

The terms  `id_lt`  and `id_gt` stand for *ID less than *and* ID greater than.*

ID-based pagination improves performance and prevents issues related to the list of messages changing while you’re paginating. If needed, you can also use the inclusive versions of those two parameters: `id_lte` and `id_gte` .

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = client.channel("messaging", "general")
val pageSize = 10

// Request for the first page
val request = QueryChannelRequest()
  .withMessages(pageSize)

channelClient.query(request).enqueue { result ->
  if (result is Result.Success) {
    val messages: List<Message> = result.value.messages
    if (messages.size < pageSize) {
      // All messages loaded
    } else {
      // Load next page
      val nextRequest = QueryChannelRequest()
        .withMessages(Pagination.LESS_THAN, messages.last().id, pageSize)
      // ...
    }
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
// the id of the last message on the page
const lastMessageId = messages[0].id;

// pick the parts you need; messages, members or watchers
const result = await channel.query({
  messages: { limit: 20, id_lt: lastMessageId },
  members: { limit: 20, offset: 0 },
  watchers: { limit: 20, offset: 0 },
});

// fetch messages around a specific message
const result = await channel.query({
  messages: { limit: 20, id_around: message_id },
});

// fetch more than 100 members
const result = await channel.query({
  state: true,
  members: { limit: 110, offset: 0 },
});
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final response = await channel.query(
 messagesPagination: PaginationParams(limit: 2, lessThanOrEqual: "123"),
 membersPagination: PaginationParams(limit: 2, offset: 0),
 watchersPagination: PaginationParams(limit: 2, offset: 0),
);
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$messages = $channel->query([
		'messages' => ['limit'=>2, 'id_lte' => '123'],
		'members' => ['limit'=>2, 'offset' => 0],
		'watchers' => ['limit'=>2, 'offset' => 0]
]);
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let messageId: MessageId = "123"
let channelId = ChannelId(type: .messaging, id: "general")
let controller = chatClient.channelController(for: channelId)

// Equals to id_around
controller.loadPageAroundMessageId(messageId) { error in
  if let error = error {
    // handle error
    print(error)
  } else {
    // access messages
    print(controller.messages)

    // Equals to id_gt (where id is the newest fetched message id)
    controller.loadNextMessages(limit: 10) { error in
      // handle error / access messages
      print(error ?? controller.messages)
      // Equals to id_lt (where id is the oldest fetched message id)
      controller.loadPreviousMessages(limit: 10) { error in
        print(error ?? controller.messages)
      }
    }
  }
}

// `Chat` provides more granual access
let chat = chatClient.makeChat(for: channelId)
// Messages for id_gt
let greaterThanPagination = MessagesPagination(
  pageSize: 10,
  parameter: .greaterThan(messageId)
)
let messagesGreaterThan = try await chat.loadMessages(with: greaterThanPagination)

// Messages for id_lt
let lessThanPagination = MessagesPagination(
  pageSize: 10,
  parameter: .lessThan(messageId)
)
let messagesLessThan = try await chat.loadMessages(with: lessThanPagination)

// Message around a message id
let aroundPagination = MessagesPagination(
  pageSize: 10,
  parameter: .around(messageId)
)
let messagesAround = try await chat.loadMessages(with: aroundPagination)

// And also convenience methods
try await chat.loadMessages(before: messageId, limit: 10)
try await chat.loadMessages(around: messageId, limit: 10)
try await chat.loadMessages(after: messageId, limit: 10)
let loadedMessages = chat.state.messages
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
# the id of the last message on the page
last_message_id = messages[0]["id"]

# pick the parts you need; messages, members or watchers
result = channel.query(
  messages={"limit": 50, "id_lt": last_message_id},
  members={"limit": 10, "offset": 0},
  watchers={"limit": 20, "offset": 0},
)

# fetch messages around a specific message
result = channel.query(
  messages={"limit": 20, "id_around": message_id},
)

# fetch more than 100 members
result = channel.query(
  state=True,
  members={"limit": 110, "offset": 0},
)
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
# require 'stream-chat'

# the id of the last message on the page
last_message_id = messages[0]["id"]

# pick the parts you need; messages, members or watchers
result = channel.query({
  'messages' => {'limit' => 50, 'id_lt' => last_message_id},
  'members' => {'limit' => 50, 'offset' => 0},
  'watchers' => {'limit' => 50, 'offset' => 0},
})

# fetch messages around a specific message
result = channel.query({
  'messages' => {'limit' => 50, 'id_around' => message_id},
})

# fetch more than 100 members
result = channel.query({
  'state' => true,
  'members' => {'limit' => 110, 'offset' => 0},
})
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
FMessagePaginationOptions MessagesPagination;
MessagesPagination.Limit = 2;
MessagesPagination.IdLte = TEXT("123");
const FUserPaginationOptions MemberPagination{2, 0};
const FUserPaginationOptions WatcherPagination{2, 0};
Channel->Query(EChannelFlags::State, MessagesPagination, MemberPagination, WatcherPagination);
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
// the id of the last message on the page
var lastMessageId = messages[0].Id;

// pick the parts you need; messages, members or watchers
await channelClient.GetOrCreateAsync("messaging", "general", new ChannelGetRequest
{
  MessagesPagination = new MessagePaginationParams { Limit = 50, IDLT = lastMessageId },
  MembersPagination = new PaginationParams { Limit = 10, Offset = 0 },
  WatchersPagination = new PaginationParams { Limit = 20, Offset = 0 },
});
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
// the id of the last message on the page
lastMessageId := messages[0].Id

// pick the parts you need; messages, members or watchers
options := map[string]interface{}{
	"messages": map[string]interface{}{"limit": 50, "id_lt": lastMessageId},
	"members": map[string]interface{}{"limit": 10, "offset": 0},
	"watchers": map[string]interface{}{"limit": 20, "offset": 0},
}

ch.query(ctx, options, nil)
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// Android SDK

ChannelClient channelClient = client.channel("messaging", "general");
int pageSize = 10;

// Request for the first page
QueryChannelRequest request = new QueryChannelRequest()
    .withMessages(pageSize);

channelClient.query(request).enqueue(result -> {
  if (result.isSuccess()) {
    List<Message> messages = result.data().getMessages();
    if (messages.size() < pageSize) {
      // All messages loaded
    } else {
      // Load next page
      Message lastMessage = messages.get(messages.size() - 1);
      QueryChannelRequest nextRequest = new QueryChannelRequest()
          .withMessages(LESS_THAN, lastMessage.getId(), pageSize);
      // ...
    }
  } else {
    // Handle result.error()
  }
});

// Backend SDK
String lastMessageId = lastMessage.getId();
ChannelGetResponse resp = Channel.getOrCreate("type", "id")
  .messages(
   MessagePaginationParameters
   .builder()
   .idLt(lastMessageId)
   .build())
  .request()

// Use resp.getMessages() somehow
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// Channel is loaded with the most recent messages
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId: "my-channel-id");

// Every call will load 1 more page of messages
await channel.LoadOlderMessagesAsync();
```

</codetabs-item>

</codetabs>

For members and watchers, we use  `limit`  and  `offset`  parameters. The maximum limit and offset that can be used is `300` and `10000` , respectively.

<admonition type="warning">

To retrieve more than `100` **members** in a single query, you must include `state` as `true` in your channel query parameters.

</admonition>

<admonition type="info">

Soon we will create friendlier aliases for id_lt and id_gt. Our best candidates are before_id and after_id, let us know if you have any feedback or suggestion!

</admonition>

<admonition type="info">

The maximum number of messages that can be retrieved at once from the API is 300.

</admonition>

---

This page was last updated at 2025-08-15T14:15:19.095Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/ruby/channel_pagination/](https://getstream.io/chat/docs/ruby/channel_pagination/).