diff --git a/classes/rpc.php b/classes/rpc.php
index 7caf37cf073a9fedf51d2fdb612fee141ff83aa9..b9fd492fac0d320ca3b19197ef52d700e7d08bd3 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -73,10 +73,7 @@ class RPC extends Handler_Protected {
 	}
 
 	function getAllCounters() {
-		$feed_ids = array_map("intval",
-			explode(",",
-				clean($_REQUEST["feed_ids"])));
-
+		$feed_ids = array_map("intval", clean($_REQUEST["feed_ids"] ?? []));
 		@$seq = (int) $_REQUEST['seq'];
 
 		// @phpstan-ignore-next-line
@@ -95,10 +92,7 @@ class RPC extends Handler_Protected {
 
 	/* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */
 	function catchupSelected() {
-		$ids = array_map("intval",
-			explode(",",
-				clean($_REQUEST["ids"])));
-
+		$ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
 		$cmode = (int)clean($_REQUEST["cmode"]);
 
 		Article::_catchup_by_id($ids, $cmode);
@@ -107,10 +101,7 @@ class RPC extends Handler_Protected {
 	}
 
 	function markSelected() {
-		$ids = array_map("intval",
-			explode(",",
-				clean($_REQUEST["ids"])));
-
+		$ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
 		$cmode = (int)clean($_REQUEST["cmode"]);
 
 		$this->markArticlesById($ids, $cmode);
@@ -119,10 +110,7 @@ class RPC extends Handler_Protected {
 	}
 
 	function publishSelected() {
-		$ids = array_map("intval",
-			explode(",",
-				clean($_REQUEST["ids"])));
-
+		$ids = array_map("intval", clean($_REQUEST["ids"] ?? []));
 		$cmode = (int)clean($_REQUEST["cmode"]);
 
 		$this->publishArticlesById($ids, $cmode);
diff --git a/js/Feeds.js b/js/Feeds.js
index d4bdd01b684cc3532d2b2d65b60044210f0507d0..5d64b77ee157749187d3d955756d75aa4df80400 100644
--- a/js/Feeds.js
+++ b/js/Feeds.js
@@ -134,7 +134,7 @@ const	Feeds = {
 		this.reloadCurrent();
 	},
 	requestCounters: function(feed_ids = null) {
-		xhr.json("backend.php", {op: "rpc", method: "getAllCounters", feed_ids: feed_ids, seq: App.next_seq()}, () => {
+		xhr.json("backend.php", {op: "rpc", method: "getAllCounters", "feed_ids[]": feed_ids, seq: App.next_seq()}, () => {
 			//
 		});
 	},
diff --git a/js/Headlines.js b/js/Headlines.js
index 18c7cda2e069a1c16b2ae6263455730d0c56c07a..8baf936711b9e577c554ced21e7b6032c5dfb36a 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -150,19 +150,19 @@ const Headlines = {
 
 		if (ops.tmark.length != 0)
 			promises.push(xhr.post("backend.php",
-				{op: "rpc", method: "markSelected", ids: ops.tmark.toString(), cmode: 2}));
+				{op: "rpc", method: "markSelected", "ids[]": ops.tmark, cmode: 2}));
 
 		if (ops.tpub.length != 0)
 			promises.push(xhr.post("backend.php",
-				{op: "rpc", method: "publishSelected", ids: ops.tpub.toString(), cmode: 2}));
+				{op: "rpc", method: "publishSelected", "ids[]": ops.tpub, cmode: 2}));
 
 		if (ops.read.length != 0)
 			promises.push(xhr.post("backend.php",
-				{op: "rpc", method: "catchupSelected", ids: ops.read.toString(), cmode: 0}));
+				{op: "rpc", method: "catchupSelected", "ids[]": ops.read, cmode: 0}));
 
 		if (ops.unread.length != 0)
 			promises.push(xhr.post("backend.php",
-				{op: "rpc", method: "catchupSelected", ids: ops.unread.toString(), cmode: 1}));
+				{op: "rpc", method: "catchupSelected", "ids[]": ops.unread, cmode: 1}));
 
 		const scores = Object.keys(ops.rescore);