Skip to content


In some scenarios the Discord API allows to paginate endpoints to retrieve entities in bulks.
Such an endpoint is especially often used for retrieving past messages in the client by scrolling up.

In JDA we allow iterating such endpoints with the Iterable PaginationAction implementations. Using it inside an enhanced for-loop is a blocking and expensive operation so it is recommended to use the async methods instead.

Example Messages

* Retrieves up to 1000 messages from the provided MessageChannel
* and then provides them to the callback.
public void get1000(MessageChannel channel, Consumer<List<Message>> callback)
    List<Message> messages = new ArrayList<>(1000);
    channel.getIterableHistory().cache(false).forEachAsync((message) ->
        return messages.size() < 1000;
    }).thenRun(() -> callback.accept(messages));

get1000(channel, (messages) -> channel.purgeMessages(messages));

Entity Cache


PaginationActions support the Stream api which was introduced in Java 1.8. Stream requires blocking operations and can take long to finish.

Every PaginationAction has a cache of already retrieved entities.
This cache is enabled by default but can be disabled via PaginationAction.cache(false). Doing so is recommended if the cache is not needed.

To retrieve all cached entities use PaginationAction.getCached(). This returns an immutable List representing all cached entities (thread-safe but expanding).
You may also get the first/last entity with getLast() and getFirst() for convenience.

By default the PaginationAction will retrieve the maximum amount of entities per request (complete/queue).
It is advised to lower this limit if only a small number of entities is required. (See limit(int)).

Usage examples

public class PaginationUtil
    // Calls the callback if the user has reacted
    public static void ifReacted(MessageReaction reaction, User user, Runnable callback)
        reaction.getUsers().cache(false).forEachAsync(u -> {
            if (u.equals(user)) {
      ; // user has reacted -> call the callback
                return false; // end iteration
            return true; // continue iteration

    // Blocking iteration using the Iterable interface (not recommended)
    public static List<Message> forEachMessage(int limit, MessageChannel channel, Consumer<Message> action)
        MessagePaginationAction paginator = channel.getIterableHistory();
        for (Message message : paginator)
            if (--limit <= 0) break;
        return paginator.getCached();