diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php
index af188f313609..0f1125f5ca2c 100644
--- a/CRM/Api4/Page/Api4Explorer.php
+++ b/CRM/Api4/Page/Api4Explorer.php
@@ -10,7 +10,6 @@
  +--------------------------------------------------------------------+
  */
 
-use Civi\Api4\Service\Schema\Joinable\Joinable;
 use Civi\Api4\Utils\CoreUtil;
 
 /**
@@ -23,18 +22,11 @@ class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page {
   public function run() {
     $apiDoc = new ReflectionFunction('civicrm_api4');
     $groupOptions = civicrm_api4('Group', 'getFields', ['loadOptions' => TRUE, 'select' => ['options', 'name'], 'where' => [['name', 'IN', ['visibility', 'group_type']]]]);
-    // Don't show n-to-many joins in Explorer
-    $entityLinks = (array) civicrm_api4('Entity', 'getLinks', [], ['entity' => 'links']);
-    foreach ($entityLinks as $entity => $links) {
-      $entityLinks[$entity] = array_filter($links, function($link) {
-        return $link['joinType'] != Joinable::JOIN_TYPE_ONE_TO_MANY;
-      });
-    }
+
     $vars = [
       'operators' => CoreUtil::getOperators(),
       'basePath' => Civi::resources()->getUrl('civicrm'),
       'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(),
-      'links' => $entityLinks,
       'docs' => \Civi\Api4\Utils\ReflectionUtils::parseDocBlock($apiDoc->getDocComment()),
       'functions' => self::getSqlFunctions(),
       'groupOptions' => array_column((array) $groupOptions, 'options', 'name'),
diff --git a/Civi/Api4/Action/Entity/GetLinks.php b/Civi/Api4/Action/Entity/GetLinks.php
index 8ae38eead3d2..e0458b56b599 100644
--- a/Civi/Api4/Action/Entity/GetLinks.php
+++ b/Civi/Api4/Action/Entity/GetLinks.php
@@ -15,11 +15,15 @@
 use Civi\Api4\Utils\CoreUtil;
 
 /**
- * Get a list of FK links between entities
+ * Get a list of FK links between entities.
+ *
+ * This action is deprecated; the API no longer uses these links to determine available joins.
+ * @deprecated
  */
 class GetLinks extends \Civi\Api4\Generic\BasicGetAction {
 
   public function getRecords() {
+    \CRM_Core_Error::deprecatedWarning('APIv4 Entity::getLinks is deprecated.');
     $result = [];
     /** @var \Civi\Api4\Service\Schema\SchemaMap $schema */
     $schema = \Civi::container()->get('schema_map');
diff --git a/Civi/Api4/Entity.php b/Civi/Api4/Entity.php
index b693895572c2..1660d74d8cb0 100644
--- a/Civi/Api4/Entity.php
+++ b/Civi/Api4/Entity.php
@@ -128,6 +128,7 @@ public static function getFields($checkPermissions = TRUE) {
 
   /**
    * @param bool $checkPermissions
+   * @deprecated
    * @return Action\Entity\GetLinks
    */
   public static function getLinks($checkPermissions = TRUE) {
diff --git a/ang/api4Explorer/Explorer.html b/ang/api4Explorer/Explorer.html
index f28cec6dbe9b..3d800559426b 100644
--- a/ang/api4Explorer/Explorer.html
+++ b/ang/api4Explorer/Explorer.html
@@ -10,10 +10,10 @@ <h1 crm-page-title>
         <div class="panel-heading">
           <div class="form-inline">
             <span ng-mouseenter="help('entity', paramDoc('$entity'))" ng-mouseleave="help()">
-              <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="::{placeholder: ts('Entity'), data: entities}" />
+              <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="::{placeholder: ts('Entity'), data: entities, formatResultCssClass: formatResultCssClass}" />
             </span>
             <span ng-mouseenter="help('action', paramDoc('$action'))" ng-mouseleave="help()">
-              <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="::{placeholder: ts('Action'), data: actions}" />
+              <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="::{placeholder: ts('Action'), data: actions, formatResultCssClass: formatResultCssClass}" />
             </span>
             <input class="form-control api4-index" type="search" ng-model="index" ng-mouseenter="help('index', paramDoc('$index'))" ng-mouseleave="help()" placeholder="{{:: ts('Index') }}" />
             <button class="btn btn-success pull-right" crm-icon="fa-bolt" ng-disabled="!entity || !action || loading" ng-click="execute()" ng-mouseenter="help(ts('Execute'), executeDoc())" ng-mouseleave="help()">{{:: ts('Execute') }}</button>
diff --git a/ang/api4Explorer/Explorer.js b/ang/api4Explorer/Explorer.js
index c51793b3a241..a0cdc38ac82e 100644
--- a/ang/api4Explorer/Explorer.js
+++ b/ang/api4Explorer/Explorer.js
@@ -2,8 +2,6 @@
 
   // Schema metadata
   var schema = CRM.vars.api4.schema;
-  // FK schema data
-  var links = CRM.vars.api4.links;
   // Cache list of entities
   var entities = [];
   // Cache list of actions
@@ -13,7 +11,6 @@
   // Api params
   var params;
 
-
   angular.module('api4Explorer').config(function($routeProvider) {
     $routeProvider.when('/explorer/:api4entity?/:api4action?', {
       controller: 'Api4Explorer',
@@ -168,17 +165,17 @@
         }
       });
       // Add implicit joins based on schema links
-      _.each(links[$scope.entity], function(link) {
-        var linkFields = _.cloneDeep(entityFields(link.entity)),
-          wildCard = addWildcard ? [{id: link.alias + '.*', text: link.alias + '.*', 'description': 'All core ' + link.entity + ' fields'}] : [];
-        if (linkFields) {
+      _.each(entityFields($scope.entity, $scope.action), function(field) {
+        if (field.fk_entity) {
+          var linkFields = _.cloneDeep(entityFields(field.fk_entity)),
+            wildCard = addWildcard ? [{id: field.name + '.*', text: field.name + '.*', 'description': 'All core ' + field.fk_entity + ' fields'}] : [];
           if (addPseudoconstant) {
             addPseudoconstants(linkFields, addPseudoconstant);
           }
           fieldList.push({
-            text: link.alias,
-            description: 'Implicit join to ' + link.entity,
-            children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.'))
+            text: field.name,
+            description: 'Implicit join to ' + field.fk_entity,
+            children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], field.name + '.'))
           });
         }
       });
@@ -361,13 +358,13 @@
       if (joins[alias]) {
         return joins[alias].entity;
       }
-      // Then lookup implicit links
+      // Then lookup implicit joins
       _.each(path, function(node) {
-        var link = _.find(links[entity], {alias: node});
-        if (!link) {
+        var field = getField(node, entity, $scope.action);
+        if (!field || !field.fk_entity) {
           return false;
         }
-        entity = link.entity;
+        entity = field.fk_entity;
       });
       return entity;
     }
@@ -476,10 +473,15 @@
       $scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
     };
 
+    // Select2 formatter: Add 'strikethrough' class to deprecated items
+    $scope.formatResultCssClass = function(result) {
+      return result.deprecated ? 'strikethrough' : '';
+    };
+
     function selectAction() {
       $scope.action = $routeParams.api4action;
       if (!actions.length) {
-        formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
+        formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params', 'deprecated']);
       }
       if ($scope.action) {
         var actionInfo = _.findWhere(actions, {id: $scope.action});
@@ -958,7 +960,7 @@
       if ($scope.entity && $routeParams.api4action !== newVal && !_.isUndefined(newVal)) {
         $location.url('/explorer/' + $scope.entity + '/' + newVal);
       } else if (newVal) {
-        setHelp($scope.entity + '::' + newVal, _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment', 'see']));
+        setHelp($scope.entity + '::' + newVal, _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment', 'see', 'deprecated']));
       }
     });
 
@@ -1413,7 +1415,7 @@
       }
       var linkName = fieldNames.shift(),
         join = getExplicitJoins()[linkName],
-        newEntity = join ? join.entity : _.findWhere(links[entity], {alias: linkName}).entity;
+        newEntity = join ? join.entity : _.findWhere(entityFields(entity, action), {name: linkName}).fk_entity;
       return get(newEntity, fieldNames);
     }
   }