diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2c801fda2097f2c0f7b89559ac8ff846988193f0..5ce3842d4bf41f82d5d4daa38fa8a403df15c866 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,7 +6,7 @@ stages:
   - deploy
 
 variables:
-    VERSION: "3.1"
+    VERSION: "3.3"
     SONARQUBE_URL: https://sonarqube.lam.fr
     CONTAINER_IMAGE: portus.lam.fr/anis/anis-server
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b82ca7a2ed4a487106f528c7df03cb60aa5f4a63..9b2bbcd3e6a146f07f917140c4ab2eb6c2c20d4b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [3.3.0]
+### Added
+ - #54: Added search by cone-search
+
 ## [3.2.1]
 ### Fixed
  - #55: Fixed bug json in json when only one column is selected
diff --git a/VERSION b/VERSION
index 0444f320767571752143e9169e56454d978101fc..0fa4ae4890372d16ea2933390c4ba9f8f28d3f8a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2.1
\ No newline at end of file
+3.3.0
\ No newline at end of file
diff --git a/anis-server.yaml b/anis-server.yaml
index aa30093867058cb5af5d217a3f6b5bb927fe9e53..1a44c1ff8426abe776aa4ca3a582ef4b28ae0789 100644
--- a/anis-server.yaml
+++ b/anis-server.yaml
@@ -1,6 +1,6 @@
 openapi: "3.0.0"
 info:
-  version: 3.1.0
+  version: 3.3.0
   title: Anis Server
   description: 'AstroNomical Information System is a generic web tool that aims to facilitate the provision of data (Astrophysics), accessible from a database, to a community of scientists.'
   contact:
diff --git a/conf-dev/create-db.sh b/conf-dev/create-db.sh
index 00b936b1120037eacc4f117ff2f35f8afcd15c13..f3457957dc30aea77e0bf0b9c3182b14d295452f 100644
--- a/conf-dev/create-db.sh
+++ b/conf-dev/create-db.sh
@@ -17,9 +17,9 @@ curl -d '{"name":"colibri","label":"Colibri Project Test","description":"Project
 
 # Add default dataset family and ANIS datasets
 curl -d '{"label":"Default dataset family","display":10}' -H "Content-Type: application/json" -X POST http://localhost/instance/default/dataset-family
-curl -d '{"name":"obs_cat","table_ref":"obs_cat","label":"ObsCat dataset","description":"ObsCat","display":10,"count":10000,"vo":false,"data_path":"/mnt/mount","selectable_row":true,"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
-curl -d '{"name":"observations","table_ref":"observations_info","label":"Observations dataset","description":"Observations","display":20,"count":177454,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
-curl -d '{"name":"vipers_dr2_w1","table_ref":"aspic_vipers_dr2_w1","label":"VIPERS-W1 (DR2)","description":"VIPERS W1 dataset","display":30,"count":1000,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
+curl -d '{"name":"obs_cat","table_ref":"obs_cat","label":"ObsCat dataset","description":"ObsCat","display":10,"count":10000,"vo":false,"data_path":"/mnt/mount","selectable_row":true,"config":{"cone_search":{"enabled":true,"column_ra":2,"column_dec":3}},"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
+curl -d '{"name":"observations","table_ref":"observations_info","label":"Observations dataset","description":"Observations","display":20,"count":177454,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"config":{},"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
+curl -d '{"name":"vipers_dr2_w1","table_ref":"aspic_vipers_dr2_w1","label":"VIPERS-W1 (DR2)","description":"VIPERS W1 dataset","display":30,"count":1000,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"config":{},"project_name":"anis_project"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/1/dataset
 
 # Add ANIS obs_cat default criteria family, default output family and default output category
 curl -d '{"label":"Default criteria family","display":10}' -H "Content-Type: application/json" -X POST http://localhost/dataset/obs_cat/criteria-family
@@ -28,9 +28,9 @@ curl -d '{"label":"Default output category","display":10}' -H "Content-Type: app
 
 # Add SVOM dataset family and SVOM datasets
 curl -d '{"label":"Svom dataset family","display":20}' -H "Content-Type: application/json" -X POST http://localhost/instance/default/dataset-family
