diff --git a/docs/ChangeLog/PR24120.md b/docs/ChangeLog/PR24120.md
new file mode 100644
index 0000000000..5c635349ea
--- /dev/null
+++ b/docs/ChangeLog/PR24120.md
@@ -0,0 +1,19 @@
+## Changes requiring user action
+
+### Key Override Introspection
+
+Changes were made to key overrides in order to hook them into the keymap introspection system.
+
+Key override signature changed from:
+
+```c
+const key_override_t **key_overrides = (const key_override_t *[]){
+```
+
+to:
+
+```c
+const key_override_t *key_overrides[] = {
+```
+
+The list of key overrides now does not need to be `NULL`-terminated.
diff --git a/docs/features/key_overrides.md b/docs/features/key_overrides.md
index 4c568f1679..9b6015175c 100644
--- a/docs/features/key_overrides.md
+++ b/docs/features/key_overrides.md
@@ -14,7 +14,7 @@ You can use key overrides in a similar way to momentary layer/fn keys to activat
 
 To enable this feature, you need to add `KEY_OVERRIDE_ENABLE = yes` to your `rules.mk`.
 
-Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides` is `NULL`-terminated and contains pointers to `key_override_t` values (`const key_override_t **`).
+Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides`contains pointers to `key_override_t` values (`const key_override_t **`).
 
 ## Creating Key Overrides {#creating-key-overrides}
 
@@ -42,9 +42,8 @@ This shows how the mentioned example of sending `delete` when `shift` + `backspa
 const key_override_t delete_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_BSPC, KC_DEL);
 
 // This globally defines all key overrides to be used
-const key_override_t **key_overrides = (const key_override_t *[]){
-	&delete_key_override,
-	NULL // Null terminate the array of overrides!
+const key_override_t *key_overrides[] = {
+	&delete_key_override
 };
 ```
 
@@ -91,14 +90,13 @@ const key_override_t brightness_up_override = ko_make_with_layers_negmods_and_op
 const key_override_t brightness_down_override = ko_make_basic(MOD_MASK_CSA, KC_MPLY, KC_BRID);
 
 // This globally defines all key overrides to be used
