diff --git a/requests.http b/requests.http
index 50ce01e6d355d28e4c09c1c33cab6092a487ed5a..7e78e46efff37a1bdc7147e29b6198566e22b12d 100644
--- a/requests.http
+++ b/requests.http
@@ -1,6 +1,6 @@
 ### Get all plants
-GET http://localhost:8080/api/v1/plants
-Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsdWthcy5rYXJzY2hAZ214LmRlIiwiaWF0IjoxNzAwOTMzNjQ4LCJleHAiOjE3MDEwMjAwNDh9.IXoMuNecB7ARvZpEyx5SraMbdoZrHgADeHd7wAo0ddw
+GET http://localhost:8080/api/v1/plants?page=0
+Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsdWthcy5rYXJzY2hAZ214LmRlIiwiaWF0IjoxNzAxMzcyNTIxLCJleHAiOjE3MDE0NTg5MjF9.8_rTh-5s4A6D1t_bdRdQFbM5RdNl2fpCLETpTQzInuc
 
 ### Get garden entries
 GET http://localhost:8080/api/v1/garden
diff --git a/src/main/java/hdm/mi/growbros/config/Seeding.java b/src/main/java/hdm/mi/growbros/config/Seeding.java
index f2ba4c5de0039925adcc4760e9247b2397589173..05bcb89164a407f555da64e1a3768201f46aa9f4 100644
--- a/src/main/java/hdm/mi/growbros/config/Seeding.java
+++ b/src/main/java/hdm/mi/growbros/config/Seeding.java
@@ -25,6 +25,7 @@ public class Seeding {
         return (args) -> {
             if (plantRepository.count() > 0) {
                 log.info("Plants table is not empty, seeding will be skipped.");
+                log.info("If you wish to seed the database, manually clear the plants table and re-run the application.");
                 return;
             }
 
diff --git a/src/main/java/hdm/mi/growbros/controllers/PlantsController.java b/src/main/java/hdm/mi/growbros/controllers/PlantsController.java
index 2011d2a09e5fce0e5073818f6261a9b81561eb46..ad8f2b4119556b4a5faf7fe43dfec2a3a5cd3e1a 100644
--- a/src/main/java/hdm/mi/growbros/controllers/PlantsController.java
+++ b/src/main/java/hdm/mi/growbros/controllers/PlantsController.java
@@ -1,30 +1,33 @@
 package hdm.mi.growbros.controllers;
 
+import hdm.mi.growbros.models.dto.CustomPageDto;
 import hdm.mi.growbros.models.plant.Plant;
 import hdm.mi.growbros.service.PlantsService;
 import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Collection;
 
 @RestController
 @CrossOrigin
+@RequestMapping("/api/v1")
+@RequiredArgsConstructor
 public class PlantsController {
     private final PlantsService plantsService;
 
-    public PlantsController(PlantsService plantsService) {
-        this.plantsService = plantsService;
+    @GetMapping("/plants")
+    public CustomPageDto<Plant> getAllPlants(
+            @RequestParam(value = "page", defaultValue = "0") int page,
+            @RequestParam(value = "pageSize", defaultValue = "25") int pageSize
+    ) {
+        return plantsService.getPlants(page, pageSize);
     }
 
-    @GetMapping("/api/v1/plants")
-    public Collection<Plant> getAllPlants() {
-        return plantsService.getPlants();
-    }
-
-    @GetMapping("api/v1/randomPlants/{numberOfPlants}")
+    @GetMapping("/randomPlants/{numberOfPlants}")
     public Collection<Plant> getRandomPlants(@PathVariable Integer numberOfPlants) {return plantsService.getRandomPlants(numberOfPlants);}
 
-    @PostMapping("/api/v1/plants")
+    @PostMapping("/plants")
     public void createPlant(@RequestBody @Valid Plant plant) {
         plantsService.createPlant(plant);
     }
diff --git a/src/main/java/hdm/mi/growbros/models/dto/CustomPageDto.java b/src/main/java/hdm/mi/growbros/models/dto/CustomPageDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..943c0d302ed63bc20978bbc0029d30ac806ee37e
--- /dev/null
+++ b/src/main/java/hdm/mi/growbros/models/dto/CustomPageDto.java
@@ -0,0 +1,6 @@
+package hdm.mi.growbros.models.dto;
+
+import java.util.Collection;
+
+public record CustomPageDto<T>(int currentPage, int pageSize, Collection<T> content) {
+}
diff --git a/src/main/java/hdm/mi/growbros/service/PlantsService.java b/src/main/java/hdm/mi/growbros/service/PlantsService.java
index 2d83c2c5538bd1437f96bcbc363f910a9f174671..a6edc3db3333e6f21d0d40042cfa919c856f6b47 100644
--- a/src/main/java/hdm/mi/growbros/service/PlantsService.java
+++ b/src/main/java/hdm/mi/growbros/service/PlantsService.java
@@ -1,10 +1,13 @@
 package hdm.mi.growbros.service;
 
+import hdm.mi.growbros.models.dto.CustomPageDto;
 import hdm.mi.growbros.models.plant.Plant;
 import hdm.mi.growbros.repositories.PlantRepository;
 import lombok.RequiredArgsConstructor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
@@ -16,11 +19,17 @@ import java.util.Random;
 @RequiredArgsConstructor
 public class PlantsService {
     private final static Logger log = LoggerFactory.getLogger(PlantsService.class);
+    private final static int MAX_PAGE_SIZE = 100;
 
     private final PlantRepository plantRepository;
 
-    public Collection<Plant> getPlants() {
-        return plantRepository.findAll();
+    public CustomPageDto<Plant> getPlants(int pageNumber, int pageSize) {
+        pageNumber = Math.max(0, pageNumber);
+        pageSize = Math.min(MAX_PAGE_SIZE, pageSize);
+
+        final Pageable page = PageRequest.of(pageNumber, pageSize);
+        List<Plant> pageContent = plantRepository.findAll(page).getContent();
+        return new CustomPageDto<>(pageNumber, pageContent.size(), pageContent);
     }
 
     public Collection<Plant> getRandomPlants(int numberOfPlants) {
diff --git a/src/test/java/hdm/mi/growbros/service/PlantsServiceTest.java b/src/test/java/hdm/mi/growbros/service/PlantsServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..65384ad273a14d0d9b73f5dc4f31d0082013fdc1
--- /dev/null
+++ b/src/test/java/hdm/mi/growbros/service/PlantsServiceTest.java
@@ -0,0 +1,113 @@
+package hdm.mi.growbros.service;
+
+import hdm.mi.growbros.models.dto.CustomPageDto;
+import hdm.mi.growbros.models.plant.Plant;
+import hdm.mi.growbros.repositories.PlantRepository;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.stream.IntStream;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SpringBootTest
+@ActiveProfiles("test")
+class PlantsServiceTest {
+    @MockBean
+    private PlantRepository plantRepository;
+
+    @Autowired
+    private PlantsService plantsService;
+
+    @Test
+    void verify_repositoryCalled_withCorrectPageable() {
+        final PageRequest correctPageable = PageRequest.of(0, 10);
+        PageImpl<Plant> page = new PageImpl<>(List.of());
+        when(plantRepository.findAll(any(PageRequest.class)))
+                .thenReturn(page);
+
+        plantsService.getPlants(0, 10);
+
+        verify(plantRepository).findAll(correctPageable);
+    }
+
+    @Test
+    void getPlants_withPageEmpty() {
+        final PageRequest pageable = PageRequest.of(0, 10);
+        PageImpl<Plant> page = new PageImpl<>(List.of());
+        final CustomPageDto<Plant> expected = new CustomPageDto<>(0, 0, page.getContent());
+        when(plantRepository.findAll(pageable))
+                .thenReturn(page);
+
+        CustomPageDto<Plant> actual = plantsService.getPlants(0, 10);
+
+        assertAll(
+                () -> assertEquals(expected.currentPage(), actual.currentPage()),
+                () -> assertEquals(0, actual.pageSize()),
+                () -> assertEquals(expected.content(), actual.content())
+        );
+    }
+
+    @Test
+    void getPlants_correctPageSize() {
+        int plantAmount = 5;
+        var plants = getMockPlants(plantAmount);
+
+        final PageRequest pageable = PageRequest.of(0, 10);
+        PageImpl<Plant> page = new PageImpl<>(plants);
+        final CustomPageDto<Plant> expected = new CustomPageDto<>(0, plantAmount, page.getContent());
+        when(plantRepository.findAll(pageable))
+                .thenReturn(page);
+
+        CustomPageDto<Plant> actual = plantsService.getPlants(0, 10);
+
+        assertAll(
+                () -> assertEquals(expected.currentPage(), actual.currentPage()),
+                () -> assertEquals(plantAmount, actual.pageSize()),
+                () -> assertEquals(expected.content(), actual.content())
+        );
+    }
+
+    @Test
+    void getPlants_pageNumberNonNegative() {
+        int pageSize = 10;
+        final PageRequest correctPageRequest = PageRequest.of(0, pageSize);
+        when(plantRepository.findAll(any(PageRequest.class)))
+                .thenReturn(new PageImpl<>(List.of()));
+
+        plantsService.getPlants(-1, pageSize);
+
+        // calling the service with -1 as page number, and verifying that the repository is called
+        // with the correct page number
+        verify(plantRepository).findAll(correctPageRequest);
+    }
+
+    @Test
+    void pageSize_cappedAt_100() {
+        int pageNumber = 0;
+        final PageRequest correctPageRequest = PageRequest.of(pageNumber, 100);
+        when(plantRepository.findAll(any(PageRequest.class)))
+                .thenReturn(new PageImpl<>(List.of()));
+
+        plantsService.getPlants(pageNumber, 1000);
+
+        verify(plantRepository).findAll(correctPageRequest);
+    }
+
+    private List<Plant> getMockPlants(int amount) {
+        return IntStream
+                .range(0, amount)
+                .mapToObj(index -> Plant.builder().name(String.format("My generated plant %d", index)).build())
+                .toList();
+    }
+}