diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 36300dbe099e777c6b1f24e239992f5d950a5a4f..0aedd952b97323853026113bd332d3f6291321fb 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -16833,6 +16833,7 @@ Extra metadata for AI message.
 | <a id="aimetricscodesuggestionscontributorscount"></a>`codeSuggestionsContributorsCount` | [`Int`](#int) | Number of code contributors who used GitLab Duo Code Suggestions features. |
 | <a id="aimetricscodesuggestionsshowncount"></a>`codeSuggestionsShownCount` | [`Int`](#int) | Total count of code suggestions shown to code contributors. |
 | <a id="aimetricsduochatcontributorscount"></a>`duoChatContributorsCount` | [`Int`](#int) | Number of contributors who used GitLab Duo Chat features. |
+| <a id="aimetricsduoproassigneduserscount"></a>`duoProAssignedUsersCount` | [`Int`](#int) | Number of assigned Duo Pro seats. Ignores time period filter and always returns current data. |
 
 ### `AiSelfHostedModel`
 
diff --git a/ee/app/finders/gitlab_subscriptions/add_on_assigned_users_finder.rb b/ee/app/finders/gitlab_subscriptions/add_on_assigned_users_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a3e6e532779bc0040792ef5e25bdccda1d81ee7f
--- /dev/null
+++ b/ee/app/finders/gitlab_subscriptions/add_on_assigned_users_finder.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module GitlabSubscriptions
+  class AddOnAssignedUsersFinder
+    include Gitlab::Utils::StrongMemoize
+
+    def initialize(current_user, namespace, add_on_name:)
+      @current_user = current_user
+      @namespace = namespace
+      @add_on_name = add_on_name
+    end
+
+    def execute
+      add_on_purchase = GitlabSubscriptions::AddOnPurchase
+        .by_add_on_name(add_on_name).by_namespace(namespace.root_ancestor).active.first
+
+      return User.none unless add_on_purchase
+
+      add_on_purchase.users.by_ids(namespace_members.reselect(:user_id))
+    end
+
+    private
+
+    attr_reader :namespace, :current_user, :add_on_name
+
+    def namespace_members
+      # rubocop:disable CodeReuse/Finder -- member finders logic is way too complex to reconstruct it with scopes.
+      if namespace.is_a?(Namespaces::ProjectNamespace)
+        MembersFinder.new(namespace.project, current_user).execute(include_relations: %i[direct inherited descendants])
+      else
+        GroupMembersFinder.new(namespace, current_user).execute(include_relations: %i[direct inherited descendants])
+      end
+      # rubocop:enable CodeReuse/Finder
+    end
+  end
+end
diff --git a/ee/app/graphql/resolvers/analytics/ai_metrics_resolver.rb b/ee/app/graphql/resolvers/analytics/ai_metrics_resolver.rb
index ac48d7f9e8a51717ae3b6c8cfdc6438bbac9b042..3be155ddf0d447b716bfbdaa2420a8fe64ee9e92 100644
--- a/ee/app/graphql/resolvers/analytics/ai_metrics_resolver.rb
+++ b/ee/app/graphql/resolvers/analytics/ai_metrics_resolver.rb
@@ -28,27 +28,17 @@ def ready?(**args)
       def resolve_with_lookahead(**args)
         params = params_with_defaults(args)
 
-        code_suggestion_usage = ::Analytics::AiAnalytics::CodeSuggestionUsageService.new(
+        usage = ::Analytics::AiAnalytics::AiMetricsService.new(
           current_user,
           namespace: namespace,
           from: params[:start_date],
           to: params[:end_date],
-          fields: selected_code_suggestion_fields
+          fields: selected_fields
         ).execute
 
-        return unless code_suggestion_usage.success?
+        return unless usage.success?
 
-        duo_chat_usage = ::Analytics::AiAnalytics::DuoChatUsageService.new(
-          current_user,
-          namespace: namespace,
-          from: params[:start_date],
-          to: params[:end_date],
-          fields: selected_duo_chat_fields
-        ).execute
-
-        return unless duo_chat_usage.success?
-
-        code_suggestion_usage.payload.merge(duo_chat_usage.payload)
+        usage.payload
       end
 
       private
@@ -69,16 +59,8 @@ def namespace
         object.respond_to?(:project_namespace) ? object.project_namespace : object
       end
 
-      def selected_code_suggestion_fields
-        ::Analytics::AiAnalytics::CodeSuggestionUsageService::FIELDS.select do |field|
-          lookahead.selects?(field)
-        end
-      end
-
-      def selected_duo_chat_fields
-        ::Analytics::AiAnalytics::DuoChatUsageService::FIELDS.select do |field|
-          lookahead.selects?(field)
-        end
+      def selected_fields
+        lookahead.selections.map(&:name)
       end
     end
   end
diff --git a/ee/app/graphql/types/analytics/ai_metrics.rb b/ee/app/graphql/types/analytics/ai_metrics.rb
index 6bd99a57274fbfe9390f230b29c94e3d023c50fd..3dd07fe59b6ac1d43b5d435cd76331f33a277449 100644
--- a/ee/app/graphql/types/analytics/ai_metrics.rb
+++ b/ee/app/graphql/types/analytics/ai_metrics.rb
@@ -19,6 +19,9 @@ class AiMetrics < BaseObject
       field :duo_chat_contributors_count, GraphQL::Types::Int,
         description: 'Number of contributors who used GitLab Duo Chat features.',
         null: true
+      field :duo_pro_assigned_users_count, GraphQL::Types::Int,
+        description: 'Number of assigned Duo Pro seats. Ignores time period filter and always returns current data.',
+        null: true
     end
     # rubocop: enable Graphql/AuthorizeTypes
   end
diff --git a/ee/app/models/gitlab_subscriptions/add_on_purchase.rb b/ee/app/models/gitlab_subscriptions/add_on_purchase.rb
index 7075970fece186d8f95026a2838b514f517c5377..ec06640a2682ccbd6b20efd2ae3c5c6d845e6bd1 100644
--- a/ee/app/models/gitlab_subscriptions/add_on_purchase.rb
+++ b/ee/app/models/gitlab_subscriptions/add_on_purchase.rb
@@ -13,6 +13,7 @@ class AddOnPurchase < ApplicationRecord
     belongs_to :namespace, optional: true
     belongs_to :organization, class_name: 'Organizations::Organization'
     has_many :assigned_users, class_name: 'GitlabSubscriptions::UserAddOnAssignment', inverse_of: :add_on_purchase
+    has_many :users, through: :assigned_users
 
     validates :add_on, :expires_on, presence: true
     validate :valid_namespace, if: :gitlab_com?
diff --git a/ee/app/services/analytics/ai_analytics/ai_metrics_service.rb b/ee/app/services/analytics/ai_analytics/ai_metrics_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e08932914ce29e689bde3596d1e453055c849b3a
--- /dev/null
+++ b/ee/app/services/analytics/ai_analytics/ai_metrics_service.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Analytics
+  module AiAnalytics
+    class AiMetricsService
+      attr_reader :current_user, :namespace, :from, :to, :fields
+
+      def initialize(current_user, namespace:, from:, to:, fields:)
+        @current_user = current_user
+        @namespace = namespace
+        @from = from
+        @to = to
+        @fields = fields
+      end
+
+      def execute
+        data = {}
+
+        data = add_code_suggestions_usage(data)
+        data = add_duo_chat_usage(data)
+        data = add_duo_pro_assigned(data)
+
+        ServiceResponse.success(payload: data)
+      end
+
+      private
+
+      def add_duo_pro_assigned(data)
+        return data unless fields.include?(:duo_pro_assigned_users_count)
+
+        users = GitlabSubscriptions::AddOnAssignedUsersFinder.new(
+          current_user, namespace, add_on_name: :code_suggestions).execute
+
+        data.merge(duo_pro_assigned_users_count: users.count)
+      end
+
+      def add_code_suggestions_usage(data)
+        usage = CodeSuggestionUsageService.new(
+          current_user,
+          namespace: namespace,
+          from: from,
+          to: to,
+          fields: fields & CodeSuggestionUsageService::FIELDS
+        ).execute
+
+        usage.success? ? data.merge(usage.payload) : data
+      end
+
+      def add_duo_chat_usage(data)
+        usage = DuoChatUsageService.new(
+          current_user,
+          namespace: namespace,
+          from: from,
+          to: to,
+          fields: fields & DuoChatUsageService::FIELDS
+        ).execute
+
+        usage.success? ? data.merge(usage.payload) : data
+      end
+    end
+  end
+end
diff --git a/ee/spec/finders/gitlab_subscriptions/add_on_assigned_users_finder_spec.rb b/ee/spec/finders/gitlab_subscriptions/add_on_assigned_users_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..35f7803b6bd3f40e0536ddb5e5e1a2f629f8cce1
--- /dev/null
+++ b/ee/spec/finders/gitlab_subscriptions/add_on_assigned_users_finder_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSubscriptions::AddOnAssignedUsersFinder, feature_category: :seat_cost_management do
+  describe '#execute' do
+    let_it_be(:user) { create(:user) }
+    let_it_be(:namespace) { create(:group, owners: user) }
+    let_it_be(:subgroup) { create(:group, parent: namespace) }
+    let_it_be(:another_subgroup) { create(:group, parent: namespace) }
+    let_it_be(:project) { create(:project, group: another_subgroup) }
+    let_it_be(:add_on) { create(:gitlab_subscription_add_on, :gitlab_duo_pro) }
+
+    subject(:assigned_users) { described_class.new(user, namespace, add_on_name: :code_suggestions).execute }
+
+    describe '#execute' do
+      context 'without add_on_purchase' do
+        it { is_expected.to be_empty }
+      end
+
+      context 'with expired add_on_purchase' do
+        let_it_be(:add_on_purchase) do
+          create(:gitlab_subscription_add_on_purchase, :expired, add_on: add_on, namespace: namespace)
+        end
+
+        let_it_be(:member_with_duo_pro) do
+          create(:user, developer_of: namespace).tap do |u|
+            create(:gitlab_subscription_user_add_on_assignment, user: u, add_on_purchase: add_on_purchase)
+          end
+        end
+
+        it { is_expected.to be_empty }
+      end
+
+      context 'with add on purchase available' do
+        let_it_be(:add_on_purchase) do
+          create(:gitlab_subscription_add_on_purchase, :active, add_on: add_on, namespace: namespace)
+        end
+
+        let_it_be(:member_with_duo_pro) do
+          create(:user, developer_of: namespace).tap do |u|
+            create(:gitlab_subscription_user_add_on_assignment, user: u, add_on_purchase: add_on_purchase)
+          end
+        end
+
+        let_it_be(:subgroup_member_with_duo_pro) do
+          create(:user, developer_of: subgroup).tap do |u|
+            create(:gitlab_subscription_user_add_on_assignment, user: u, add_on_purchase: add_on_purchase)
+          end
+        end
+
+        let_it_be(:another_subgroup_member_with_duo_pro) do
+          create(:user, developer_of: another_subgroup).tap do |u|
+            create(:gitlab_subscription_user_add_on_assignment, user: u, add_on_purchase: add_on_purchase)
+          end
+        end
+
+        let_it_be(:project_member_with_duo_pro) do
+          create(:user, developer_of: project).tap do |u|
+            create(:gitlab_subscription_user_add_on_assignment, user: u, add_on_purchase: add_on_purchase)
+          end
+        end
+
+        let_it_be(:member_without_duo_pro) { create(:user, developer_of: namespace) }
+
+        it 'returns all assigned users of a group' do
+          expect(assigned_users).to match_array([member_with_duo_pro, another_subgroup_member_with_duo_pro,
+            subgroup_member_with_duo_pro])
+        end
+
+        context 'with subgroup' do
+          let(:assigned_users) { described_class.new(user, subgroup, add_on_name: :code_suggestions).execute }
+
+          it 'returns all subgroup members with assigned seat' do
+            expect(assigned_users).to match_array([member_with_duo_pro, subgroup_member_with_duo_pro])
+          end
+        end
+
+        context 'with project namespace' do
+          let(:assigned_users) do
+            described_class.new(user, project.project_namespace, add_on_name: :code_suggestions).execute
+          end
+
+          it 'returns all project members with assigned seat' do
+            expect(assigned_users)
+              .to match_array([member_with_duo_pro, another_subgroup_member_with_duo_pro, project_member_with_duo_pro])
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/requests/api/graphql/analytics/ai_analytics/ai_metrics_spec.rb b/ee/spec/requests/api/graphql/analytics/ai_analytics/ai_metrics_spec.rb
index 5833db62bd6f4f3efe0f18db35942b2303dcb6bb..afddcaf8af021ccdbe82ab8f172063e0d8188f4d 100644
--- a/ee/spec/requests/api/graphql/analytics/ai_analytics/ai_metrics_spec.rb
+++ b/ee/spec/requests/api/graphql/analytics/ai_analytics/ai_metrics_spec.rb
@@ -18,7 +18,7 @@
   shared_examples 'common ai metrics' do
     let(:fields) do
       %w[codeSuggestionsContributorsCount codeContributorsCount codeSuggestionsShownCount codeSuggestionsAcceptedCount
-        duoChatContributorsCount]
+        duoChatContributorsCount duoProAssignedUsersCount]
     end
 
     let(:from) { '2024-05-01'.to_date }
@@ -26,22 +26,21 @@
     let(:filter_params) { { startDate: from, endDate: to } }
     let(:expected_filters) { { from: from, to: to } }
 
-    before do
-      allow_next_instance_of(::Analytics::AiAnalytics::CodeSuggestionUsageService,
-        current_user, hash_including(expected_filters)) do |instance|
-        allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: {
-          code_contributors_count: 10,
-          code_suggestions_contributors_count: 3,
-          code_suggestions_shown_count: 5,
-          code_suggestions_accepted_count: 2
-        }))
-      end
+    let(:service_payload) do
+      {
+        code_contributors_count: 10,
+        code_suggestions_contributors_count: 3,
+        code_suggestions_shown_count: 5,
+        code_suggestions_accepted_count: 2,
+        duo_chat_contributors_count: 8,
+        duo_pro_assigned_users_count: 18
+      }
+    end
 
-      allow_next_instance_of(::Analytics::AiAnalytics::DuoChatUsageService,
+    before do
+      allow_next_instance_of(::Analytics::AiAnalytics::AiMetricsService,
         current_user, hash_including(expected_filters)) do |instance|
-        allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: {
-          duo_chat_contributors_count: 8
-        }))
+        allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: service_payload))
       end
 
       post_graphql(query, current_user: current_user)
