diff --git a/app/graphql/resolvers/projects/plan_limits_resolver.rb b/app/graphql/resolvers/projects/plan_limits_resolver.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f47a8f3c65e90aa9fdf9b0be997332acc960882e
--- /dev/null
+++ b/app/graphql/resolvers/projects/plan_limits_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+  module Projects
+    class PlanLimitsResolver < BaseResolver
+      include Gitlab::Graphql::Authorize::AuthorizeResource
+
+      type Types::ProjectPlanLimitsType, null: false
+
+      authorize :read_project
+
+      def resolve
+        authorize!(object)
+
+        schedule_allowed = Ability.allowed?(current_user, :read_ci_pipeline_schedules_plan_limit, object)
+
+        {
+          ci_pipeline_schedules: schedule_allowed ? object.actual_limits.ci_pipeline_schedules : nil
+        }
+      end
+    end
+  end
+end
diff --git a/app/graphql/types/project_plan_limits_type.rb b/app/graphql/types/project_plan_limits_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ed7e5bee9270716feeae95d4dec35e525a924461
--- /dev/null
+++ b/app/graphql/types/project_plan_limits_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+  # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request
+  class ProjectPlanLimitsType < BaseObject
+    graphql_name 'ProjectPlanLimits'
+    description 'Plan limits for the current project.'
+
+    field :ci_pipeline_schedules, GraphQL::Types::Int, null: true,
+      description: 'Maximum number of pipeline schedules allowed per project.'
+  end
+  # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 5dbf20cc7252e9123a9da3f3e187a5f2b22c1f32..db850f60910450546df646bf6f4ff2dc6db2ba69 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -698,6 +698,12 @@ class ProjectType < BaseObject
       calls_gitaly: true,
       alpha: { milestone: '16.9' }
 
+    field :project_plan_limits, Types::ProjectPlanLimitsType,
+      resolver: Resolvers::Projects::PlanLimitsResolver,
+      description: 'Plan limits for the current project.',
+      alpha: { milestone: '16.9' },
+      null: true
+
     def protectable_branches
       ProtectableDropdown.new(project, :branches).protectable_ref_names
     end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 01c9d94e0e69cb9f48e10e097e4a6b1dcd53ce9a..a686a94b7c66d93bdd1febb57fb6e9af04bbfa56 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -992,6 +992,10 @@ class ProjectPolicy < BasePolicy
 
   rule { ~private_project & guest & external_user }.enable :read_container_image
 
