diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 90ba816b65..572abcdb31 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -964,6 +964,73 @@ func Routes() *web.Router {
 			}, context.UserAssignmentAPI(), individualPermsChecker)
 		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))
 
+		// m.Group("/projects", func() {
+		// 	m.Group("", func() {
+		// 		m.Get("", org.Projects)
+		// 		m.Get("/{id}", org.ViewProject)
+		// 	}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
+		// 	m.Group("", func() { //nolint:dupl
+		// 		m.Get("/new", org.RenderNewProject)
+		// 		m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
+		// 		m.Group("/{id}", func() {
+		// 			m.Post("", web.Bind(forms.EditProjectColumnForm{}), org.AddColumnToProjectPost)
+		// 			m.Post("/move", project.MoveColumns)
+		// 			m.Post("/delete", org.DeleteProject)
+
+		// 			m.Get("/edit", org.RenderEditProject)
+		// 			m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost)
+		// 			m.Post("/{action:open|close}", org.ChangeProjectStatus)
+
+		// 			m.Group("/{columnID}", func() {
+		// 				m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn)
+		// 				m.Delete("", org.DeleteProjectColumn)
+		// 				m.Post("/default", org.SetDefaultProjectColumn)
+		// 				m.Post("/move", org.MoveIssues)
+		// 			})
+		// 		})
+		// 	}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) {
+		// 		if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
+		// 			ctx.NotFound("NewProject", nil)
+		// 			return
+		// 		}
+		// 	})
+		// }, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
+		// Users (requires user scope)
+		m.Group("/{username}/-", func() {
+			m.Group("/projects", func() {
+				m.Group("", func() {
+					m.Get("", org.GetProjects)
+					// m.Get("/{id}", org.ViewProject)
+				}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
+				m.Group("", func() {
+					m.Post("", bind(api.CreateProjectOption{}), org.CreateProject)
+					m.Group("/{id}", func() {
+						m.Post("/{action:open|close}", org.ChangeProjectStatus)
+					})
+				}, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.APIContext) {
+					if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
+						ctx.NotFound("NewProject", nil)
+						return
+					}
+				})
+			}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
+		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken(), context.UserAssignmentAPI())
+
+		m.Group("/{org}/-", func() {
+			m.Group("/projects", func() {
+				m.Group("", func() {
+					// m.Get("", org.Projects)
+					// m.Get("/{id}", org.ViewProject)
+				}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
+				m.Group("", func() {
+					m.Post("", bind(api.CreateProjectOption{}), org.CreateProject)
+					m.Group("/{id}", func() {
+						m.Post("/{action:open|close}", org.ChangeProjectStatus)
+					})
+				}, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true))
+			}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
+		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), orgAssignment(true))
+
 		// Users (requires user scope)
 		m.Group("/users", func() {
 			m.Group("/{username}", func() {
@@ -979,19 +1046,7 @@ func Routes() *web.Router {
 				m.Get("/starred", user.GetStarredRepos)
 
 				m.Get("/subscriptions", user.GetWatchedRepos)
-
-				m.Group("/projects", func() {
-					m.Post("", bind(api.CreateProjectOption{}), org.CreateProject)
-					m.Group("/{id}", func() {
-						m.Post("/{action:open|close}", org.ChangeProjectStatus)
-					})
-				})
-			}, context.UserAssignmentAPI(), reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.APIContext) {
-				if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
-					ctx.NotFound("NewProject", nil)
-					return
-				}
-			})
+			}, context.UserAssignmentAPI())
 		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
 
 		// Users (requires user scope)
diff --git a/routers/api/v1/org/project.go b/routers/api/v1/org/project.go
index 9415727b28..1b0f96c7f3 100644
--- a/routers/api/v1/org/project.go
+++ b/routers/api/v1/org/project.go
@@ -3,8 +3,12 @@ package org
 import (
 	"log"
 	"net/http"
+	"strings"
 
+	"code.gitea.io/gitea/models/db"
 	project_model "code.gitea.io/gitea/models/project"
+	"code.gitea.io/gitea/modules/optional"
+	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/context"
@@ -59,3 +63,41 @@ func ChangeProjectStatus(ctx *context.APIContext) {
 	}
 	ctx.JSON(http.StatusOK, map[string]any{"message": "project status updated successfully"})
 }
+
+// Projects renders the home page of projects
+func GetProjects(ctx *context.APIContext) {
+	ctx.Data["Title"] = ctx.Tr("repo.projects")
+
+	sortType := ctx.FormTrim("sort")
+
+	isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed"
+	keyword := ctx.FormTrim("q")
+	page := ctx.FormInt("page")
+	if page <= 1 {
+		page = 1
+	}
+
+	var projectType project_model.Type
+	if ctx.ContextUser.IsOrganization() {
+		projectType = project_model.TypeOrganization
+	} else {
+		projectType = project_model.TypeIndividual
+	}
+	projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
+		ListOptions: db.ListOptions{
+			Page:     page,
+			PageSize: setting.UI.IssuePagingNum,
+		},
+		OwnerID:  ctx.ContextUser.ID,
+		IsClosed: optional.Some(isShowClosed),
+		OrderBy:  project_model.GetSearchOrderByBySortType(sortType),
+		Type:     projectType,
+		Title:    keyword,
+	})
+	if err != nil {
+		ctx.ServerError("FindProjects", err)
+		return
+	}
+
+	ctx.JSON(http.StatusOK, projects)
+}