diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..7cf08736edefd1c9514ad63c49b716b500b2d827
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApi.java
@@ -0,0 +1,47 @@
+package org.etsi.osl.tmf.metrics.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.time.OffsetDateTime;
+import java.util.Map;
+
+@Tag(name = "ResourceMetricsApi")
+public interface ResourceMetricsApi {
+
+    Logger log = LoggerFactory.getLogger(ResourceMetricsApi.class);
+
+    @Operation(summary = "Get total number of resources", operationId = "getTotalResources")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Success"),
+            @ApiResponse(responseCode = "400", description = "Bad Request"),
+            @ApiResponse(responseCode = "500", description = "Internal Server Error")
+    })
+    @RequestMapping(value = "/tmf-api/metrics/totalResources", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
+    ResponseEntity<Map<String, Integer>> getTotalResources(
+            @Valid @RequestParam(value = "state", required = false) ResourceStatusType state
+    );
+
+    @Operation(summary = "Get resources grouped by state", operationId = "getResourcesGroupedByState")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Success"),
+            @ApiResponse(responseCode = "400", description = "Bad Request"),
+            @ApiResponse(responseCode = "500", description = "Internal Server Error")
+    })
+    @RequestMapping(value = "/tmf-api/metrics/resourcesGroupByState", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
+    ResponseEntity<Map<String, Object>> getResourcesGroupedByState(
+            @Valid @RequestParam(value = "starttime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime starttime,
+            @Valid @RequestParam(value = "endtime", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime endtime
+    );
+}
diff --git a/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java
new file mode 100644
index 0000000000000000000000000000000000000000..14be245ca433a44cb0593e826accf4e54be9c289
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/metrics/api/ResourceMetricsApiController.java
@@ -0,0 +1,85 @@
+package org.etsi.osl.tmf.metrics.api;
+
+import org.etsi.osl.tmf.metrics.reposervices.ResourceMetricsRepoService;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+
+import java.time.OffsetDateTime;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@Controller
+public class ResourceMetricsApiController implements ResourceMetricsApi{
+
+    private static final Logger log = LoggerFactory.getLogger(ResourceMetricsApiController.class);
+    private final ResourceMetricsRepoService resourceMetricsRepoService;
+
+    @Autowired
+    public ResourceMetricsApiController(ResourceMetricsRepoService resourceMetricsRepoService) {
+        this.resourceMetricsRepoService = resourceMetricsRepoService;
+    }
+
+    @Override
+    public ResponseEntity<Map<String, Integer>> getTotalResources(ResourceStatusType state) {
+        try {
+            int totalResources = resourceMetricsRepoService.countTotalResources(state);
+            Map<String, Integer> response = new HashMap<>();
+            response.put("totalResources", totalResources);
+            return new ResponseEntity<>(response, HttpStatus.OK);
+        } catch (Exception e) {
+            log.error("Couldn't retrieve total resources. ", e);
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @Override
+    public ResponseEntity<Map<String, Object>> getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) {
+        try {
+            Map<String, Integer> resourcesByState = resourceMetricsRepoService.getResourcesGroupedByState(starttime, endtime);
+
+            // Initialize with all possible states and 0. Ensures that all states are represented, even if not present in the data.
+            Map<String, Integer> fullStateMap = new LinkedHashMap<>();
+            for (ResourceStatusType state : ResourceStatusType.values()) {
+                fullStateMap.put(state.name(), 0); // default to 0
+            }
+
+            // Overwrite counts with actual data
+            resourcesByState.forEach((key, value) -> {
+                fullStateMap.put(key.toUpperCase(), value); // normalize case just in case
+            });
+
+            // Build groupByState list
+            List<Map<String, Object>> groupByStateList = fullStateMap.entrySet().stream()
+                    .map(entry -> {
+                        Map<String, Object> map = new HashMap<>();
+                        map.put("key", entry.getKey());
+                        map.put("count", entry.getValue());
+                        return map;
+                    })
+                    .toList();
+
+
+            // Wrap in response structure
+            Map<String, Object> aggregations = Map.of("groupByState", groupByStateList);
+            Map<String, Object> services = Map.of(
+                    "total", fullStateMap.values().stream().mapToInt(Integer::intValue).sum(),
+                    "aggregations", aggregations
+            );
+
+            Map<String, Object> response = Map.of("resources", services);
+            return new ResponseEntity<>(response, HttpStatus.OK);
+
+        } catch (Exception e) {
+            log.error("Couldn't retrieve resources grouped by state. ", e);
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+}
diff --git a/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..70feca732a32c6d6bdac9890570ec24e79a53844
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/metrics/reposervices/ResourceMetricsRepoService.java
@@ -0,0 +1,46 @@
+package org.etsi.osl.tmf.metrics.reposervices;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
+import org.etsi.osl.tmf.ri639.repo.ResourceRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+@Service
+public class ResourceMetricsRepoService {
+
+    @Autowired
+    ObjectMapper objectMapper;
+
+    @Autowired
+    ResourceRepository resourceRepository;
+
+    public int countTotalResources(ResourceStatusType state) {
+        if (state == null) {
+            return resourceRepository.countAll();
+        } else {
+            return resourceRepository.countByResourceStatus(state);
+        }
+    }
+
+    public Map<String, Integer> getResourcesGroupedByState(OffsetDateTime starttime, OffsetDateTime endtime) {
+        if (starttime.plusDays(31).isBefore(endtime)) {
+            starttime = endtime.minusDays(31);
+        }
+
+        List<Object[]> rawResults = resourceRepository.groupByStateBetweenDates(starttime, endtime);
+
+        return rawResults.stream()
+                .collect(Collectors.toMap(
+                        row -> row[0].toString(),
+                        row -> ((Number) row[1]).intValue()
+                ));
+    }
+
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java b/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java
index 446a04ca1c4193c892f0f27ece6d20126ac148cf..c324cad99e2a734d147fc881981f769e9c726244 100644
--- a/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java
+++ b/src/main/java/org/etsi/osl/tmf/ri639/repo/ResourceRepository.java
@@ -19,9 +19,11 @@
  */
 package org.etsi.osl.tmf.ri639.repo;
 
+import java.time.OffsetDateTime;
 import java.util.List;
 import java.util.Optional;
 import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
@@ -56,4 +58,18 @@ public interface ResourceRepository extends  JpaRepository<Resource, Long> {
 
 	List<Resource> findByNameAndResourceVersion(String aname, String aversion);
 	List<Resource> findByNameAndCategoryAndResourceVersion(String aname, String acategory, String aversion);
+
+
+	// Methods for metrics
+
+	@Query("SELECT COUNT(res) FROM RIResource res")
+	int countAll();
+
+	int countByResourceStatus(ResourceStatusType status);
+
+	@Query("SELECT res.resourceStatus, COUNT(res) FROM RIResource res "
+			+ "WHERE res.startOperatingDate >= :starttime AND res.endOperatingDate <= :endtime "
+			+ "GROUP BY res.resourceStatus")
+	List<Object[]> groupByStateBetweenDates(OffsetDateTime starttime, OffsetDateTime endtime);
+
 }
diff --git a/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..406580fe19dfabdcc7fb506e97a1099893b6d86f
--- /dev/null
+++ b/src/test/java/org/etsi/osl/services/api/metrics/ResourceMetricsApiControllerTest.java
@@ -0,0 +1,240 @@
+package org.etsi.osl.services.api.metrics;
+
+import com.jayway.jsonpath.JsonPath;
+import org.apache.commons.io.IOUtils;
+import org.etsi.osl.tmf.JsonUtils;
+import org.etsi.osl.tmf.OpenAPISpringBoot;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.common.model.UserPartRoleType;
+import org.etsi.osl.tmf.common.model.service.Note;
+import org.etsi.osl.tmf.common.model.service.ResourceRef;
+import org.etsi.osl.tmf.prm669.model.RelatedParty;
+import org.etsi.osl.tmf.rcm634.model.*;
+import org.etsi.osl.tmf.ri639.model.*;
+import org.etsi.osl.tmf.ri639.reposervices.ResourceRepoService;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@Transactional
+@SpringBootTest(
+        webEnvironment = SpringBootTest.WebEnvironment.MOCK,
+        classes = OpenAPISpringBoot.class
+)
+//@AutoConfigureTestDatabase //this automatically uses h2
+@AutoConfigureMockMvc
+@ActiveProfiles("testing")
+//@TestPropertySource(
+//		  locations = "classpath:application-testing.yml")
+public class ResourceMetricsApiControllerTest {
+
+    @Autowired
+    private MockMvc mvc;
+
+    @Autowired
+    ResourceRepoService resourceRepoService;
+
+    @Autowired
+    private WebApplicationContext context;
+
+    @Before
+    public void setup() throws Exception {
+        mvc = MockMvcBuilders
+                .webAppContextSetup(context)
+                .apply(springSecurity())
+                .build();
+    }
+
+    @WithMockUser(username="osadmin", roles = {"ADMIN","USER"})
+    @Test
+    public void testCountTotalResources() throws Exception {
+        createResource(ResourceStatusType.AVAILABLE);
+
+        String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" )
+                        .contentType(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk() )
+                .andReturn().getResponse().getContentAsString();
+
+        int totalResources = JsonPath.read(response, "$.totalResources");
+
+
+        assertThat(totalResources).isEqualTo(resourceRepoService.findAll().size());
+    }
+
+    @WithMockUser(username="osadmin", roles = {"ADMIN","USER"})
+    @Test
+    public void testCountTotalServicesWithState() throws Exception {
+        createResource(ResourceStatusType.AVAILABLE);
+        createResource(ResourceStatusType.STANDBY);
+
+        String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/totalResources" )
+                        .param("state", "STANDBY")
+                        .contentType(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk() )
+                .andReturn().getResponse().getContentAsString();
+
+        int totalResources = JsonPath.read(response, "$.totalResources");
+
+
+        List<Resource> resourcesList = resourceRepoService.findAll();
+        int standByResources = (int) resourcesList.stream().filter(resource -> resource.getResourceStatus() == ResourceStatusType.STANDBY).count();
+
+        assertThat(totalResources).isEqualTo(standByResources);
+        assertThat(totalResources).isEqualTo(1);
+    }
+
+    @WithMockUser(username = "osadmin", roles = {"ADMIN", "USER"})
+    @Test
+    public void testGetServicesGroupedByState() throws Exception {
+        String startTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT);
+
+        createResource(ResourceStatusType.AVAILABLE);
+        createResource(ResourceStatusType.AVAILABLE);
+        createResource(ResourceStatusType.AVAILABLE);
+        createResource(ResourceStatusType.RESERVED);
+        createResource(ResourceStatusType.RESERVED);
+        createResource(ResourceStatusType.STANDBY);
+
+        String endTime = OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT);
+
+        String response = mvc.perform(MockMvcRequestBuilders.get("/tmf-api/metrics/resourcesGroupByState")
+                        .param("starttime", startTime)
+                        .param("endtime", endTime)
+                        .contentType(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+
+        List<Map<String, Object>> groupByState = JsonPath.read(response, "$.resources.aggregations.groupByState");
+
+        // Create a map from key -> count
+        Map<String, Integer> stateCounts = groupByState.stream()
+                .collect(Collectors.toMap(
+                        entry -> (String) entry.get("key"),
+                        entry -> (Integer) entry.get("count")
+                ));
+
+        assertThat(stateCounts.get("AVAILABLE")).isEqualTo(3);
+        assertThat(stateCounts.get("RESERVED")).isEqualTo(2);
+        assertThat(stateCounts.get("STANDBY")).isEqualTo(1);
+        assertThat(stateCounts.get("ALARM")).isEqualTo(0);
+        assertThat(stateCounts.get("UNKNOWN")).isEqualTo(0);
+        assertThat(stateCounts.get("SUSPENDED")).isEqualTo(0);
+    }
+
+    private void createResource(ResourceStatusType statusType) throws Exception{
+
+        /**
+         * first add 2 specs
+         */
+
+        File sspec = new File( "src/test/resources/testResourceSpec.json" );
+        InputStream in = new FileInputStream( sspec );
+        String sspectext = IOUtils.toString(in, "UTF-8");
+
+
+        ResourceSpecificationCreate sspeccr1 = JsonUtils.toJsonObj( sspectext,  ResourceSpecificationCreate.class);
+        sspeccr1.setName("Spec1");
+        ResourceSpecification responsesSpec1 = createResourceSpec( sspeccr1);
+
+        //res 2 is an RFS
+        ResourceSpecificationCreate sspeccr2 = JsonUtils.toJsonObj( sspectext,  ResourceSpecificationCreate.class);
+        sspeccr2.setName("Spec2");
+
+        sspeccr2.addResourceSpecificationRelationshipWith( responsesSpec1 );
+        LogicalResourceSpecification responsesSpec2 = (LogicalResourceSpecification) createResourceSpec( sspeccr2 );
+        /**
+         * add them as bundle
+         */
+
+        ResourceSpecificationCreate sspeccr3 = JsonUtils.toJsonObj( sspectext,  ResourceSpecificationCreate.class);
+        sspeccr3.setName("BundleExampleSpec");
+        sspeccr3.isBundle(true);
+        sspeccr3.addResourceSpecificationRelationshipWith( responsesSpec1 );
+        sspeccr3.addResourceSpecificationRelationshipWith( responsesSpec2 );
+        ResourceSpecification responsesSpec3 = createResourceSpec( sspeccr3);
+
+        ResourceCreate aResource = new ResourceCreate();
+        aResource.setName("aNew Resource parent");
+        aResource.setCategory("Experimentation");
+        aResource.setDescription("Experimentation Descr");
+        aResource.setStartOperatingDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() );
+        aResource.setEndOperatingDate( OffsetDateTime.now(ZoneOffset.UTC ).toString() );
+        aResource.setResourceStatus(statusType);
+
+
+
+        Note noteItem = new Note();
+        noteItem.text("test note");
+        aResource.addNoteItem(noteItem);
+
+        Characteristic resCharacteristicItem = new Characteristic();
+
+        resCharacteristicItem.setName( "externalPartnerServiceId" );
+        resCharacteristicItem.setValue( new Any("NONE"));
+        aResource.addResourceCharacteristicItem(resCharacteristicItem);
+
+        ResourceSpecificationRef aServiceSpecificationRef = new ResourceSpecificationRef();
+        aServiceSpecificationRef.setId(responsesSpec3.getId() );
+        aServiceSpecificationRef.setName(responsesSpec3.getName());
+
+        aResource.setResourceSpecification( aServiceSpecificationRef );
+
+        resourceRepoService.addResource(aResource);
+    }
+
+
+    private ResourceSpecification createResourceSpec(ResourceSpecificationUpdate sspeccr1) throws Exception{
+
+        URI url = new URI("/resourceCatalogManagement/v4/resourceSpecification");
+        if (sspeccr1 instanceof PhysicalResourceSpecificationUpdate ) {
+            url = new URI("/resourceCatalogManagement/v4/resourceSpecification");
+        }
+        String responseSpec = mvc.perform(MockMvcRequestBuilders.post( url  )
+                        .with( SecurityMockMvcRequestPostProcessors.csrf())
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content( JsonUtils.toJson( sspeccr1 ) ))
+                .andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+
+        ResourceSpecification responsesSpec1;
+        if (sspeccr1 instanceof PhysicalResourceSpecificationUpdate ) {
+            responsesSpec1 = JsonUtils.toJsonObj(responseSpec,  PhysicalResourceSpecification.class);
+        }else {
+            responsesSpec1 = JsonUtils.toJsonObj(responseSpec,  LogicalResourceSpecification.class);
+        }
+
+        return responsesSpec1;
+    }
+}