Index: src/bp-notifications/bp-notifications-functions.php
===================================================================
--- src/bp-notifications/bp-notifications-functions.php
+++ src/bp-notifications/bp-notifications-functions.php
@@ -462,6 +462,74 @@
 add_action( 'wpmu_delete_user', 'bp_notifications_delete_notifications_on_user_delete' );
 add_action( 'delete_user', 'bp_notifications_delete_notifications_on_user_delete' );
 
+/**
+ * Removes stale notifications from the database.
+ *
+ * @since 3.0.0
+ */
+function bp_notifications_delete_stale() {
+	// Helper function to return stale notification IDs.
+	$get_stale_ids = function( $date, $per_page ) {
+		// @todo ::get() should support 'fields' parameter to limit DB columns return.
+		$q = BP_Notifications_Notification::get( array(
+			'is_new'            => 0,
+			'component_name'    => false,
+			'update_meta_cache' => false,
+			'order_by'          => 'ID',
+			'sort_order'        => 'ASC',
+			'date_query'        => array( array(
+				'before' => $date
+			) ),
+			'per_page'          => $per_page,
+			'page'              => 1
+		) );
+
+		if ( ! empty( $q ) ) {
+			$q = wp_list_pluck( $q, 'id' );
+			return $q;
+		} else {
+			return array();
+		}
+	};
+
+	/**
+	 * Date limit to delete stale notifications for.
+	 *
+	 * @since 3.0.0
+	 *
+	 * @param string $date_limit Default: 30 days ago.
+	 */
+	$date_limit = apply_filters( 'bp_notifications_delete_date_limit', '30 days ago' );
+
+	/**
+	 * Number of stale notifications to delete at a time.
+	 *
+	 * @since 3.0.0
+	 *
+	 * @param int $limit 1000 by default.
+	 */
+	$limit = (int) apply_filters( 'bp_notifications_delete_stale_limit', 1000 );
+
+	// Purge time!
+	$ids = $get_stale_ids( $date_limit, $limit );
+	if ( ! empty( $ids ) ) {
+		// Remove notification user cache purge as this only affects unread items.
+		remove_action( 'bp_notification_before_delete', 'bp_notifications_clear_all_for_user_cache_before_delete' );
+
+		BP_Notifications_Notification::delete_by_ids( $ids );
+
+		add_action( 'bp_notification_before_delete', 'bp_notifications_clear_all_for_user_cache_before_delete' );
+
+		// Recurse if necessary.
+		$ids = $get_stale_ids( $date_limit, $limit );
+		if ( ! empty( $ids ) ) {
+			// Sleep for half a second before doing this again.
+			usleep( 500000 );
+			bp_notifications_delete_stale();
+		}
+	}
+}
+
 /** Mark **********************************************************************/
 
 /**
@@ -790,3 +858,38 @@
 
 	return $retval;
 }
+
+/** Cron **********************************************************************/
+
+/**
+ * Schedule cronjobs for notifications component.
+ *
+ * @since 3.0.0
+ */
+function bp_notifications_cron() {
+	if ( bp_is_root_blog() && ! wp_next_scheduled( 'bp_notifications_delete_stale' ) ) {
+		wp_schedule_event( time(), 'weekly', 'bp_notifications_delete_stale' );
+	}
+}
+add_action( 'bp_init', 'bp_notifications_cron' );
+
+/**
+ * Remove notification cronjobs on deactivation.
+ *
+ * @since 3.0.0
+ */
+function bp_notifications_deactivation_cron() {
+	$switched = false;
+	if ( ! bp_is_root_blog() ) {
+		$switched = true;
+		switch_to_blog( bp_get_root_blog_id() );
+	}
+
+	$timestamp = wp_next_scheduled( 'bp_notifications_delete_stale' );
+	wp_unschedule_event( $timestamp, 'bp_notifications_delete_stale' );
+
+	if ( $switched ) {
+		restore_current_blog();
+	}
+}
+add_action( 'bp_deactivation', 'bp_notifications_deactivation' );
Index: src/bp-notifications/classes/class-bp-notifications-notification.php
===================================================================
--- src/bp-notifications/classes/class-bp-notifications-notification.php
+++ src/bp-notifications/classes/class-bp-notifications-notification.php
@@ -816,7 +816,7 @@
 		}
 
 		// Date query.
