diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp
index 22402d8823d21c38bb2ba636915fd507a11b32cd..57e49b420bbaddecabc70d312a6b941525c733ab 100644
--- a/include/mtxclient/http/client.hpp
+++ b/include/mtxclient/http/client.hpp
@@ -63,6 +63,19 @@ struct SyncOpts
         bool full_state = false;
 };
 
+//! Configuration for the /messages endpoint.
+struct MessagesOpts
+{
+        std::string room_id;
+        std::string from;
+        std::string to;
+        std::string filter;
+
+        PaginationDirection dir = PaginationDirection::Backwards;
+
+        uint16_t limit = 30;
+};
+
 //! The main object that the user will interact.
 class Client : public std::enable_shared_from_this<Client>
 {
@@ -145,13 +158,7 @@ public:
         void sync(const SyncOpts &opts, Callback<mtx::responses::Sync> cb);
 
         //! Paginate through room messages.
-        void messages(const std::string &room_id,
-                      const std::string &from,
-                      const std::string &to,
-                      PaginationDirection dir,
-                      uint16_t limit,
-                      const std::string &filter,
-                      Callback<mtx::responses::Messages> cb);
+        void messages(const MessagesOpts &opts, Callback<mtx::responses::Messages> cb);
 
         //! Get the supported versions from the server.
         void versions(Callback<mtx::responses::Versions> cb);
diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index 922598899b19082cc2ad399aec12aad2ed9f3b2f..e218ef0e9b62fbfa4761fb4e59761877c014b388 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -288,28 +288,23 @@ Client::stop_typing(const std::string &room_id, ErrCallback callback)
 }
 
 void
-Client::messages(const std::string &room_id,
-                 const std::string &from,
-                 const std::string &to,
-                 PaginationDirection dir,
-                 uint16_t limit,
-                 const std::string &filter,
-                 Callback<mtx::responses::Messages> callback)
+Client::messages(const MessagesOpts &opts, Callback<mtx::responses::Messages> callback)
 {
         std::map<std::string, std::string> params;
 
-        params.emplace("from", from);
-        params.emplace("dir", to_string(dir));
+        params.emplace("dir", to_string(opts.dir));
 
-        if (!to.empty())
-                params.emplace("to", to);
-        if (limit > 0)
-                params.emplace("limit", std::to_string(limit));
-        if (!filter.empty())
-                params.emplace("filter", filter);
+        if (!opts.from.empty())
+                params.emplace("from", opts.from);
+        if (!opts.to.empty())
+                params.emplace("to", opts.to);
+        if (opts.limit > 0)
+                params.emplace("limit", std::to_string(opts.limit));
+        if (!opts.filter.empty())
+                params.emplace("filter", opts.filter);
 
         const auto api_path =
-          "/client/r0/rooms/" + room_id + "/messages?" + client::utils::query_params(params);
+          "/client/r0/rooms/" + opts.room_id + "/messages?" + client::utils::query_params(params);
 
         get<mtx::responses::Messages>(
           api_path, [callback](const mtx::responses::Messages &res, HeaderFields, RequestErr err) {
diff --git a/tests/client_api.cpp b/tests/client_api.cpp
index 9955c855090ae32c7abe5a264c6182ce51f5e385..907aadfa6d029a60b62fbe93f2c3fad8b0ab2290 100644
--- a/tests/client_api.cpp
+++ b/tests/client_api.cpp
@@ -851,26 +851,20 @@ TEST(ClientAPI, Pagination)
                 check_error(err);
                 auto room_id = res.room_id;
 
+                MessagesOpts opts;
+                opts.room_id = res.room_id.to_string();
+
                 alice->messages(
-                  res.room_id.to_string(),
-                  "", // from
-                  "", // to
-                  PaginationDirection::Backwards,
-                  30, // limit. Just enough messages so can fetch the whole history on
-                      // this newly created room.
-                  "", // filter
-                  [room_id, alice](const mtx::responses::Messages &res, RequestErr err) {
+                  opts, [room_id, alice](const mtx::responses::Messages &res, RequestErr err) {
                           check_error(err);
 
                           ASSERT_TRUE(res.chunk.size() > 5);
                           ASSERT_NE(res.start, res.end);
 
-                          alice->messages(room_id.to_string(),
-                                          res.end,
-                                          "",
-                                          PaginationDirection::Backwards,
-                                          30,
-                                          "",
+                          MessagesOpts opts;
+                          opts.from    = res.end;
+                          opts.room_id = room_id.to_string();
+                          alice->messages(opts,
                                           [](const mtx::responses::Messages &res, RequestErr err) {
                                                   check_error(err);