diff --git a/backend/src/monarch_py/datamodels/solr.py b/backend/src/monarch_py/datamodels/solr.py
index 541aaed3..841d3f98 100644
--- a/backend/src/monarch_py/datamodels/solr.py
+++ b/backend/src/monarch_py/datamodels/solr.py
@@ -45,6 +45,10 @@ class SolrQuery(BaseModel):
     facet_queries: Optional[List[str]] = Field(default_factory=list)
     filter_queries: Optional[List[str]] = Field(default_factory=list)
     facet_mincount: int = 1
+    group: bool = False
+    group_queries: Optional[List[str]] = Field(default_factory=list)
+    group_limit: int = 5
+    group_offset: int = 0
     query_fields: Optional[str] = None
     def_type: str = "edismax"
     q_op: str = "AND"  # See SOLR-8812, need this plus mm=100% to allow boolean operators in queries
@@ -88,6 +92,12 @@ def _solrize(self, value):
             return "fq"
         elif value == "facet_mincount":
             return "facet.mincount"
+        elif value == "group_queries":
+            return "group.query"
+        elif value == "group_limit":
+            return "group.limit"
+        elif value == "group_offset":
+            return "group.offset"
         elif value == "query_fields":
             return "qf"
         elif value == "def_type":
@@ -118,7 +128,18 @@ class SolrFacetCounts(BaseModel):
     facet_queries: Optional[Dict]
 
 
+class SolrQueryGroup(BaseModel):
+    matches: int
+    doclist: SolrQueryResponse
+
+
 class SolrQueryResult(BaseModel):
     responseHeader: SolrQueryResponseHeader
     response: SolrQueryResponse
     facet_counts: Optional[SolrFacetCounts]
+
+
+class SolrGroupedQueryResult(BaseModel):
+    responseHeader: SolrQueryResponseHeader
+    grouped: Dict[str, SolrQueryGroup]
+    facet_counts: Optional[SolrFacetCounts]
diff --git a/backend/src/monarch_py/service/solr_service.py b/backend/src/monarch_py/service/solr_service.py
index 8793c46b..6e969fe2 100644
--- a/backend/src/monarch_py/service/solr_service.py
+++ b/backend/src/monarch_py/service/solr_service.py
@@ -3,7 +3,7 @@
 
 import requests
 from loguru import logger
-from monarch_py.datamodels.solr import SolrQuery, SolrQueryResult, core
+from monarch_py.datamodels.solr import SolrGroupedQueryResult, SolrQuery, SolrQueryResult, core
 from monarch_py.utils.utils import escape
 from pydantic import BaseModel
 
@@ -23,6 +23,28 @@ def get(self, id):
             return None
         return entity
 
+    def group_query(self, q: SolrQuery) -> SolrGroupedQueryResult:
+        url = f"{self.base_url}/{self.core.value}/select?{q.query_string()}"
+        response = requests.get(url)
+        logger.debug(f"SolrService.query: {url}")
+        data = json.loads(response.text)
+        if "error" in data:
+            logger.error("Solr error message: " + data["error"]["msg"])
+        response.raise_for_status()
+        solr_query_result = SolrGroupedQueryResult.model_validate(data, from_attributes=True)
+        for group in solr_query_result.grouped.values():
+            for doc in group.doclist.docs:
+                self._strip_json(
+                    doc,
+                    "_version_",
+                    "iri",
+                    "frequency_computed_sortable_float",
+                    "has_quotient_sortable_float",
+                    "has_percentage_sortable_float",
+                )
+
+        return solr_query_result
+
     def query(self, q: SolrQuery) -> SolrQueryResult:
         url = f"{self.base_url}/{self.core.value}/select?{q.query_string()}"
         response = requests.get(url)
diff --git a/backend/tests/fixtures/association_counts_query.py b/backend/tests/fixtures/association_counts_query.py
index 848ec8ba..912beae9 100644
--- a/backend/tests/fixtures/association_counts_query.py
+++ b/backend/tests/fixtures/association_counts_query.py
@@ -7,6 +7,10 @@ def association_counts_query():
         "q": "*:*",
         "rows": 20,
         "start": 0,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/association_query_direct.py b/backend/tests/fixtures/association_query_direct.py
index b8c2c2ed..d0ff7545 100644
--- a/backend/tests/fixtures/association_query_direct.py
+++ b/backend/tests/fixtures/association_query_direct.py
@@ -7,6 +7,10 @@ def association_query_direct():
         "q": "test:q",
         "rows": 100,
         "start": 100,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/association_query_indirect.py b/backend/tests/fixtures/association_query_indirect.py
index 5e8503e9..8e6cef2f 100644
--- a/backend/tests/fixtures/association_query_indirect.py
+++ b/backend/tests/fixtures/association_query_indirect.py
@@ -7,6 +7,10 @@ def association_query_indirect():
         "q": "test:q",
         "rows": 100,
         "start": 100,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/autocomplete_query.py b/backend/tests/fixtures/autocomplete_query.py
index 733a5be2..4b45f4b6 100644
--- a/backend/tests/fixtures/autocomplete_query.py
+++ b/backend/tests/fixtures/autocomplete_query.py
@@ -7,6 +7,10 @@ def autocomplete_query():
         "q": "fanc",
         "rows": 10,
         "start": 0,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/histopheno_query.py b/backend/tests/fixtures/histopheno_query.py
index e2d3dfb4..33eeb7f0 100644
--- a/backend/tests/fixtures/histopheno_query.py
+++ b/backend/tests/fixtures/histopheno_query.py
@@ -7,6 +7,10 @@ def histopheno_query():
         "q": "*:*",
         "rows": 0,
         "start": 0,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/mapping_query.py b/backend/tests/fixtures/mapping_query.py
index b24fa2ac..b35abb9b 100644
--- a/backend/tests/fixtures/mapping_query.py
+++ b/backend/tests/fixtures/mapping_query.py
@@ -7,6 +7,10 @@ def mapping_query():
         "q": "*:*",
         "rows": 20,
         "start": 0,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],
diff --git a/backend/tests/fixtures/search_query.py b/backend/tests/fixtures/search_query.py
index 159fcaf1..4a4c2733 100644
--- a/backend/tests/fixtures/search_query.py
+++ b/backend/tests/fixtures/search_query.py
@@ -7,6 +7,10 @@ def search_query():
         "q": "fanconi",
         "rows": 20,
         "start": 0,
+        "group": False,
+        "group_limit": 5,
+        "group_offset": 0,
+        "group_queries": [],
         "facet": True,
         "facet_min_count": 1,
         "facet_fields": [],