diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
index b18471d0e..e10544c6d 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
@@ -2588,6 +2588,15 @@ void InnerWidget::dragPinnedFromTouch() {
 	updateReorderPinned(now);
 }
 
+void InnerWidget::searchRequested(bool loading) {
+	_searchWaiting = false;
+	_searchLoading = loading;
+	if (loading) {
+		clearSearchResults(true);
+	}
+	refresh(true);
+}
+
 void InnerWidget::applySearchState(SearchState state) {
 	if (_searchState == state) {
 		return;
@@ -2700,9 +2709,13 @@ void InnerWidget::applySearchState(SearchState state) {
 		clearMouseSelection(true);
 	}
 	if (_state != WidgetState::Default) {
-		_searchLoading = true;
-		_searchMessages.fire({});
-		refresh(true);
+		_searchWaiting = true;
+		_searchRequests.fire(otherChanged
+			? SearchRequestDelay::Instant
+			: SearchRequestDelay::Delayed);
+		if (_searchWaiting) {
+			refresh(true);
+		}
 	}
 }
 
@@ -2918,8 +2931,8 @@ rpl::producer<Ui::ScrollToRequest> InnerWidget::dialogMoved() const {
 	return _dialogMoved.events();
 }
 
-rpl::producer<> InnerWidget::searchMessages() const {
-	return _searchMessages.events();
+rpl::producer<SearchRequestDelay> InnerWidget::searchRequests() const {
+	return _searchRequests.events();
 }
 
 rpl::producer<QString> InnerWidget::completeHashtagRequests() const {
@@ -3007,6 +3020,7 @@ void InnerWidget::searchReceived(
 		HistoryItem *inject,
 		SearchRequestType type,
 		int fullCount) {
+	_searchWaiting = false;
 	_searchLoading = false;
 
 	const auto uniquePeers = uniqueSearchResults();
@@ -3171,7 +3185,7 @@ void InnerWidget::refreshEmpty() {
 			&& _searchResults.empty()
 			&& _peerSearchResults.empty()
 			&& _hashtagResults.empty();
-		if (_searchLoading || !empty) {
+		if (_searchLoading || _searchWaiting || !empty) {
 			if (_searchEmpty) {
 				_searchEmpty->hide();
 			}
@@ -3185,7 +3199,7 @@ void InnerWidget::refreshEmpty() {
 			_searchEmpty->show();
 		}
 
-		if (!_searchLoading || !empty) {
+		if ((!_searchLoading && !_searchWaiting) || !empty) {
 			_loadingAnimation.destroy();
 		} else if (!_loadingAnimation) {
 			_loadingAnimation = Ui::CreateLoadingDialogRowWidget(
diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
index ebb22aa82..3adad0cf4 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
@@ -71,7 +71,7 @@ struct ChosenRow {
 	bool newWindow : 1 = false;
 };
 
-enum class SearchRequestType {
+enum class SearchRequestType : uchar {
 	FromStart,
 	FromOffset,
 	PeerFromStart,
@@ -80,6 +80,12 @@ enum class SearchRequestType {
 	MigratedFromOffset,
 };
 
+enum class SearchRequestDelay : uchar {
+	InCache,
+	Instant,
+	Delayed,
+};
+
 enum class WidgetState {
 	Default,
 	Filtered,
@@ -145,6 +151,7 @@ public:
 	}
 	[[nodiscard]] bool hasFilteredResults() const;
 
+	void searchRequested(bool loading);
 	void applySearchState(SearchState state);
 	[[nodiscard]] auto searchTagsChanges() const
 		-> rpl::producer<std::vector<Data::ReactionId>>;
@@ -168,7 +175,7 @@ public:
 	[[nodiscard]] rpl::producer<int> scrollByDeltaRequests() const;
 	[[nodiscard]] rpl::producer<Ui::ScrollToRequest> mustScrollTo() const;
 	[[nodiscard]] rpl::producer<Ui::ScrollToRequest> dialogMoved() const;
-	[[nodiscard]] rpl::producer<> searchMessages() const;
+	[[nodiscard]] rpl::producer<SearchRequestDelay> searchRequests() const;
 	[[nodiscard]] rpl::producer<QString> completeHashtagRequests() const;
 	[[nodiscard]] rpl::producer<> refreshHashtagsRequests() const;
 
@@ -529,7 +536,7 @@ private:
 
 	rpl::event_stream<Ui::ScrollToRequest> _mustScrollTo;
 	rpl::event_stream<Ui::ScrollToRequest> _dialogMoved;
-	rpl::event_stream<> _searchMessages;
+	rpl::event_stream<SearchRequestDelay> _searchRequests;
 	rpl::event_stream<QString> _completeHashtagRequests;
 	rpl::event_stream<> _refreshHashtagsRequests;
 
@@ -547,6 +554,7 @@ private:
 
 	bool _savedSublists = false;
 	bool _searchLoading = false;
+	bool _searchWaiting = false;
 
 	base::unique_qptr<Ui::PopupMenu> _menu;
 
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 068c30815..61689d105 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -95,6 +95,7 @@ namespace {
 
 constexpr auto kSearchPerPage = 50;
 constexpr auto kStoriesExpandDuration = crl::time(200);
+constexpr auto kSearchRequestDelay = crl::time(900);
 
 base::options::toggle OptionForumHideChatsList({
 	.id = kOptionForumHideChatsList,
@@ -324,9 +325,9 @@ Widget::Widget(
 			_scroll->scrollToY(st + _inner->st()->height);
 		}
 	}, lifetime());
-	_inner->searchMessages(
-	) | rpl::start_with_next([=] {
-		searchRequested();
+	_inner->searchRequests(
+	) | rpl::start_with_next([=](SearchRequestDelay delay) {
+		searchRequested(delay);
 	}, lifetime());
 	_inner->completeHashtagRequests(
 	) | rpl::start_with_next([=](const QString &tag) {
@@ -1921,7 +1922,7 @@ void Widget::loadMoreBlockedByDate() {
 	session().api().requestMoreBlockedByDateDialogs();
 }
 
-bool Widget::search(bool inCache) {
+bool Widget::search(bool inCache, SearchRequestDelay delay) {
 	_processingSearch = true;
 	const auto guard = gsl::finally([&] {
 		_processingSearch = false;
@@ -1950,7 +1951,7 @@ bool Widget::search(bool inCache) {
 		return true;
 	} else if (inCache) {
 		const auto success = _singleMessageSearch.lookup(query, [=] {
-			searchRequested();
+			searchRequested(delay);
 		});
 		if (!success) {
 			return false;
@@ -2068,6 +2069,9 @@ bool Widget::search(bool inCache) {
 			}).send();
 			_searchQueries.emplace(_searchRequest, _searchQuery);
 		}
+		_inner->searchRequested(true);
+	} else {
+		_inner->searchRequested(false);
 	}
 	const auto peerQuery = Api::ConvertPeerSearchQuery(query);
 	if (searchForPeersRequired(peerQuery)) {
@@ -2130,9 +2134,14 @@ bool Widget::searchForTopicsRequired(const QString &query) const {
 		&& !_openedForum->topicsList()->loaded();
 }
 
-void Widget::searchRequested() {
-	if (!search(true)) {
-		_searchTimer.callOnce(AutoSearchTimeout);
+void Widget::searchRequested(SearchRequestDelay delay) {
+	if (search(true, delay)) {
+		return;
+	} else if (delay == SearchRequestDelay::Instant) {
+		_searchTimer.cancel();
+		search();
+	} else {
+		_searchTimer.callOnce(kSearchRequestDelay);
 	}
 }
 
@@ -2187,10 +2196,11 @@ void Widget::searchTopics() {
 }
 
 void Widget::searchMore() {
-	if (_searchRequest || _searchInHistoryRequest) {
+	if (_searchRequest
+		|| _searchInHistoryRequest
+		|| _searchTimer.isActive()) {
 		return;
-	}
-	if (!_searchFull) {
+	} else if (!_searchFull) {
 		if (const auto peer = searchInPeer()) {
 			auto &histories = session().data().histories();
 			const auto topic = searchInTopic();
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h
index 581bfdc00..46e5b55e0 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h
@@ -74,7 +74,8 @@ class FakeRow;
 class Key;
 struct ChosenRow;
 class InnerWidget;
-enum class SearchRequestType;
+enum class SearchRequestType : uchar;
+enum class SearchRequestDelay : uchar;
 class Suggestions;
 class ChatSearchIn;
 enum class ChatSearchTab : uchar;
@@ -156,8 +157,8 @@ private:
 	[[nodiscard]] QString currentSearchQuery() const;
 	[[nodiscard]] int currentSearchQueryCursorPosition() const;
 	void clearSearchField();
-	void searchRequested();
-	bool search(bool inCache = false);
+	void searchRequested(SearchRequestDelay delay);
+	bool search(bool inCache = false, SearchRequestDelay after = {});
 	void searchTopics();
 	void searchMore();