-		$date_query = new BP_Date_Query( $date_query, 'date_recorded' );
+		$date_query = new BP_Date_Query( $date_query, 'date_notified' );
 
 		// Strip the leading AND - it's handled in get().
 		return preg_replace( '/^\sAND/', '', $date_query->get_sql() );
@@ -890,6 +890,31 @@
 		return self::_delete( $where['data'], $where['format'] );
 	}
 
+	/**
+	 * Delete multiple notifications by IDs.
+	 *
+	 * Unlike the delete() method, this method can handle bulk deletions.
+	 *
+	 * @since 3.0.0
+	 *
+	 * @param  array $ids Array of notification IDs.
+	 * @return int|false Number of rows affected/selected or false on error
+	 */
+	public static function delete_by_ids( $ids = array() ) {
+		$bp = buddypress();
+
+		$ids = array_map( 'absint', $ids );
+		foreach ( $ids as $id ) {
+			/** This hook is documented in /bp-notifications/classes/class-bp-notifications-notification.php */
+			do_action( 'bp_notification_before_delete', array( 'id' => $id ) );
+		}
+
+		$ids = implode( ',', $ids );
+
+		// Delete in bulk.
+		return $GLOBALS['wpdb']->query( "DELETE FROM {$bp->notifications->table_name} WHERE ID IN ($ids)" );
+	}
+
 	/** Convenience methods ***************************************************/
 
 	/**
Index: tests/phpunit/testcases/notifications/functions.php
===================================================================
--- tests/phpunit/testcases/notifications/functions.php
+++ tests/phpunit/testcases/notifications/functions.php
@@ -393,6 +393,59 @@
 		unset( $this->n_args );
 	}
 
+	/**
+	 * @ticket BP7534
+	 */
+	public function test_bp_notifications_delete_stale() {
+		$u   = self::factory()->user->create();
+		$now = time();
+
+		$n1 = self::factory()->notification->create( array(
+			'component_name'    => 'messages',
+			'component_action'  => 'new_message',
+			'item_id'           => 99,
+			'user_id'           => $u,
+			'is_new'            => 0
+		) );
+
+		$n2 = self::factory()->notification->create( array(
+			'component_name'    => 'activity',
+			'component_action'  => 'new_at_mention',
+			'item_id'           => 99,
+			'user_id'           => $u,
+			'is_new'            => 0,
+			'date_notified'     => date( 'Y-m-d H:i:s', $now - DAY_IN_SECONDS * 35 )
+		) );
+
+		$n3 = self::factory()->notification->create( array(
+			'component_name' => 'groups',
+			'user_id'        => $u,
+			'is_new'         => 0,
+			'date_notified'  => date( 'Y-m-d H:i:s', $now - DAY_IN_SECONDS * 40 )
+		) );
+
+		$n4 = self::factory()->notification->create( array(
+			'component_name'   => 'friends',
+			'component_action' => 'friendship_request',
+			'user_id'          => $u,
+			'is_new'           => 0
+		) );
+
+		// Remove stale notifications.
+		bp_notifications_delete_stale();
+
+		// Assert notifications are deleted.
+		$this->assertNull( bp_notifications_get_notification( $n2 )->component_name );
+		$this->assertNull( bp_notifications_get_notification( $n3 )->component_name );
+
+		// Check user's read notifications and ensure stale notifications are removed.
+		$n = BP_Notifications_Notification::get( array(
+			'user_id' => $u,
+			'is_new'  => 0
+		) );
+		$this->assertEqualSets( array( $n1, $n4 ), wp_list_pluck( $n, 'id' ) );
+	}
+
 	/**
 	 * Used in test_notification_callback_parameter_integrity() test.
 	 */
