diff --git a/CRM/Utils/Address.php b/CRM/Utils/Address.php
index ff30f95cb138c82f9781db19cb0fc3512b65fcc6..ff54a5b83b56ac42f42389c02fa8ccdc8196c931 100644
--- a/CRM/Utils/Address.php
+++ b/CRM/Utils/Address.php
@@ -598,4 +598,13 @@ class CRM_Utils_Address {
     return CRM_Utils_Address::format($addressFields);
   }
 
+  /**
+   * @return string
+   */
+  public static function getDefaultDistanceUnit() {
+    $countryDefault = Civi::settings()->get('defaultContactCountry');
+    // US, UK use miles. Everything else is Km
+    return ($countryDefault == '1228' || $countryDefault == '1226') ? 'miles' : 'km';
+  }
+
 }
diff --git a/CRM/Utils/Geocode/Google.php b/CRM/Utils/Geocode/Google.php
index ad670f6c94ffe796fde3d12bba175f70a4b98ca8..0885a3d3ae6a59daa4d31dc7e8a707c33b05af3e 100644
--- a/CRM/Utils/Geocode/Google.php
+++ b/CRM/Utils/Geocode/Google.php
@@ -51,8 +51,6 @@ class CRM_Utils_Geocode_Google {
       return FALSE;
     }
 
-    $config = CRM_Core_Config::singleton();
-
     $add = '';
 
     if (!empty($values['street_address'])) {
@@ -99,6 +97,37 @@ class CRM_Utils_Geocode_Google {
       $add .= '+' . urlencode(str_replace('', '+', $values['country']));
     }
 
+    $coord = self::makeRequest($add);
+
+    $values['geo_code_1'] = $coord['geo_code_1'] ?? 'null';
+    $values['geo_code_2'] = $coord['geo_code_2'] ?? 'null';
+
+    if (isset($coord['geo_code_error'])) {
+      $values['geo_code_error'] = $coord['geo_code_error'];
+    }
+
+    return isset($coord['geo_code_1'], $coord['geo_code_2']);
+  }
+
+  /**
+   * @param string $address
+   *   Plain text address
+   * @return array
+   * @throws \GuzzleHttp\Exception\GuzzleException
+   */
+  public static function getCoordinates($address) {
+    return self::makeRequest(urlencode($address));
+  }
+
+  /**
+   * @param string $add
+   *   Url-encoded address
+   * @return array
+   * @throws \GuzzleHttp\Exception\GuzzleException
+   */
+  private static function makeRequest($add) {
+
+    $config = CRM_Core_Config::singleton();
     if (!empty($config->geoAPIKey)) {
       $add .= '&key=' . urlencode($config->geoAPIKey);
     }
@@ -115,7 +144,7 @@ class CRM_Utils_Geocode_Google {
     if ($xml === FALSE) {
       // account blocked maybe?
       CRM_Core_Error::debug_var('Geocoding failed.  Message from Google:', $string);
-      return FALSE;
+      return ['geo_code_error' => $string];
     }
 
     if (isset($xml->status)) {
@@ -126,23 +155,22 @@ class CRM_Utils_Geocode_Google {
       ) {
         $ret = $xml->result->geometry->location->children();
         if ($ret->lat && $ret->lng) {
-          $values['geo_code_1'] = (float) $ret->lat;
-          $values['geo_code_2'] = (float) $ret->lng;
-          return TRUE;
+          return [
+            'geo_code_1' => (float) $ret->lat,
+            'geo_code_2' => (float) $ret->lng,
+          ];
         }
       }
       elseif ($xml->status == 'ZERO_RESULTS') {
         // reset the geo code values if we did not get any good values
-        $values['geo_code_1'] = $values['geo_code_2'] = 'null';
-        return FALSE;
+        return [];
       }
       else {
         CRM_Core_Error::debug_var("Geocoding failed. Message from Google: ({$xml->status})", (string ) $xml->error_message);
-        $values['geo_code_1'] = $values['geo_code_2'] = 'null';
-        $values['geo_code_error'] = $xml->status;
-        return FALSE;
+        return ['geo_code_error' => $xml->status];
       }
     }
+    return [];
   }
 
 }
