diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go
index 604e6d6381..74512cafa3 100644
--- a/integrations/api_issue_test.go
+++ b/integrations/api_issue_test.go
@@ -222,6 +222,20 @@ func TestAPISearchIssues(t *testing.T) {
 	resp = session.MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
 	assert.Len(t, apiIssues, 1)
+
+	query = url.Values{"milestones": {"milestone1"}, "state": {"all"}}
+	link.RawQuery = query.Encode()
+	req = NewRequest(t, "GET", link.String())
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	DecodeJSON(t, resp, &apiIssues)
+	assert.Len(t, apiIssues, 1)
+
+	query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}}
+	link.RawQuery = query.Encode()
+	req = NewRequest(t, "GET", link.String())
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	DecodeJSON(t, resp, &apiIssues)
+	assert.Len(t, apiIssues, 2)
 }
 
 func TestAPISearchIssuesWithLabels(t *testing.T) {
diff --git a/models/issue.go b/models/issue.go
index f1a092debd..ffbc110a6b 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -1100,6 +1100,7 @@ type IssuesOptions struct {
 	LabelIDs           []int64
 	IncludedLabelNames []string
 	ExcludedLabelNames []string
+	IncludeMilestones  []string
 	SortType           string
 	IssueIDs           []int64
 	UpdatedAfterUnix   int64
@@ -1241,6 +1242,13 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
 	if len(opts.ExcludedLabelNames) > 0 {
 		sess.And(builder.NotIn("issue.id", BuildLabelNamesIssueIDsCondition(opts.ExcludedLabelNames)))
 	}
+
+	if len(opts.IncludeMilestones) > 0 {
+		sess.In("issue.milestone_id",
+			builder.Select("id").
+				From("milestone").
+				Where(builder.In("name", opts.IncludeMilestones)))
+	}
 }
 
 func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 5932765ab8..5a7d10b36f 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -42,6 +42,10 @@ func SearchIssues(ctx *context.APIContext) {
 	//   in: query
 	//   description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
 	//   type: string
+	// - name: milestones
+	//   in: query
+	//   description: comma separated list of milestone names. Fetch only issues that have any of this milestones. Non existent are discarded
+	//   type: string
 	// - name: q
 	//   in: query
 	//   description: search string
@@ -164,6 +168,12 @@ func SearchIssues(ctx *context.APIContext) {
 		includedLabelNames = strings.Split(labels, ",")
 	}
 
+	milestones := strings.TrimSpace(ctx.Query("milestones"))
+	var includedMilestones []string
+	if len(milestones) > 0 {
+		includedMilestones = strings.Split(milestones, ",")
+	}
+
 	// this api is also used in UI,
 	// so the default limit is set to fit UI needs
 	limit := ctx.QueryInt("limit")
@@ -175,7 +185,7 @@ func SearchIssues(ctx *context.APIContext) {
 
 	// 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(includedLabelNames) > 0 {
+	if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 {
 		issuesOpt := &models.IssuesOptions{
 			ListOptions: models.ListOptions{
 				Page:     ctx.QueryInt("page"),
@@ -185,6 +195,7 @@ func SearchIssues(ctx *context.APIContext) {
 			IsClosed:           isClosed,
 			IssueIDs:           issueIDs,
 			IncludedLabelNames: includedLabelNames,
+			IncludeMilestones:  includedMilestones,
 			SortType:           "priorityrepo",
 			PriorityRepoID:     ctx.QueryInt64("priority_repo_id"),
 			IsPull:             isPull,
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 8ea5edb6fc..4f54b90492 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -1876,6 +1876,12 @@
             "name": "labels",
             "in": "query"
           },
+          {
+            "type": "string",
+            "description": "comma separated list of milestone names. Fetch only issues that have any of this milestones. Non existent are discarded",
+            "name": "milestones",
+            "in": "query"
+          },
           {
             "type": "string",
             "description": "search string",