-curl -d '{"name":"l1","table_ref":"public.v_rawproducts","label":"L0 & L1","description":"L0 & L1 products list","display":10,"count":0,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
-curl -d '{"name":"products","table_ref":"public.v_products","label":"Scientific Products","description":"SR3 & SR4 products list","display":20,"count":0,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
-curl -d '{"name":"sp_cards","table_ref":"sp_cards","label":"SP Metadata","description":"Contains metadata of scientific products (Core Program & General Program)","display":30,"count":100,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
+curl -d '{"name":"l1","table_ref":"public.v_rawproducts","label":"L0 & L1","description":"L0 & L1 products list","display":10,"count":0,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"config":{},"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
+curl -d '{"name":"products","table_ref":"public.v_products","label":"Scientific Products","description":"SR3 & SR4 products list","display":20,"count":0,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"config":{},"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
+curl -d '{"name":"sp_cards","table_ref":"sp_cards","label":"SP Metadata","description":"Contains metadata of scientific products (Core Program & General Program)","display":30,"count":100,"vo":false,"data_path":"/mnt/mount","selectable_row":false,"config":{},"project_name":"svom"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/2/dataset
 
 # Add SVOM l1 criteria families, default output family and default output category
 curl -d '{"label":"Obs","display":10}' -H "Content-Type: application/json" -X POST http://localhost/dataset/l1/criteria-family
@@ -99,7 +99,7 @@ curl -d '{"id":9,"name":"json_schema_uploaded","table_name":"sp_cards","label":"
 
 # Add COLIBRI dataset family and COLIBRI dataset
 curl -d '{"label":"Colibri dataset family","display":30}' -H "Content-Type: application/json" -X POST http://localhost/instance/default/dataset-family
-curl -d '{"name":"anis_observation","table_ref":"anis_observation","label":"COLIBRI OBS","description":"colibri observations","display":10,"count":0,"vo":true,"data_path":"/mnt/mount","selectable_row":true,"project_name":"colibri"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/3/dataset
+curl -d '{"name":"anis_observation","table_ref":"anis_observation","label":"COLIBRI OBS","description":"colibri observations","display":10,"count":0,"vo":true,"data_path":"/mnt/mount","selectable_row":true,"config":{},"project_name":"colibri"}' -H "Content-Type: application/json" -X POST http://localhost/dataset-family/3/dataset
 
 # Add COLIBRI anis_observation default criteria family, output families and output categories
 curl -d '{"label":"Default criteria family","display":10}' -H "Content-Type: application/json" -X POST http://localhost/dataset/anis_observation/criteria-family
diff --git a/src/Action/SearchAction.php b/src/Action/SearchAction.php
index 58e84b273f5193efc866d046f2925ca928714862..b15d43cce8014dca805567a6bfcc172195ebc539 100644
--- a/src/Action/SearchAction.php
+++ b/src/Action/SearchAction.php
@@ -103,6 +103,11 @@ final class SearchAction extends AbstractAction
                 $attributes = $this->select($queryBuilder, $dataset, explode(';', $queryParams['a']));
             }
 
+            // The special parameter cs is not mandatory and represents the Cone Search
+            if (array_key_exists('cs', $queryParams)) {
+                $this->coneSearch($queryBuilder, $dataset, $queryParams['cs']);
+            }
+
             // The parameter c is not mandatory and represents the SQL where clause
             if (array_key_exists('c', $queryParams)) {
                 $this->where($queryBuilder, $dataset, explode(';', $queryParams['c']));
@@ -156,6 +161,67 @@ final class SearchAction extends AbstractAction
         return $attributes;
     }
 