@@ -53,10 +52,33 @@
         'codeContributorsCount' => 10,
         'codeSuggestionsShownCount' => 5,
         'codeSuggestionsAcceptedCount' => 2,
-        'duoChatContributorsCount' => 8
+        'duoChatContributorsCount' => 8,
+        'duoProAssignedUsersCount' => 18
       })
     end
 
+    context 'when AiMetrics service returns only part of queried fields' do
+      let(:service_payload) do
+        {
+          code_contributors_count: 10,
+          code_suggestions_contributors_count: 3,
+          code_suggestions_shown_count: 5,
+          code_suggestions_accepted_count: 2
+        }
+      end
+
+      it 'returns all metrics filled by default' do
+        expect(ai_metrics).to eq({
+          'codeSuggestionsContributorsCount' => 3,
+          'codeContributorsCount' => 10,
+          'codeSuggestionsShownCount' => 5,
+          'codeSuggestionsAcceptedCount' => 2,
+          'duoChatContributorsCount' => nil,
+          'duoProAssignedUsersCount' => nil
+        })
+      end
+    end
+
     context 'when filter range is too wide' do
       let(:filter_params) { { startDate: 5.years.ago } }
 
diff --git a/ee/spec/services/analytics/ai_analytics/ai_metrics_service_spec.rb b/ee/spec/services/analytics/ai_analytics/ai_metrics_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fdd2ac477b0e1d85755a545119504514fe780877
--- /dev/null
+++ b/ee/spec/services/analytics/ai_analytics/ai_metrics_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Analytics::AiAnalytics::AiMetricsService, feature_category: :value_stream_management do
+  subject(:service_response) do
+    described_class.new(current_user, namespace: container, from: from, to: to, fields: fields).execute
+  end
+
+  let_it_be(:group) { create(:group) }
+  let_it_be(:subgroup) { create(:group, parent: group) }
+  let_it_be(:project) { create(:project, group: subgroup) }
+  let_it_be(:user1) { create(:user, developer_of: group) }
+
+  let(:current_user) { user1 }
+  let(:from) { Time.current }
+  let(:to) { Time.current }
+  let(:fields) do
+    Analytics::AiAnalytics::DuoChatUsageService::FIELDS +
+      Analytics::AiAnalytics::CodeSuggestionUsageService::FIELDS +
+      [:duo_pro_assigned_users_count]
+  end
+
+  let(:expected_filters) { { from: from, to: to } }
+
+  before do
+    allow(Gitlab::ClickHouse).to receive(:enabled_for_analytics?).and_return(true)
+  end
+
+  shared_examples 'common ai metrics service' do
+    before do
+      allow_next_instance_of(::Analytics::AiAnalytics::DuoChatUsageService,
+        current_user,
+        hash_including(expected_filters.merge(fields: ::Analytics::AiAnalytics::DuoChatUsageService::FIELDS))
+      ) do |instance|
+        allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: {
+          duo_chat_contributors_count: 8
+        }))
+      end
+
+      allow_next_instance_of(::Analytics::AiAnalytics::CodeSuggestionUsageService,
+        current_user,
+        hash_including(expected_filters.merge(fields: ::Analytics::AiAnalytics::CodeSuggestionUsageService::FIELDS))
+      ) do |instance|
+        allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: {
+          code_contributors_count: 10,
+          code_suggestions_contributors_count: 3,
+          code_suggestions_shown_count: 5,
+          code_suggestions_accepted_count: 2
+        }))
+      end
+
+      allow_next_instance_of(GitlabSubscriptions::AddOnAssignedUsersFinder,
+        current_user, container, add_on_name: :code_suggestions) do |instance|
+        allow(instance).to receive(:execute).and_return([:foo, :bar, :baz])
+      end
+    end
+
+    it 'returns merged payload of all services' do
+      expect(service_response).to be_success
+      expect(service_response.payload).to eq({
+        duo_chat_contributors_count: 8,
+        code_contributors_count: 10,
+        code_suggestions_contributors_count: 3,
+        code_suggestions_shown_count: 5,
+        code_suggestions_accepted_count: 2,
+        duo_pro_assigned_users_count: 3
+      })
+    end
+  end
+
+  context 'for group' do
+    let_it_be(:container) { subgroup }
+
+    it_behaves_like 'common ai metrics service'
+  end
+
+  context 'for project' do
+    let_it_be(:container) { project.project_namespace.reload }
+
+    it_behaves_like 'common ai metrics service'
+  end
+end