+  rule { can?(:create_pipeline_schedule) }.policy do
+    enable :read_ci_pipeline_schedules_plan_limit
+  end
+
   private
 
   def user_is_user?
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index af136cad7b367a35bd37ffebae517bb785b1bab0..7881f5afc33ec5372cb1e5a5479e89d3ab131545 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -24470,6 +24470,7 @@ Represents vulnerability finding of a security report on the pipeline.
 | <a id="projectprintingmergerequestlinkenabled"></a>`printingMergeRequestLinkEnabled` | [`Boolean`](#boolean) | Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line. |
 | <a id="projectproductanalyticsinstrumentationkey"></a>`productAnalyticsInstrumentationKey` **{warning-solid}** | [`String`](#string) | **Introduced** in 16.0. **Status**: Experiment. Product Analytics instrumentation key assigned to the project. |
 | <a id="projectproductanalyticsstate"></a>`productAnalyticsState` **{warning-solid}** | [`ProductAnalyticsState`](#productanalyticsstate) | **Introduced** in 15.10. **Status**: Experiment. Current state of the product analytics stack for this project.Can only be called for one project in a single request. |
+| <a id="projectprojectplanlimits"></a>`projectPlanLimits` **{warning-solid}** | [`ProjectPlanLimits`](#projectplanlimits) | **Introduced** in 16.9. **Status**: Experiment. Plan limits for the current project. |
 | <a id="projectprotectablebranches"></a>`protectableBranches` **{warning-solid}** | [`[String!]`](#string) | **Introduced** in 16.9. **Status**: Experiment. List of unprotected branches, ignoring any wildcard branch rules. |
 | <a id="projectpublicjobs"></a>`publicJobs` | [`Boolean`](#boolean) | Indicates if there is public access to pipelines and job details of the project, including output logs and artifacts. |
 | <a id="projectpushrules"></a>`pushRules` | [`PushRules`](#pushrules) | Project's push rules settings. |
@@ -26128,6 +26129,16 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
 | <a id="projectpermissionsupdatewiki"></a>`updateWiki` | [`Boolean!`](#boolean) | If `true`, the user can perform `update_wiki` on this resource. |
 | <a id="projectpermissionsuploadfile"></a>`uploadFile` | [`Boolean!`](#boolean) | If `true`, the user can perform `upload_file` on this resource. |
 
+### `ProjectPlanLimits`
+
+Plan limits for the current project.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectplanlimitscipipelineschedules"></a>`ciPipelineSchedules` | [`Int`](#int) | Maximum number of pipeline schedules allowed per project. |
+
 ### `ProjectRepositoryRegistry`
 
 Represents the Geo replication and verification state of a project repository.
diff --git a/spec/graphql/resolvers/projects/plan_limits_resolver_spec.rb b/spec/graphql/resolvers/projects/plan_limits_resolver_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2476964b94c49b0347334c4301d947c89f4eda38
--- /dev/null
+++ b/spec/graphql/resolvers/projects/plan_limits_resolver_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Projects::PlanLimitsResolver, feature_category: :api do
+  include GraphqlHelpers
+
+  let_it_be(:user) { create(:user) }
+  let(:project) { build(:project, :repository) }
+
+  describe 'Pipeline schedule limits' do
+    before do
+      project.add_owner(user)
+    end
+
+    it 'gets the current limits for pipeline schedules' do
+      limits = resolve_plan_limits
+
+      expect(limits).to include({ ci_pipeline_schedules: project.actual_limits.ci_pipeline_schedules })
+    end
+  end
+
+  describe 'Pipeline schedule limits without authorization' do
+    it 'returns a ResourceNotAvailable error' do
+      expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ResourceNotAvailable) do
+        resolve_plan_limits
+      end
+    end
+
+    it 'returns null when a user is not allowed to see the limit but allowed to see project' do
+      project.add_reporter(user)
+
+      limits = resolve_plan_limits
+
+      expect(limits).to include({ ci_pipeline_schedules: nil })
+    end
+  end
+
+  def resolve_plan_limits(args: {})
+    resolve(described_class, obj: project, ctx: { current_user: user }, args: args)
+  end
+end
diff --git a/spec/graphql/types/project_plan_limits_type_spec.rb b/spec/graphql/types/project_plan_limits_type_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ac9af421a518f568dcb2ed9d6d88ceb678537d6f
--- /dev/null
+++ b/spec/graphql/types/project_plan_limits_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::ProjectPlanLimitsType, feature_category: :api do
+  include GraphqlHelpers
+
+  specify { expect(described_class.graphql_name).to eq('ProjectPlanLimits') }
+
+  it 'exposes the expected fields' do
+    expected_fields = %i[ci_pipeline_schedules]
+
+    expect(described_class).to have_graphql_fields(*expected_fields)
+  end
+end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 9f4bf4f6b369c873f8eb21b58a119c081802fd2e..69060ee4e1ebaf1e6e122530c5cbf6c98b1ecebc 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -216,6 +216,7 @@ def set_access_level(access_level)
         expect_allowed(:update_pipeline)
         expect_allowed(:cancel_pipeline)
         expect_allowed(:create_pipeline_schedule)
+        expect_allowed(:read_ci_pipeline_schedules_plan_limit)
       end
     end
 
@@ -228,6 +229,7 @@ def set_access_level(access_level)
         expect_disallowed(:cancel_pipeline)
         expect_disallowed(:destroy_pipeline)
         expect_disallowed(:create_pipeline_schedule)
+        expect_disallowed(:read_ci_pipeline_schedules_plan_limit)
       end
     end