diff --git a/backend/src/main.py b/backend/src/main.py
index 0fcc6ce..a2efbf1 100644
--- a/backend/src/main.py
+++ b/backend/src/main.py
@@ -1,13 +1,14 @@
 """Main app for backend api"""
 
 import logging
-from fastapi import FastAPI, HTTPException
+from fastapi import FastAPI, HTTPException, Query
 
-from .structs.landmark import Landmark
+from .structs.landmark import Landmark, Toilets
 from .structs.preferences import Preferences
 from .structs.linked_landmarks import LinkedLandmarks
 from .structs.trip import Trip
 from .utils.landmarks_manager import LandmarkManager
+from .utils.toilets_manager import ToiletsManager
 from .utils.optimizer import Optimizer
 from .utils.refiner import Refiner
 from .persistence import client as cache_client
@@ -36,19 +37,15 @@ def new_trip(preferences: Preferences,
         (uuid) : The uuid of the first landmark in the optimized route
     """
     if preferences is None:
-        raise HTTPException(status_code=406,
-                            detail="Preferences not provided or incomplete.")
+        raise HTTPException(status_code=406, detail="Preferences not provided or incomplete.")
     if (preferences.shopping.score == 0 and
         preferences.sightseeing.score == 0 and
         preferences.nature.score == 0) :
-        raise HTTPException(status_code=406,
-                            detail="All preferences are 0.")
+        raise HTTPException(status_code=406, detail="All preferences are 0.")
     if start is None:
-        raise HTTPException(status_code=406,
-                            detail="Start coordinates not provided")
+        raise HTTPException(status_code=406, detail="Start coordinates not provided")
     if not (-90 <= start[0] <= 90 or -180 <= start[1] <= 180):
-        raise HTTPException(status_code=422,
-                            detail="Start coordinates not in range")
+        raise HTTPException(status_code=422, detail="Start coordinates not in range")
     if end is None:
         end = start
         logger.info("No end coordinates provided. Using start=end.")
@@ -135,3 +132,31 @@ def get_landmark(landmark_uuid: str) -> Landmark:
         return landmark
     except KeyError as exc:
         raise HTTPException(status_code=404, detail="Landmark not found") from exc
+
+
+@app.post("/toilets/new")
+def get_toilets(location: tuple[float, float] = Query(...), radius: int = 500) -> list[Toilets] :
+    """
+    Endpoint to find toilets within a specified radius from a given location.
+
+    This endpoint expects the `location` and `radius` as **query parameters**, not in the request body.
+
+    Args:
+        location (tuple[float, float]): The latitude and longitude of the location to search from.
+        radius (int, optional): The radius (in meters) within which to search for toilets. Defaults to 500 meters.
+
+    Returns:
+        list[Toilets]: A list of Toilets objects that meet the criteria.
+    """
+    if location is None:
+        raise HTTPException(status_code=406, detail="Coordinates not provided or invalid")
+    if not (-90 <= location[0] <= 90 or -180 <= location[1] <= 180):
+        raise HTTPException(status_code=422, detail="Start coordinates not in range")
+    
+    toilets_manager = ToiletsManager(location, radius)
+
+    try :
+        toilets_list = toilets_manager.generate_toilet_list()
+        return toilets_list
+    except KeyError as exc:
+        raise HTTPException(status_code=404, detail="No toilets found") from exc
diff --git a/backend/src/sandbox/get_streets.py b/backend/src/sandbox/get_streets.py
index c7f604c..7940017 100644
--- a/backend/src/sandbox/get_streets.py
+++ b/backend/src/sandbox/get_streets.py
@@ -79,8 +79,8 @@ def get_distance(p1: tuple[float, float], p2: tuple[float, float]) -> int:
     Calculate the time in minutes to travel from one location to another.
 
     Args:
-        p1 (Tuple[float, float]): Coordinates of the starting location.
-        p2 (Tuple[float, float]): Coordinates of the destination.
+        p1 (tuple[float, float]): Coordinates of the starting location.
+        p2 (tuple[float, float]): Coordinates of the destination.
 
         Returns:
         int: Time to travel from p1 to p2 in minutes.
diff --git a/backend/src/structs/landmark.py b/backend/src/structs/landmark.py
index 85eb996..a494896 100644
--- a/backend/src/structs/landmark.py
+++ b/backend/src/structs/landmark.py
@@ -115,3 +115,28 @@ class Landmark(BaseModel) :
         return (self.uuid == value.uuid or
                 self.osm_id == value.osm_id or
                 (self.name == value.name and self.distance(value) < 0.001))
+
+
+class Toilets(BaseModel) :
+    """
+    Model for toilets. When false/empty the information is either false either not known.
+    """
+    location : tuple
+    wheelchair : Optional[bool] = False
+    changing_table : Optional[bool] = False
+    fee : Optional[bool] = False
+    opening_hours : Optional[str] = ""
+
+
+    def __str__(self) -> str:
+        """
+        String representation of the Toilets object.
+
+        Returns:
+            str: A formatted string with the toilets location.
+        """
+        return f'Toilets @{self.location}'
+    
+    class Config:
+        # This allows us to easily convert the model to and from dictionaries
+        orm_mode = True
\ No newline at end of file
diff --git a/backend/src/tests/test_toilets.py b/backend/src/tests/test_toilets.py
new file mode 100644
index 0000000..89f71a5
--- /dev/null
+++ b/backend/src/tests/test_toilets.py
@@ -0,0 +1,103 @@
+"""Collection of tests to ensure correct implementation and track progress. """
+
+from fastapi.testclient import TestClient
+import pytest
+
+from ..structs.landmark import Toilets
+from ..main import app
+
+@pytest.fixture(scope="module")
+def client():
+    """Client used to call the app."""
+    return TestClient(app)
+
+@pytest.mark.parametrize(
+    "location,radius,status_code",
+    [
+        ({}, None, 422),                # Invalid case: no location at all.
+        ([443], None, 422),             # Invalid cases: invalid location.
+        ([443, 433], None, 422),             # Invalid cases: invalid location.
+    ]
+)
+def test_invalid_input(client, location, radius, status_code):    # pylint: disable=redefined-outer-name
+    """
+    Test n°1 : Verify handling of invalid input.
+
+    Args:
+        client:
+        request:
+    """
+    response = client.post(
+        "/toilets/new",
+        params={
+            "location": location,
+            "radius": radius
+        }
+    )
+
+    # checks :
+    assert response.status_code == status_code
+
+
+
+
+@pytest.mark.parametrize(
+    "location,status_code",
+    [
+        ([48.2270, 7.4370], 200), # Orschwiller.
+        ([10.2012, 10.123], 200), # Nigerian desert.
+        ([63.989, -19.677], 200), # Hekla volcano, Iceland
+    ]
+)
+def test_no_toilets(client, location, status_code):    # pylint: disable=redefined-outer-name
+    """
+    Test n°3 : Verify the code finds some toilets in big cities.
+
+    Args:
+        client:
+        request:
+    """
+    response = client.post(
+        "/toilets/new",
+        params={
+            "location": location
+        }
+    )
+    toilets_list = [Toilets.model_validate(toilet) for toilet in response.json()]
+
+    # checks :
+    assert response.status_code == 200  # check for successful planning
+    assert isinstance(toilets_list, list)  # check that the return type is a list
+
+
+
+@pytest.mark.parametrize(
+    "location,status_code",
+    [
+        ([45.7576485, 4.8330241], 200), # Lyon, Bellecour.
+        ([40.768502, -73.958408], 200), # New York, Upper East Side.
+        ([53.482864, -2.2411116], 200), # Manchester, centre.
+        ([-6.913795,  107.60278], 200), # Bandung, train station
+        ([-22.970140, -43.18181], 200), # Rio de Janeiro, Copacabana
+    ]
+)
+def test_toilets(client, location, status_code):    # pylint: disable=redefined-outer-name
+    """
+    Test n°3 : Verify the code finds some toilets in big cities.
+
+    Args:
+        client:
+        request:
+    """
+    response = client.post(
+        "/toilets/new",
+        params={
+            "location": location
+        }
+    )
+    toilets_list = [Toilets.model_validate(toilet) for toilet in response.json()]
+
+    # checks :
+    assert response.status_code == 200  # check for successful planning
+    assert isinstance(toilets_list, list)  # check that the return type is a list
+    assert len(toilets_list) > 0
\ No newline at end of file
diff --git a/backend/src/tests/test_utils.py b/backend/src/tests/test_utils.py
index ed4e271..d5b69ad 100644
--- a/backend/src/tests/test_utils.py
+++ b/backend/src/tests/test_utils.py
@@ -1,6 +1,5 @@
 """Helper methods for testing."""
 import logging
-from typing import List
 from fastapi import HTTPException
 from pydantic import ValidationError
 
@@ -8,7 +7,7 @@ from ..structs.landmark import Landmark
 from ..persistence import client as cache_client
 
 
-def landmarks_to_osmid(landmarks: List[Landmark]) -> List[int] :
+def landmarks_to_osmid(landmarks: list[Landmark]) -> list[int] :
     """
     Convert the list of landmarks into a list containing their osm ids for quick landmark checking.
     
@@ -95,7 +94,7 @@ def fetch_landmark_cache(landmark_uuid: str):
     
 
 
-def load_trip_landmarks(client, first_uuid: str, from_cache=None) -> List[Landmark]:
+def load_trip_landmarks(client, first_uuid: str, from_cache=None) -> list[Landmark]:
     """
     Load all landmarks for a trip using the response from the API.
 
@@ -120,7 +119,7 @@ def load_trip_landmarks(client, first_uuid: str, from_cache=None) -> List[Landma
     return landmarks
 
 
-def log_trip_details(request, landmarks: List[Landmark], duration: int, target_duration: int) :
+def log_trip_details(request, landmarks: list[Landmark], duration: int, target_duration: int) :
     """
     Allows to show the detailed trip in the html test report.
     
diff --git a/backend/src/utils/get_time_separation.py b/backend/src/utils/get_time_separation.py
index 210c28d..c8bd509 100644
--- a/backend/src/utils/get_time_separation.py
+++ b/backend/src/utils/get_time_separation.py
@@ -15,8 +15,8 @@ def get_time(p1: tuple[float, float], p2: tuple[float, float]) -> int:
     Calculate the time in minutes to travel from one location to another.
 
     Args:
-        p1 (Tuple[float, float]): Coordinates of the starting location.
-        p2 (Tuple[float, float]): Coordinates of the destination.
+        p1 (tuple[float, float]): Coordinates of the starting location.
+        p2 (tuple[float, float]): Coordinates of the destination.
 
         Returns:
         int: Time to travel from p1 to p2 in minutes.
@@ -55,8 +55,8 @@ def get_distance(p1: tuple[float, float], p2: tuple[float, float]) -> int:
     Calculate the time in minutes to travel from one location to another.
 
     Args:
-        p1 (Tuple[float, float]): Coordinates of the starting location.
-        p2 (Tuple[float, float]): Coordinates of the destination.
+        p1 (tuple[float, float]): Coordinates of the starting location.
+        p2 (tuple[float, float]): Coordinates of the destination.
 
         Returns:
         int: Time to travel from p1 to p2 in minutes.
diff --git a/backend/src/utils/landmarks_manager.py b/backend/src/utils/landmarks_manager.py
index 883fca9..c5e6091 100644
--- a/backend/src/utils/landmarks_manager.py
+++ b/backend/src/utils/landmarks_manager.py
@@ -79,6 +79,7 @@ class LandmarkManager:
 
         # Create a bbox using the around technique
         bbox = tuple((f"around:{reachable_bbox_side/2}", str(center_coordinates[0]), str(center_coordinates[1])))
+        
         # list for sightseeing
         if preferences.sightseeing.score != 0:
             score_function = lambda score: score * 10 * preferences.sightseeing.score / 5
diff --git a/backend/src/utils/optimizer.py b/backend/src/utils/optimizer.py
index e20aa65..d7f354e 100644
--- a/backend/src/utils/optimizer.py
+++ b/backend/src/utils/optimizer.py
@@ -44,7 +44,7 @@ class Optimizer:
             resx (list[float]): List of edge weights.
 
         Returns:
-            Tuple[list[int], list[int]]: A tuple containing a new row for constraint matrix and new value for upper bound vector.
+            tuple[list[int], list[int]]: A tuple containing a new row for constraint matrix and new value for upper bound vector.
         """
         
         for i, elem in enumerate(resx):
@@ -79,7 +79,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: A tuple containing a new row for constraint matrix and new value for upper bound vector.
+            tuple[np.ndarray, list[int]]: A tuple containing a new row for constraint matrix and new value for upper bound vector.
         """
 
         l1 = [0]*L*L
@@ -107,7 +107,7 @@ class Optimizer:
             resx (list): List of edge weights.
 
         Returns:
-            Tuple[list[int], Optional[list[list[int]]]]: A tuple containing the visit order and a list of any detected circles.
+            tuple[list[int], Optional[list[list[int]]]]: A tuple containing the visit order and a list of any detected circles.
         """
 
         # first round the results to have only 0-1 values
@@ -180,7 +180,7 @@ class Optimizer:
             max_time (int): Maximum time of visit allowed.
 
         Returns:
-            Tuple[list[float], list[float], list[int]]: Objective function coefficients, inequality constraint coefficients, and the right-hand side of the inequality constraint.
+            tuple[list[float], list[float], list[int]]: Objective function coefficients, inequality constraint coefficients, and the right-hand side of the inequality constraint.
         """
         
         # Objective function coefficients. a*x1 + b*x2 + c*x3 + ...
@@ -212,7 +212,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         ones = [1]*L
@@ -239,7 +239,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         upper_ind = np.triu_indices(L,0,L)
@@ -270,7 +270,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[list[np.ndarray], list[int]]: Equality constraint coefficients and the right-hand side of the equality constraints.
+            tuple[list[np.ndarray], list[int]]: Equality constraint coefficients and the right-hand side of the equality constraints.
         """
 
         l = [0]*L*L
@@ -293,7 +293,7 @@ class Optimizer:
             landmarks (list[Landmark]): List of landmarks, where some are marked as 'must_do'.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         L = len(landmarks)
@@ -319,7 +319,7 @@ class Optimizer:
             landmarks (list[Landmark]): List of landmarks, where some are marked as 'must_avoid'.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         L = len(landmarks)
@@ -346,7 +346,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         l_start = [1]*L + [0]*L*(L-1)   # sets departures only for start (horizontal ones)
@@ -374,7 +374,7 @@ class Optimizer:
             L (int): Number of landmarks.
 
         Returns:
-            Tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
+            tuple[np.ndarray, list[int]]: Inequality constraint coefficients and the right-hand side of the inequality constraints.
         """
 
         A = [0]*L*L
diff --git a/backend/src/utils/refiner.py b/backend/src/utils/refiner.py
index 28227a6..1f9c755 100644
--- a/backend/src/utils/refiner.py
+++ b/backend/src/utils/refiner.py
@@ -2,7 +2,6 @@ import yaml, logging
 
 from shapely import buffer, LineString, Point, Polygon, MultiPoint, concave_hull
 from math import pi
-from typing import List
 
 from ..structs.landmark import Landmark
 from . import take_most_important, get_time_separation
@@ -135,7 +134,7 @@ class Refiner :
     
         return tour
     
-    def integrate_landmarks(self, sub_list: List[Landmark], main_list: List[Landmark]) :
+    def integrate_landmarks(self, sub_list: list[Landmark], main_list: list[Landmark]) :
         """
         Inserts 'sub_list' of Landmarks inside the 'main_list' by leaving the ends untouched.
         
diff --git a/backend/src/utils/toilets_manager.py b/backend/src/utils/toilets_manager.py
new file mode 100644
index 0000000..210d00b
--- /dev/null
+++ b/backend/src/utils/toilets_manager.py
@@ -0,0 +1,78 @@
+import logging, yaml
+from OSMPythonTools.overpass import Overpass, overpassQueryBuilder
+from OSMPythonTools.cachingStrategy import CachingStrategy, JSON
+
+from ..structs.landmark import Toilets
+from ..constants import LANDMARK_PARAMETERS_PATH, OSM_CACHE_DIR
+
+
+# silence the overpass logger
+logging.getLogger('OSMPythonTools').setLevel(level=logging.CRITICAL)
+
+class ToiletsManager:
+
+    logger = logging.getLogger(__name__)
+
+    location: tuple[float, float]
+    radius: int    # radius in meters
+
+
+    def __init__(self, location: tuple[float, float], radius : int) -> None:
+
+        self.radius = radius
+        self.location = location
+        self.overpass = Overpass()
+        CachingStrategy.use(JSON, cacheDir=OSM_CACHE_DIR)
+
+
+    def generate_toilet_list(self) -> list[Toilets] :
+
+        
+        # Create a bbox using the around technique
+        bbox = tuple((f"around:{self.radius}", str(self.location[0]), str(self.location[1])))
+        toilets_list = []
+
+        query = overpassQueryBuilder(
+                bbox = bbox,
+                elementType = ['node', 'way', 'relation'],
+                # selector can in principle be a list already,
+                # but it generates the intersection of the queries
+                # we want the union
+                selector = ['"amenity"="toilets"'],
+                includeCenter = True,
+                out = 'center'
+                )
+        self.logger.debug(f"Query: {query}")
+
+        try:
+            result = self.overpass.query(query)
+        except Exception as e:
+            self.logger.error(f"Error fetching landmarks: {e}")
+            return None
+
+        for elem in result.elements():
+            location = (elem.centerLat(), elem.centerLon())
+
+            # handle unprecise and no-name locations
+            if location[0] is None:
+                    location = (elem.lat(), elem.lon())
+            else : 
+                continue
+            
+            toilets = Toilets(location=location)
+            
+            if 'wheelchair' in elem.tags().keys() and elem.tag('wheelchair') == 'yes':
+                toilets.wheelchair = True
+
+            if 'changing_table' in elem.tags().keys() and elem.tag('changing_table') == 'yes':
+                toilets.changing_table = True
+
+            if 'fee' in elem.tags().keys() and elem.tag('fee') == 'yes':
+                toilets.fee = True
+
+            if 'opening_hours' in elem.tags().keys() :
+                toilets.opening_hours = elem.tag('opening_hours')
+
+            toilets_list.append(toilets)
+
+        return toilets_list