diff --git a/models/notification.go b/models/notification.go
index 1f51c99c4c..362b490994 100644
--- a/models/notification.go
+++ b/models/notification.go
@@ -10,7 +10,6 @@ import (
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
-	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
 	"xorm.io/builder"
@@ -332,56 +331,6 @@ func countUnread(e Engine, userID int64) int64 {
 	return exist
 }
 
-// APIFormat converts a Notification to api.NotificationThread
-func (n *Notification) APIFormat() *api.NotificationThread {
-	result := &api.NotificationThread{
-		ID:        n.ID,
-		Unread:    !(n.Status == NotificationStatusRead || n.Status == NotificationStatusPinned),
-		Pinned:    n.Status == NotificationStatusPinned,
-		UpdatedAt: n.UpdatedUnix.AsTime(),
-		URL:       n.APIURL(),
-	}
-
-	//since user only get notifications when he has access to use minimal access mode
-	if n.Repository != nil {
-		result.Repository = n.Repository.APIFormat(AccessModeRead)
-	}
-
-	//handle Subject
-	switch n.Source {
-	case NotificationSourceIssue:
-		result.Subject = &api.NotificationSubject{Type: "Issue"}
-		if n.Issue != nil {
-			result.Subject.Title = n.Issue.Title
-			result.Subject.URL = n.Issue.APIURL()
-			result.Subject.State = n.Issue.State()
-			comment, err := n.Issue.GetLastComment()
-			if err == nil && comment != nil {
-				result.Subject.LatestCommentURL = comment.APIURL()
-			}
-		}
-	case NotificationSourcePullRequest:
-		result.Subject = &api.NotificationSubject{Type: "Pull"}
-		if n.Issue != nil {
-			result.Subject.Title = n.Issue.Title
-			result.Subject.URL = n.Issue.APIURL()
-			result.Subject.State = n.Issue.State()
-			comment, err := n.Issue.GetLastComment()
-			if err == nil && comment != nil {
-				result.Subject.LatestCommentURL = comment.APIURL()
-			}
-		}
-	case NotificationSourceCommit:
-		result.Subject = &api.NotificationSubject{
-			Type:  "Commit",
-			Title: n.CommitID,
-		}
-		//unused until now
-	}
-
-	return result
-}
-
 // LoadAttributes load Repo Issue User and Comment if not loaded
 func (n *Notification) LoadAttributes() (err error) {
 	return n.loadAttributes(x)
@@ -470,15 +419,6 @@ func (n *Notification) APIURL() string {
 // NotificationList contains a list of notifications
 type NotificationList []*Notification
 
-// APIFormat converts a NotificationList to api.NotificationThread list
-func (nl NotificationList) APIFormat() []*api.NotificationThread {
-	var result = make([]*api.NotificationThread, 0, len(nl))
-	for _, n := range nl {
-		result = append(result, n.APIFormat())
-	}
-	return result
-}
-
 // LoadAttributes load Repo Issue User and Comment if not loaded
 func (nl NotificationList) LoadAttributes() (err error) {
 	for i := 0; i < len(nl); i++ {
diff --git a/modules/convert/notification.go b/modules/convert/notification.go
new file mode 100644
index 0000000000..6934871844
--- /dev/null
+++ b/modules/convert/notification.go
@@ -0,0 +1,69 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package convert
+
+import (
+	"code.gitea.io/gitea/models"
+	api "code.gitea.io/gitea/modules/structs"
+)
+
+// ToNotificationThread convert a Notification to api.NotificationThread
+func ToNotificationThread(n *models.Notification) *api.NotificationThread {
+	result := &api.NotificationThread{
+		ID:        n.ID,
+		Unread:    !(n.Status == models.NotificationStatusRead || n.Status == models.NotificationStatusPinned),
+		Pinned:    n.Status == models.NotificationStatusPinned,
+		UpdatedAt: n.UpdatedUnix.AsTime(),
+		URL:       n.APIURL(),
+	}
+
+	//since user only get notifications when he has access to use minimal access mode
+	if n.Repository != nil {
+		result.Repository = n.Repository.APIFormat(models.AccessModeRead)
+	}
+
+	//handle Subject
+	switch n.Source {
+	case models.NotificationSourceIssue:
+		result.Subject = &api.NotificationSubject{Type: "Issue"}
+		if n.Issue != nil {
+			result.Subject.Title = n.Issue.Title
+			result.Subject.URL = n.Issue.APIURL()
+			result.Subject.State = n.Issue.State()
+			comment, err := n.Issue.GetLastComment()
+			if err == nil && comment != nil {
+				result.Subject.LatestCommentURL = comment.APIURL()
+			}
+		}
+	case models.NotificationSourcePullRequest:
+		result.Subject = &api.NotificationSubject{Type: "Pull"}
+		if n.Issue != nil {
+			result.Subject.Title = n.Issue.Title
+			result.Subject.URL = n.Issue.APIURL()
+			result.Subject.State = n.Issue.State()
+			comment, err := n.Issue.GetLastComment()
+			if err == nil && comment != nil {
+				result.Subject.LatestCommentURL = comment.APIURL()
+			}
+		}
+	case models.NotificationSourceCommit:
+		result.Subject = &api.NotificationSubject{
+			Type:  "Commit",
+			Title: n.CommitID,
+		}
+		//unused until now
+	}
+
+	return result
+}
+
+// ToNotifications convert list of Notification to api.NotificationThread list
+func ToNotifications(nl models.NotificationList) []*api.NotificationThread {
+	var result = make([]*api.NotificationThread, 0, len(nl))
+	for _, n := range nl {
+		result = append(result, ToNotificationThread(n))
+	}
+	return result
+}
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index cc66e0f743..0a75fcd30a 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/routers/api/v1/utils"
 )
@@ -127,7 +128,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, nl.APIFormat())
+	ctx.JSON(http.StatusOK, convert.ToNotifications(nl))
 }
 
 // ReadRepoNotifications mark notification threads as read on a specific repo
diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go
index 86ae2dca31..efe5bcb59c 100644
--- a/routers/api/v1/notify/threads.go
+++ b/routers/api/v1/notify/threads.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/convert"
 )
 
 // GetThread get notification by ID
@@ -44,7 +45,7 @@ func GetThread(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, n.APIFormat())
+	ctx.JSON(http.StatusOK, convert.ToNotificationThread(n))
 }
 
 // ReadThread mark notification as read by ID
diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go
index 373c88d372..e739c6a38d 100644
--- a/routers/api/v1/notify/user.go
+++ b/routers/api/v1/notify/user.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/routers/api/v1/utils"
 )
 
@@ -87,7 +88,7 @@ func ListNotifications(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, nl.APIFormat())
+	ctx.JSON(http.StatusOK, convert.ToNotifications(nl))
 }
 
 // ReadNotifications mark notification threads as read, unread, or pinned