diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php
index 5e355611c11d34d25e20a8b50fefc25450fad3ba..3eea0f1d5c8949843fd0912a85777c7e5df5fa62 100644
--- a/apps/dav/lib/CardDAV/AddressBookImpl.php
+++ b/apps/dav/lib/CardDAV/AddressBookImpl.php
@@ -105,6 +105,8 @@ class AddressBookImpl implements IAddressBook {
 	 * 	- 'types' boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
 	 *    example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']]
 	 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
+	 * 	- 'limit' - Set a numeric limit for the search results
+	 * 	- 'offset' - Set the offset for the limited search results
 	 * @return array an array of contacts which are arrays of key-value-pairs
 	 *  example result:
 	 *  [
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php
index f4de1b717fb139a747279995a01fc45638294e3e..87e8e11cd167daeaab9a81af0677219a71d50c08 100644
--- a/apps/dav/lib/CardDAV/CardDavBackend.php
+++ b/apps/dav/lib/CardDAV/CardDavBackend.php
@@ -965,6 +965,12 @@ class CardDavBackend implements BackendInterface, SyncSupport {
 				$query2->andWhere($query2->expr()->ilike('cp.value', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
 			}
 		}
+		if (isset($options['limit'])) {
+			$query2->setMaxResults($options['limit']);
+		}
+		if (isset($options['offset'])) {
+			$query2->setFirstResult($options['offset']);
+		}
 
 		$query->select('c.carddata', 'c.uri')->from($this->dbCardsTable, 'c')
 			->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL())));
diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php
index e387e38c6dc9a5d2711a6480c548995e80ba25ee..8266ca6794f1d66dff386385df1fbfd491b63679 100644
--- a/lib/private/Collaboration/Collaborators/MailPlugin.php
+++ b/lib/private/Collaboration/Collaborators/MailPlugin.php
@@ -81,8 +81,7 @@ class MailPlugin implements ISearchPlugin {
 		$emailType = new SearchResultType('emails');
 
 		// Search in contacts
-		//@todo Pagination missing
-		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
+		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN'], ['limit' => $limit, 'offset' => $offset]);
 		$lowerSearch = strtolower($search);
 		foreach ($addressBookContacts as $contact) {
 			if (isset($contact['EMAIL'])) {
diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php
index 6309b8e6eab4d705c836bade8454f6b736cf94fa..e4340f4c19b6741288aaa067492fcabb6138acf0 100644
--- a/lib/private/Collaboration/Collaborators/RemotePlugin.php
+++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php
@@ -67,8 +67,7 @@ class RemotePlugin implements ISearchPlugin {
 		$resultType = new SearchResultType('remotes');
 
 		// Search in contacts
-		//@todo Pagination missing
-		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
+		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], ['limit' => $limit, 'offset' => $offset]);
 		foreach ($addressBookContacts as $contact) {
 			if (isset($contact['isLocalSystemBook'])) {
 				continue;
diff --git a/lib/private/ContactsManager.php b/lib/private/ContactsManager.php
index c28e7f6ad4bdee13f0d15c2488739173bb03bcca..fca0a6e5ba3d9f2c172e922a19c7376e03b60ea6 100644
--- a/lib/private/ContactsManager.php
+++ b/lib/private/ContactsManager.php
@@ -40,6 +40,8 @@ namespace OC {
 		 * @param array $searchProperties defines the properties within the query pattern should match
 		 * @param array $options = array() to define the search behavior
 		 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
+		 * 	- 'limit' - Set a numeric limit for the search results
+		 * 	- 'offset' - Set the offset for the limited search results
 		 * @return array an array of contacts which are arrays of key-value-pairs
 		 */
 		public function search($pattern, $searchProperties = [], $options = []) {
diff --git a/lib/public/Contacts/IManager.php b/lib/public/Contacts/IManager.php
index dcb4c7ddbb33a77d25784af84fe576c1458316d1..e0551bcd9dd93c2b9d900519fb18d6f7fd023a45 100644
--- a/lib/public/Contacts/IManager.php
+++ b/lib/public/Contacts/IManager.php
@@ -96,6 +96,8 @@ interface IManager {
 	 * @param array $searchProperties defines the properties within the query pattern should match
 	 * @param array $options = array() to define the search behavior
 	 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
+	 * 	- 'limit' - Set a numeric limit for the search results
+	 * 	- 'offset' - Set the offset for the limited search results
 	 * @return array an array of contacts which are arrays of key-value-pairs
 	 * @since 6.0.0
 	 */
diff --git a/lib/public/IAddressBook.php b/lib/public/IAddressBook.php
index 0bce0f44dc044c7a44204c5c5987217abe3880b5..b2c9a103c2cf55808fe2af6b97666b52a4a4cc16 100644
--- a/lib/public/IAddressBook.php
+++ b/lib/public/IAddressBook.php
@@ -71,6 +71,8 @@ namespace OCP {
 		 * 	- 'types' boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
 		 *    example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']]
 		 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
+		 * 	- 'limit' - Set a numeric limit for the search results
+		 * 	- 'offset' - Set the offset for the limited search results
 		 * @return array an array of contacts which are arrays of key-value-pairs
 		 *  example result:
 		 *  [