diff --git a/app/models/project.rb b/app/models/project.rb
index 35e38cd37d1ef85e9e1a3c9a25b8e929b3e9ad53..26d5ca6c69b47caa265518208b25138854645ce4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -551,6 +551,7 @@ def self.integration_association_name(name)
       delegate :show_default_award_emojis, :show_default_award_emojis=
       delegate :enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=
       delegate :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=
+      delegate :code_suggestions, :code_suggestions=
     end
   end
 
@@ -3199,6 +3200,11 @@ def instance_runner_running_jobs_count
   end
   strong_memoize_attr :instance_runner_running_jobs_count
 
+  def code_suggestions_enabled?
+    code_suggestions && (group.nil? || group.code_suggestions)
+  end
+  strong_memoize_attr :code_suggestions_enabled?
+
   private
 
   # overridden in EE
diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb
index f684feacace1420b84e692602d93ea1ae4d04508..e3ffe2347d868dd40514305cbf622fbdd6be5966 100644
--- a/app/models/project_setting.rb
+++ b/app/models/project_setting.rb
@@ -39,6 +39,7 @@ class ProjectSetting < ApplicationRecord
   validates :issue_branch_template, length: { maximum: Issue::MAX_BRANCH_TEMPLATE }
   validates :target_platforms, inclusion: { in: ALLOWED_TARGET_PLATFORMS }
   validates :suggested_reviewers_enabled, inclusion: { in: [true, false] }
+  validates :code_suggestions, allow_nil: false, inclusion: { in: [true, false] }
 
   validates :pages_unique_domain,
     uniqueness: { if: -> { pages_unique_domain.present? } },
diff --git a/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb b/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb
new file mode 100644
index 0000000000000000000000000000000000000000..53c303963a55c7030b703b0981d3a4bdaf3c52db
--- /dev/null
+++ b/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddCodeSuggestionsToProjectSetting < Gitlab::Database::Migration[2.2]
+  enable_lock_retries!
+  milestone '16.7'
+
+  def change
+    add_column :project_settings, :code_suggestions, :boolean, default: true, null: false
+  end
+end
diff --git a/db/schema_migrations/20231205163658 b/db/schema_migrations/20231205163658
new file mode 100644
index 0000000000000000000000000000000000000000..c2ecfa8486bcc1e1d10c36d71b6a5c2f5a5fee4c
--- /dev/null
+++ b/db/schema_migrations/20231205163658
@@ -0,0 +1 @@
+94118057fe8e0d4ed9ac6590e3aa48088f26524f02dead72f338ff58c078ef33
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index dc6e8c4cef9a6e3e81452d48b8eb509b0057d620..c1ef64251e1dce60e5e53afab904349b9c3b13d0 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22195,6 +22195,7 @@ CREATE TABLE project_settings (
     encrypted_product_analytics_configurator_connection_string_iv bytea,
     pages_multiple_versions_enabled boolean DEFAULT false NOT NULL,
     allow_merge_without_pipeline boolean DEFAULT false NOT NULL,
+    code_suggestions boolean DEFAULT true NOT NULL,
     CONSTRAINT check_1a30456322 CHECK ((char_length(pages_unique_domain) <= 63)),
     CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
     CONSTRAINT check_3ca5cbffe6 CHECK ((char_length(issue_branch_template) <= 255)),
diff --git a/ee/lib/api/code_suggestions.rb b/ee/lib/api/code_suggestions.rb
index 69e2a29e68ee7f7231fdf8e526f4f70760f7b152..e6fb68c3d3ad06ff054c69a5b0f47bca14b3a468 100644
--- a/ee/lib/api/code_suggestions.rb
+++ b/ee/lib/api/code_suggestions.rb
@@ -173,6 +173,35 @@ def gitlab_realm
           body ''
         end
       end
+
+      resources :enabled do
+        desc 'Code suggestions enabled for a project' do
+          success code: 200
+          failure [
+            { code: 401, message: 'Unauthorized' },
+            { code: 403, message: '403 Code Suggestions Disabled' },
+            { code: 404, message: 'Not found' }
+          ]
+        end
+        params do
+          requires :project_path, type: String, desc: 'The path of the project',
+            documentation: { example: 'namespace/project' }
+        end
+
+        post do
+          path = declared_params[:project_path]
+
+          not_found! if path.empty?
+
+          projects = ::ProjectsFinder.new(params: { full_paths: [path] }, current_user: current_user).execute
+
+          not_found! if projects.none?
+
+          forbidden! unless projects.first.code_suggestions_enabled?
+
+          status :ok
+        end
+      end
     end
   end
 end
diff --git a/ee/spec/requests/api/code_suggestions_spec.rb b/ee/spec/requests/api/code_suggestions_spec.rb
index b8a76901a6b43f63a1d4f0726defb6e33d61d96e..253a0dca3dd33992cdea22b2de58c0c96b19c335 100644
--- a/ee/spec/requests/api/code_suggestions_spec.rb
+++ b/ee/spec/requests/api/code_suggestions_spec.rb
@@ -725,4 +725,51 @@ def is_even(n: int) ->
       end
     end
   end
+
+  context 'when checking in project has code suggestions enabled' do
+    let_it_be(:enabled_project) { create(:project, :with_code_suggestions_enabled) }
+    let(:current_user) { authorized_user }
+    let_it_be(:disabled_project) { create(:project, :with_code_suggestions_disabled) }
+    let_it_be(:secret_project) { create(:project, :with_code_suggestions_enabled) }
+
+    before_all do
+      enabled_project.add_maintainer(authorized_user)
+      disabled_project.add_maintainer(authorized_user)
+    end
+
+    subject { post api("/code_suggestions/enabled", current_user), params: { project_path: project_path } }
+
+    context 'when not logged in' do
+      let(:current_user) { nil }
+      let(:project_path) { enabled_project.full_path }
+
+      it { is_expected.to eq(401) }
+    end
+
+    context 'when authorized' do
+      context 'when enabled' do
+        let(:project_path) { enabled_project.full_path }
+
+        it { is_expected.to eq(200) }
+      end
+
+      context 'when disabled' do
+        let(:project_path) { disabled_project.full_path }
+
+        it { is_expected.to eq(403) }
+      end
+
+      context 'when user cannot access project' do
+        let(:project_path) { secret_project.full_path }
+
+        it { is_expected.to eq(404) }
+      end
+
+      context 'when does not exist' do
+        let(:project_path) { 'not_a_real_project' }
+
+        it { is_expected.to eq(404) }
+      end
+    end
+  end
 end
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 12e022bfb2044f5ac43ea5c4ef166c32d7b9104e..b8e489c9d5192b48884cdbaa76c0b1131e3e9a9e 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -41,6 +41,7 @@ class Project < BasicProjectDetails
         end
       end
 
+      expose :code_suggestions, documentation: { type: 'boolean' }
       expose :packages_enabled, documentation: { type: 'boolean' }
       expose :empty_repo?, as: :empty_repo, documentation: { type: 'boolean' }
       expose :archived?, as: :archived, documentation: { type: 'boolean' }
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 4da7dcba7635dab605b531463157c63f8a7f98a1..a2848bd025613647183c93009bc44237373121cf 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -608,4 +608,16 @@
     path { 'gitlab-profile' }
     files { { 'README.md' => 'Hello World' } }
   end
+
+  trait :with_code_suggestions_enabled do
+    after(:create) do |project|
+      project.project_setting.update!(code_suggestions: true)
+    end
+  end
+
+  trait :with_code_suggestions_disabled do
+    after(:create) do |project|
+      project.project_setting.update!(code_suggestions: false)
+    end
+  end
 end