diff --git a/models/issue.go b/models/issue.go
index 03af32700d..8aa02873a1 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -1045,16 +1045,18 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
 // IssuesOptions represents options of an issue.
 type IssuesOptions struct {
 	ListOptions
-	RepoIDs     []int64 // include all repos if empty
-	AssigneeID  int64
-	PosterID    int64
-	MentionedID int64
-	MilestoneID int64
-	IsClosed    util.OptionalBool
-	IsPull      util.OptionalBool
-	LabelIDs    []int64
-	SortType    string
-	IssueIDs    []int64
+	RepoIDs            []int64 // include all repos if empty
+	AssigneeID         int64
+	PosterID           int64
+	MentionedID        int64
+	MilestoneID        int64
+	IsClosed           util.OptionalBool
+	IsPull             util.OptionalBool
+	LabelIDs           []int64
+	IncludedLabelNames []string
+	ExcludedLabelNames []string
+	SortType           string
+	IssueIDs           []int64
 	// prioritize issues from this repo
 	PriorityRepoID int64
 }
@@ -1153,6 +1155,14 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
 			}
 		}
 	}
+
+	if len(opts.IncludedLabelNames) > 0 {
+		sess.In("issue.id", BuildLabelNamesIssueIDsCondition(opts.IncludedLabelNames))
+	}
+
+	if len(opts.ExcludedLabelNames) > 0 {
+		sess.And(builder.NotIn("issue.id", BuildLabelNamesIssueIDsCondition(opts.ExcludedLabelNames)))
+	}
 }
 
 // CountIssuesByRepo map from repoID to number of issues matching the options
diff --git a/models/issue_label.go b/models/issue_label.go
index c111afb2ff..3b516a7aed 100644
--- a/models/issue_label.go
+++ b/models/issue_label.go
@@ -269,6 +269,17 @@ func GetLabelIDsInRepoByNames(repoID int64, labelNames []string) ([]int64, error
 		Find(&labelIDs)
 }
 
+// BuildLabelNamesIssueIDsCondition returns a builder where get issue ids match label names
+func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
+	return builder.Select("issue_label.issue_id").
+		From("issue_label").
+		InnerJoin("label", "label.id = issue_label.label_id").
+		Where(
+			builder.In("label.name", labelNames),
+		).
+		GroupBy("issue_label.issue_id")
+}
+
 // GetLabelIDsInReposByNames returns a list of labelIDs by names in one of the given
 // repositories.
 // it silently ignores label names that do not belong to the repository.
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 2f08ba6ea6..25664e45a9 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -88,6 +88,7 @@ func SearchIssues(ctx *context.APIContext) {
 		opts.Private = true
 		opts.AllLimited = true
 	}
+
 	issueCount := 0
 	for page := 1; ; page++ {
 		opts.Page = page
@@ -127,15 +128,6 @@ func SearchIssues(ctx *context.APIContext) {
 		issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword)
 	}
 
-	labels := ctx.Query("labels")
-	if splitted := strings.Split(labels, ","); labels != "" && len(splitted) > 0 {
-		labelIDs, err = models.GetLabelIDsInReposByNames(repoIDs, splitted)
-		if err != nil {
-			ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err)
-			return
-		}
-	}
-
 	var isPull util.OptionalBool
 	switch ctx.Query("type") {
 	case "pulls":
@@ -146,6 +138,12 @@ func SearchIssues(ctx *context.APIContext) {
 		isPull = util.OptionalBoolNone
 	}
 
+	labels := strings.TrimSpace(ctx.Query("labels"))
+	var includedLabelNames []string
+	if len(labels) > 0 {
+		includedLabelNames = strings.Split(labels, ",")
+	}
+
 	// Only fetch the issues if we either don't have a keyword or the search returned issues
 	// This would otherwise return all issues if no issues were found by the search.
 	if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
@@ -154,13 +152,13 @@ func SearchIssues(ctx *context.APIContext) {
 				Page:     ctx.QueryInt("page"),
 				PageSize: setting.UI.IssuePagingNum,
 			},
-			RepoIDs:        repoIDs,
-			IsClosed:       isClosed,
-			IssueIDs:       issueIDs,
-			LabelIDs:       labelIDs,
-			SortType:       "priorityrepo",
-			PriorityRepoID: ctx.QueryInt64("priority_repo_id"),
-			IsPull:         isPull,
+			RepoIDs:            repoIDs,
+			IsClosed:           isClosed,
+			IssueIDs:           issueIDs,
+			IncludedLabelNames: includedLabelNames,
+			SortType:           "priorityrepo",
+			PriorityRepoID:     ctx.QueryInt64("priority_repo_id"),
+			IsPull:             isPull,
 		})
 	}