-const key_override_t **key_overrides = (const key_override_t *[]){
+const key_override_t *key_overrides[] = {
 	&next_track_override,
 	&prev_track_override,
 	&vol_up_override,
 	&vol_down_override,
 	&brightness_up_override,
-	&brightness_down_override,
-	NULL
+	&brightness_down_override
 };
 ```
 
@@ -112,10 +110,9 @@ const key_override_t tilde_esc_override = ko_make_basic(MOD_MASK_SHIFT, KC_ESC,
 // GUI + esc = `
 const key_override_t grave_esc_override = ko_make_basic(MOD_MASK_GUI, KC_ESC, KC_GRV);
 
-const key_override_t **key_overrides = (const key_override_t *[]){
+const key_override_t *key_overrides[] = {
 	&tilde_esc_override,
-	&grave_esc_override,
-	NULL
+	&grave_esc_override
 };
 ```
 
diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c
index 4e95125335..327df6e277 100644
--- a/quantum/keymap_introspection.c
+++ b/quantum/keymap_introspection.c
@@ -10,6 +10,7 @@
 #endif // INTROSPECTION_KEYMAP_C
 
 #include "keymap_introspection.h"
+#include "util.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Key mapping
@@ -83,7 +84,7 @@ uint16_t keycode_at_dip_switch_map_location_raw(uint8_t switch_idx, bool on) {
     return KC_TRNS;
 }
 
-uint16_t keycode_at_dip_switch_map_location(uint8_t switch_idx, bool on) {
+__attribute__((weak)) uint16_t keycode_at_dip_switch_map_location(uint8_t switch_idx, bool on) {
     return keycode_at_dip_switch_map_location_raw(switch_idx, on);
 }
 
@@ -95,13 +96,16 @@ uint16_t keycode_at_dip_switch_map_location(uint8_t switch_idx, bool on) {
 #if defined(COMBO_ENABLE)
 
 uint16_t combo_count_raw(void) {
-    return sizeof(key_combos) / sizeof(combo_t);
+    return ARRAY_SIZE(key_combos);
 }
 __attribute__((weak)) uint16_t combo_count(void) {
     return combo_count_raw();
 }
 
 combo_t* combo_get_raw(uint16_t combo_idx) {
+    if (combo_idx >= combo_count_raw()) {
+        return NULL;
+    }
     return &key_combos[combo_idx];
 }
 __attribute__((weak)) combo_t* combo_get(uint16_t combo_idx) {
@@ -116,19 +120,48 @@ __attribute__((weak)) combo_t* combo_get(uint16_t combo_idx) {
 #if defined(TAP_DANCE_ENABLE)
 
 uint16_t tap_dance_count_raw(void) {
-    return sizeof(tap_dance_actions) / sizeof(tap_dance_action_t);
+    return ARRAY_SIZE(tap_dance_actions);
 }
 
-uint16_t tap_dance_count(void) {
+__attribute__((weak)) uint16_t tap_dance_count(void) {
     return tap_dance_count_raw();
 }
 
 tap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx) {
+    if (tap_dance_idx >= tap_dance_count_raw()) {
+        return NULL;
+    }
     return &tap_dance_actions[tap_dance_idx];
 }
 
-tap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx) {
+__attribute__((weak)) tap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx) {
     return tap_dance_get_raw(tap_dance_idx);
 }
 
 #endif // defined(TAP_DANCE_ENABLE)
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Key Overrides
+
+#if defined(KEY_OVERRIDE_ENABLE)
+
+uint16_t key_override_count_raw(void) {
+    return ARRAY_SIZE(key_overrides);
+}
+
+__attribute__((weak)) uint16_t key_override_count(void) {
+    return key_override_count_raw();
+}
+
+const key_override_t* key_override_get_raw(uint16_t key_override_idx) {
+    if (key_override_idx >= key_override_count_raw()) {
+        return NULL;
+    }
+    return key_overrides[key_override_idx];
+}
+
+__attribute__((weak)) const key_override_t* key_override_get(uint16_t key_override_idx) {
+    return key_override_get_raw(key_override_idx);
+}
+
+#endif // defined(KEY_OVERRIDE_ENABLE)
diff --git a/quantum/keymap_introspection.h b/quantum/keymap_introspection.h
index bc4dd93b4c..719825c674 100644
--- a/quantum/keymap_introspection.h
+++ b/quantum/keymap_introspection.h
@@ -88,3 +88,24 @@ tap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx);
 tap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx);
 
 #endif // defined(TAP_DANCE_ENABLE)
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Key Overrides
+
+#if defined(KEY_OVERRIDE_ENABLE)
+
+// Forward declaration of key_override_t so we don't need to deal with header reordering
+struct key_override_t;
+typedef struct key_override_t key_override_t;
+
+// Get the number of key overrides defined in the user's keymap, stored in firmware rather than any other persistent storage
+uint16_t key_override_count_raw(void);
+// Get the number of key overrides defined in the user's keymap, potentially stored dynamically
+uint16_t key_override_count(void);
+
+// Get the key override definitions, stored in firmware rather than any other persistent storage
+const key_override_t* key_override_get_raw(uint16_t key_override_idx);
+// Get the key override definitions, potentially stored dynamically
+const key_override_t* key_override_get(uint16_t key_override_idx);
+
+#endif // defined(KEY_OVERRIDE_ENABLE)
diff --git a/quantum/process_keycode/process_key_override.c b/quantum/process_keycode/process_key_override.c
index 264e2562b8..ce30477ee8 100644
--- a/quantum/process_keycode/process_key_override.c
+++ b/quantum/process_keycode/process_key_override.c
@@ -23,6 +23,7 @@
 #include "action_util.h"
 #include "quantum.h"
 #include "quantum_keycodes.h"
+#include "keymap_introspection.h"
 
 #ifndef KEY_OVERRIDE_REPEAT_DELAY
 #    define KEY_OVERRIDE_REPEAT_DELAY 500
@@ -83,9 +84,6 @@ static uint16_t deferred_register = 0;
 // TODO: in future maybe save in EEPROM?
 static bool enabled = true;
 
-// Public variables
-__attribute__((weak)) const key_override_t **key_overrides = NULL;
-
 // Forward decls
 static const key_override_t *clear_active_override(const bool allow_reregister);
 
@@ -247,12 +245,12 @@ static bool check_activation_event(const key_override_t *override, const bool ke
 
 /** Iterates through the list of key overrides and tries activating each, until it finds one that activates or reaches the end of overrides. Returns true if the key action for `keycode` should be sent */
 static bool try_activating_override(const uint16_t keycode, const uint8_t layer, const bool key_down, const bool is_mod, const uint8_t active_mods, bool *activated) {
-    if (key_overrides == NULL) {
+    if (key_override_count() == 0) {
         return true;
     }
 
-    for (uint8_t i = 0;; i++) {
-        const key_override_t *const override = key_overrides[i];
+    for (uint8_t i = 0; i < key_override_count(); i++) {
+        const key_override_t *const override = key_override_get(i);
 
         // End of array
         if (override == NULL) {
diff --git a/quantum/process_keycode/process_key_override.h b/quantum/process_keycode/process_key_override.h
index 3e37c7e63a..9ec84dbe6e 100644
--- a/quantum/process_keycode/process_key_override.h
+++ b/quantum/process_keycode/process_key_override.h
@@ -55,7 +55,7 @@ typedef enum {
 } ko_option_t;
 
 /** Defines a single key override */
-typedef struct {
+typedef struct key_override_t {
     // The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (trigger_mods) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to KC_NO to require only the necessary modifiers to be pressed and no non-modifier.
     uint16_t trigger;
 
@@ -87,9 +87,6 @@ typedef struct {
     bool *enabled;
 } key_override_t;
 
-/** Define this as a null-terminated array of pointers to key overrides. These key overrides will be used by qmk. */
-extern const key_override_t **key_overrides;
-
 /** Turns key overrides on */
 void key_override_on(void);