+    /**
+     * Adds the cone search clause to the request
+     *
+     * @param QueryBuilder $queryBuilder Represents the query being built
+     * @param Dataset      $dataset      Represents the requested dataset
+     * @param string       $param        The substring of the url (parameter cs)
+     */
+    private function coneSearch(QueryBuilder $queryBuilder, Dataset $dataset, string $param): void
+    {
+        if (count(explode(':', $param)) < 3) {
+            throw SearchException::badNumberOfParamsForConeSearch();
+        }
+
+        list($ra, $dec, $radius) = explode(':', $param);
+        $coneSearchConfig = $dataset->getConfig()['cone_search'];
+
+        if ($coneSearchConfig['enabled'] !== true) {
+            throw SearchException::coneSearchUnavailable();
+        }
+
+        $attributeRa = $this->getAttribute($dataset, $coneSearchConfig['column_ra']);
+        $attributeDec = $this->getAttribute($dataset, $coneSearchConfig['column_dec']);
+        $columnRa = $dataset->getTableRef() . '.' . $attributeRa->getName();
+        $columnDec = $dataset->getTableRef() . '.' . $attributeDec->getName();
+
+        $cdcl2 = pow(cos($dec * (M_PI * 2) / 360), 2);
+        if ($radius == 0) {
+            $radius = 1 / 1000000;
+        }
+        $radius2 = pow($radius, 2);
+
+        $raddeg = $radius / 3600;
+        $decmin = $dec - $raddeg;
+        $decmax = $dec + $raddeg;
+        if ($decmin < -90 || $decmax > 90) {
+            $ramin = 0;
+            $ramax = 360;
+        } else {
+            $ra_corrected_radius = $raddeg / cos(deg2rad(abs($dec) + $raddeg));
+            $ramin = $ra - $ra_corrected_radius;
+            $ramax = $ra + $ra_corrected_radius;
+        }
+
+        $coneSearchValue = '(' . $cdcl2 . ' * (' . $ra . ' - ' . $columnRa . ') * (' . $ra . ' - ' . $columnRa . '))';
+        $coneSearchValue .= ' + ((' . $dec . ' - ' . $columnDec . ') * (' . $dec . ' - ' . $columnDec . '))';
+        $coneSearchCriterion = $queryBuilder->expr()->lte($coneSearchValue, $radius2);
+        $raCriterion = (new CompositeExpression(CompositeExpression::TYPE_AND, [
+            $queryBuilder->expr()->gte($columnRa, $ramin),
+            $queryBuilder->expr()->lte($columnRa, $ramax)
+        ]));
+        $decCriterion = (new CompositeExpression(CompositeExpression::TYPE_AND, [
+            $queryBuilder->expr()->gte($columnDec, $decmin),
+            $queryBuilder->expr()->lte($columnDec, $decmax)
+        ]));
+        $queryBuilder->where(new CompositeExpression(CompositeExpression::TYPE_AND, [
+            $coneSearchCriterion,
+            $raCriterion,
+            $decCriterion
+        ]));
+    }
+
     /**
      * Adds the where clause to the request
      *
@@ -185,7 +251,7 @@ final class SearchAction extends AbstractAction
             );
             $expressions[] = $operator->getExpression();
         }
-        $queryBuilder->where(new CompositeExpression(CompositeExpression::TYPE_AND, $expressions));
+        $queryBuilder->andWhere(new CompositeExpression(CompositeExpression::TYPE_AND, $expressions));
     }
 
     /**
diff --git a/src/Utils/Operator/LessThanEqual.php b/src/Utils/Operator/LessThanEqual.php
index ec0315df06ce6a6872e58d60392dd0e8fd6c4fed..0a3bcae1a51fdf6069dfdd84e58bbef95db41a73 100644
--- a/src/Utils/Operator/LessThanEqual.php
+++ b/src/Utils/Operator/LessThanEqual.php
@@ -49,7 +49,7 @@ class LessThanEqual extends Operator
      *
      * @return string
      */
-    public function getExpression() : string
+    public function getExpression(): string
     {
         return $this->expr->lte($this->column, $this->getSqlValue($this->value));
     }
diff --git a/src/Utils/SearchException.php b/src/Utils/SearchException.php
index 67b44dc4e33ffc2870abc4f1de35236599e8b038..1ac20014c117aae22b40b52fe52ffb30068e7bfa 100644
--- a/src/Utils/SearchException.php
+++ b/src/Utils/SearchException.php
@@ -87,6 +87,22 @@ class SearchException extends Exception
         return new self("Limit (p) needs 2 params to work");
     }
 
+    /**
+     * @return SearchException
+     */
+    public static function badNumberOfParamsForConeSearch(): SearchException
+    {
+        return new self("Cone search (cs) needs 3 params to work");
+    }
+
+    /**
+     * @return SearchException
+     */
+    public static function coneSearchUnavailable(): SearchException
+    {
+        return new self("The cone search is unavailable for this dataset");
+    }
+
     /**
      * @return SearchException
      */