diff --git a/Civi/Api4/Action/Address/GetCoordinates.php b/Civi/Api4/Action/Address/GetCoordinates.php
new file mode 100644
index 0000000000000000000000000000000000000000..86bcd16a49026ab6095a476409dfd9e1c14d3589
--- /dev/null
+++ b/Civi/Api4/Action/Address/GetCoordinates.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\Address;
+
+use Civi\Api4\Generic\Result;
+
+/**
+ * Converts an address string to lat/long coordinates.
+ *
+ * @method $this setAddress(string $address)
+ * @method string getAddress()
+ */
+class GetCoordinates extends \Civi\Api4\Generic\AbstractAction {
+
+  /**
+   * Address string to convert to lat/long
+   *
+   * @var string
+   * @required
+   */
+  protected $address;
+
+  public function _run(Result $result) {
+    $coord = \CRM_Utils_Geocode_Google::getCoordinates($this->address);
+    if (isset($coord['geo_code_1'], $coord['geo_code_2'])) {
+      $result[] = $coord;
+    }
+    elseif (!empty($coord['geo_code_error'])) {
+      throw new \API_Exception('Geocoding failed. ' . $coord['geo_code_error']);
+    }
+  }
+
+}
diff --git a/Civi/Api4/Address.php b/Civi/Api4/Address.php
index a4ec60aeb31eefeeac3fa2cd34681d97bd8f3877..c9db2a80150d569211bca93f329a8c14855c192f 100644
--- a/Civi/Api4/Address.php
+++ b/Civi/Api4/Address.php
@@ -54,4 +54,13 @@ class Address extends Generic\DAOEntity {
       ->setCheckPermissions($checkPermissions);
   }
 
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Address\GetCoordinates
+   */
+  public static function getCoordinates($checkPermissions = TRUE) {
+    return (new Action\Address\GetCoordinates(__CLASS__, __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
 }
diff --git a/Civi/Api4/Generic/BasicGetFieldsAction.php b/Civi/Api4/Generic/BasicGetFieldsAction.php
index afaee7fa0794a75286d49ceebd557e69445c4e58..494af4829cd44341f42e73e02ee646e6f485ac9f 100644
--- a/Civi/Api4/Generic/BasicGetFieldsAction.php
+++ b/Civi/Api4/Generic/BasicGetFieldsAction.php
@@ -340,6 +340,7 @@ class BasicGetFieldsAction extends BasicGetAction {
           'Radio' => ts('Radio Buttons'),
           'Select' => ts('Select'),
           'Text' => ts('Text'),
+          'Location' => ts('Address Location'),
         ],
       ],
       [
diff --git a/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..65e891bde9fae5724c1b299b99fdb44330caa720
--- /dev/null
+++ b/Civi/Api4/Service/Spec/Provider/AddressGetSpecProvider.php
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Address;
+use Civi\Api4\Query\Api4SelectQuery;
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class AddressGetSpecProvider implements Generic\SpecProviderInterface {
+
+  /**
+   * @param \Civi\Api4\Service\Spec\RequestSpec $spec
+   */
+  public function modifySpec(RequestSpec $spec) {
+    // Groups field
+    $field = new FieldSpec('proximity', 'Address', 'Boolean');
+    $field->setLabel(ts('Address Proximity'))
+      ->setTitle(ts('Address Proximity'))
+      ->setInputType('Location')
+      ->setColumnName('geo_code_1')
+      ->setDescription(ts('Address is within a given distance to a location'))
+      ->setType('Filter')
+      ->setOperators(['<='])
+      ->addSqlFilter([__CLASS__, 'getProximitySql']);
+    $spec->addFieldSpec($field);
+  }
+
+  /**
+   * @param string $entity
+   * @param string $action
+   *
+   * @return bool
+   */
+  public function applies($entity, $action) {
+    return $entity === 'Address' && $action === 'get';
+  }
+
+  /**
+   * @param array $field
+   * @param string $fieldAlias
+   * @param string $operator
+   * @param mixed $value
+   * @param \Civi\Api4\Query\Api4SelectQuery $query
+   * @param int $depth
+   * return string
+   */
+  public static function getProximitySql(array $field, string $fieldAlias, string $operator, $value, Api4SelectQuery $query, int $depth): string {
+    $unit = $value['distance_unit'] ?? 'km';
+    $distance = $value['distance'] ?? 0;
+
+    if ($unit === 'miles') {
+      $distance = $distance * 1609.344;
+    }
+    else {
+      $distance = $distance * 1000.00;
+    }
+
+    if (!isset($value['geo_code_1'], $value['geo_code_2'])) {
+      $value = Address::getCoordinates(FALSE)
+        ->setAddress($value['address'])
+        ->execute()->first();
+    }
+
+    if (
+      isset($value['geo_code_1']) && is_numeric($value['geo_code_1']) &&
+      isset($value['geo_code_2']) && is_numeric($value['geo_code_2'])
+    ) {
+      return \CRM_Contact_BAO_ProximityQuery::where(
+        $value['geo_code_1'],
+        $value['geo_code_2'],
+        $distance,
+        explode('.', $fieldAlias)[0]
+      );
+    }
+
+    return '(0)';
+  }
+
+}
diff --git a/ext/legacycustomsearches/CRM/Contact/Form/Search/Custom/Proximity.php b/ext/legacycustomsearches/CRM/Contact/Form/Search/Custom/Proximity.php
index e572e99f5e8bfbeba7d903575252ab04ffbd82eb..d33c0731a4011cbf93fa6e3935d3a027daae82d8 100644
--- a/ext/legacycustomsearches/CRM/Contact/Form/Search/Custom/Proximity.php
+++ b/ext/legacycustomsearches/CRM/Contact/Form/Search/Custom/Proximity.php
@@ -237,7 +237,7 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C
   }
 
   /**
-   * @return array|null
+   * @return array
    */
   public function setDefaultValues() {
     if (!empty($this->_formValues)) {
@@ -246,22 +246,17 @@ class CRM_Contact_Form_Search_Custom_Proximity extends CRM_Contact_Form_Search_C
     $config = CRM_Core_Config::singleton();
     $countryDefault = $config->defaultContactCountry;
     $stateprovinceDefault = $config->defaultContactStateProvince;
-    $defaults = [];
+    $defaults = [
+      'prox_distance_unit' => CRM_Utils_Address::getDefaultDistanceUnit(),
+    ];
 
     if ($countryDefault) {
-      if ($countryDefault == '1228' || $countryDefault == '1226') {
-        $defaults['prox_distance_unit'] = 'miles';
-      }
-      else {
-        $defaults['prox_distance_unit'] = 'km';
-      }
       $defaults['country_id'] = $countryDefault;
       if ($stateprovinceDefault) {
         $defaults['state_province_id'] = $stateprovinceDefault;
       }
-      return $defaults;
     }
-    return NULL;
+    return $defaults;
   }
 
   /**
diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php
index 5a652474b3886bc39ee79250fd9ab90b971baa63..5eb3d4601a7743e63e432fe627ef4cec4f04dbbc 100644
--- a/ext/search_kit/Civi/Search/Admin.php
+++ b/ext/search_kit/Civi/Search/Admin.php
@@ -47,6 +47,7 @@ class Admin {
       'defaultDisplay' => SearchDisplay::getDefault(FALSE)->setSavedSearch(['id' => NULL])->execute()->first(),
       'modules' => $extensions,
       'defaultContactType' => \CRM_Contact_BAO_ContactType::basicTypeInfo()['Individual']['name'] ?? NULL,
+      'defaultDistanceUnit' => \CRM_Utils_Address::getDefaultDistanceUnit(),
       'tags' => Tag::get()
         ->addSelect('id', 'name', 'color', 'is_selectable', 'description')
         ->addWhere('used_for', 'CONTAINS', 'civicrm_saved_search')
diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.component.js
index cd235a73128d0853aa2e035cf29b9a559db684a0..4b7b4b05ee0cd4d2e1093454ab25d64e22bb847b 100644
--- a/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.component.js
+++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.component.js
@@ -61,6 +61,10 @@
         return expr.indexOf('(') > -1;
       };
 
+      this.areFunctionsAllowed = function(expr) {
+        return this.allowFunctions && ctrl.getField(expr).type !== 'Filter';
+      };
+
       this.addGroup = function(op) {
         ctrl.clauses.push([op, []]);
       };
diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.html b/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.html
index b5a3ef25fb478483fbf295ee7c94f1df84bf9ef5..5baaaa7bd1ca0ab0c7cbc4227390556e704d428f 100644
--- a/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.html
+++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchClause.html
@@ -15,7 +15,7 @@
         </span>
       </div>
       <div ng-if="!$ctrl.conjunctions[clause[0]]" class="api4-input-group">
-        <crm-search-function ng-if="$ctrl.allowFunctions" class="form-group" expr="clause[0]" mode="clause"></crm-search-function>
+        <crm-search-function ng-if="$ctrl.areFunctionsAllowed(clause[0])" class="form-group" expr="clause[0]" mode="clause"></crm-search-function>
         <span ng-if="!$ctrl.hasFunction(clause[0])">
           <input class="form-control collapsible-optgroups" ng-model="clause[0]" crm-ui-select="{data: $ctrl.fields, allowClear: true, placeholder: 'Field'}" ng-change="$ctrl.changeClauseField(clause, index)" />
         </span>
diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchCondition.html b/ext/search_kit/ang/crmSearchAdmin/crmSearchCondition.html
index f1c149de3a5e6b9b77423b04e6395afc5d52392c..ed54a75a289ae8c4696685b362fe89fe44799cd5 100644
--- a/ext/search_kit/ang/crmSearchAdmin/crmSearchCondition.html
+++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchCondition.html
@@ -1,2 +1,2 @@
-<select class="form-control api4-operator" ng-model="$ctrl.getSetOperator" ng-model-options="{getterSetter: true}" ng-options="o.key as o.value for o in $ctrl.getOperators()" ng-change="$ctrl.changeClauseOperator()" ></select>
+<select class="form-control api4-operator" ng-model="$ctrl.getSetOperator" ng-if="$ctrl.getOperators().length > 1" ng-model-options="{getterSetter: true}" ng-options="o.key as o.value for o in $ctrl.getOperators()" ng-change="$ctrl.changeClauseOperator()" ></select>
 <crm-search-input ng-if="$ctrl.operatorTakesInput()" ng-model="$ctrl.getSetValue" ng-model-options="{getterSetter: true}" field="$ctrl.field" option-key="$ctrl.optionKey" op="$ctrl.getSetOperator()" format="$ctrl.format" class="form-group"></crm-search-input>
diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchInput/crmSearchInputVal.component.js b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/crmSearchInputVal.component.js
index a249019898ceb36389faacd84d3135988edce4ab..5cc68b77d209e8fb0124f8e28f909e17eed662f6 100644
--- a/ext/search_kit/ang/crmSearchTasks/crmSearchInput/crmSearchInputVal.component.js
+++ b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/crmSearchInputVal.component.js
@@ -9,7 +9,7 @@
     },
     require: {ngModel: 'ngModel'},
     template: '<div class="form-group" ng-include="$ctrl.getTemplate()"></div>',
-    controller: function($scope, formatForSelect2) {
+    controller: function($scope, formatForSelect2, crmApi4) {
       var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
         ctrl = this;
 
@@ -95,6 +95,21 @@
         }
       };
 
+      this.lookupAddress = function() {
+        ctrl.value.geo_code_1 = null;
+        ctrl.value.geo_code_2 = null;
+        if (ctrl.value.address) {
+          crmApi4('Address', 'getCoordinates', {
+            address: ctrl.value.address
+          }).then(function(coordinates) {
+            if (coordinates[0]) {
+              ctrl.value.geo_code_1 = coordinates[0].geo_code_1;
+              ctrl.value.geo_code_2 = coordinates[0].geo_code_2;
+            }
+          });
+        }
+      };
+
       this.getTemplate = function() {
         var field = ctrl.field || {};
 
@@ -102,6 +117,11 @@
           return '~/crmSearchTasks/crmSearchInput/text.html';
         }
 
+        if (field.input_type === 'Location') {
+          ctrl.value = ctrl.value || {distance_unit: CRM.crmSearchAdmin.defaultDistanceUnit};
+          return '~/crmSearchTasks/crmSearchInput/location.html';
+        }
+
         if (isDateField(field)) {
           return '~/crmSearchTasks/crmSearchInput/date.html';
         }
diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchInput/location.html b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/location.html
new file mode 100644
index 0000000000000000000000000000000000000000..8e5959808490556d24722942464810539f1f9cc9
--- /dev/null
+++ b/ext/search_kit/ang/crmSearchTasks/crmSearchInput/location.html
@@ -0,0 +1,8 @@
+<div class="form-group">
+  <input class="form-control" type="number" ng-model="$ctrl.value.distance" placeholder="{{:: ts('Distance') }}" >
+  <select class="form-control" ng-model="$ctrl.value.distance_unit">
+    <option value="km">{{:: ts('Km') }}</option>
+    <option value="miles">{{:: ts('Miles') }}</option>
+  </select>
+  <input class="form-control" ng-model="$ctrl.value.address" placeholder="{{:: ts('Street, City, State, Country') }}" ng-change="$ctrl.lookupAddress()" ng-model-options="{updateOn: 'blur'}" >
+</div>
diff --git a/tests/phpunit/api/v4/Entity/AddressTest.php b/tests/phpunit/api/v4/Entity/AddressTest.php
index 19c21e40447249d5b06d20afc5fc72fada0d3c6b..087bf350637cabba18517fee842268d04731f297 100644
--- a/tests/phpunit/api/v4/Entity/AddressTest.php
+++ b/tests/phpunit/api/v4/Entity/AddressTest.php
@@ -59,4 +59,29 @@ class AddressTest extends Api4TestBase implements TransactionalInterface {
     $this->assertTrue($addresses[1]['is_primary']);
   }
 
+  public function testSearchProximity() {
+    $cid = $this->createTestRecord('Contact')['id'];
+    $sampleData = [
+      ['geo_code_1' => 20, 'geo_code_2' => 20],
+      ['geo_code_1' => 21, 'geo_code_2' => 21],
+      ['geo_code_1' => 19, 'geo_code_2' => 19],
+      ['geo_code_1' => 15, 'geo_code_2' => 15],
+    ];
+    $addreses = $this->saveTestRecords('Address', [
+      'records' => $sampleData,
+      'defaults' => ['contact_id' => $cid],
+    ])->column('id');
+
+    $result = Address::get(FALSE)
+      ->addWhere('contact_id', '=', $cid)
+      ->addWhere('proximity', '<=', ['distance' => 600, 'geo_code_1' => 20, 'geo_code_2' => 20])
+      ->execute()->column('id');
+
+    $this->assertCount(3, $result);
+    $this->assertContains($addreses[0], $result);
+    $this->assertContains($addreses[1], $result);
+    $this->assertContains($addreses[2], $result);
+    $this->assertNotContains($addreses[3], $result);
+  }
+
 }