diff --git a/client/mpd/djrandom.go b/client/mpd/djrandom.go
index f425ede1a097234d2a830c7777d4f08f9a74751a..e43cded11264346b239374e8f7cb9f10d90589c6 100644
--- a/client/mpd/djrandom.go
+++ b/client/mpd/djrandom.go
@@ -10,6 +10,7 @@ import (
 	"log"
 	"net/http"
 	"net/url"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -23,6 +24,7 @@ import (
 
 var (
 	configFile = flag.String("djrandom-config", util.ExpandTilde("~/.djrandom.conf"), "Config file location")
+	maxResults = flag.Int("max-search-results", 200, "maximum number of search results to return")
 
 	serverUrl     = config.String("djrandom-server", "https://djrandom.incal.net", "Server URL")
 	authKeyId     = config.String("auth_key", "", "API authentication key")
@@ -182,6 +184,7 @@ func (d *DJRandomDatabase) doQuery(query string) ([]mpd.Song, error) {
 
 	values := url.Values{}
 	values.Add("q", query)
+	values.Add("limit", strconv.Itoa(*maxResults))
 	req, err := d.client.NewRequest("POST", "/api/search", strings.NewReader(values.Encode()))
 	if err != nil {
 		return nil, err
diff --git a/server/frontend/api_views.go b/server/frontend/api_views.go
index deaaabddfe67a6669815206e0f7eaad41e25ec91..3cab330e53e98284b0b7144b5e15ed74e4471f0f 100644
--- a/server/frontend/api_views.go
+++ b/server/frontend/api_views.go
@@ -2,6 +2,7 @@ package frontend
 
 import (
 	"encoding/json"
+	"errors"
 	// "fmt"
 	"io"
 	"io/ioutil"
@@ -182,49 +183,67 @@ func CheckFingerprints(w http.ResponseWriter, r *djRequest) {
 	sendJsonResponse(w, &fpResp)
 }
 
-// SearchIds runs a search query and returns only song IDs.
-func SearchIds(w http.ResponseWriter, r *djRequest) {
-	// Assemble the search query.
+func doSearch(r *djRequest) ([]api.SongID, error) {
+	// Extract query parameters from the request.
 	query := r.Request.FormValue("q")
 	if query == "" {
-		http.Error(w, "Empty search query", http.StatusBadRequest)
-		return
+		return nil, errors.New("empty search query")
+	}
+	var limit int
+	limitstr := r.Request.FormValue("limit")
+	if limitstr != "" {
+		var err error
+		limit, err = strconv.Atoi(limitstr)
+		if err != nil {
+			return nil, err
+		}
 	}
 
-	// Run search.
-	var resp api.SearchIdsResponse
-	for item, _ := range r.Ctx.Index.Search(query) {
-		resp.Results = append(resp.Results, item)
+	// Run search, only return 'limit' results (if 0, return all
+	// of them). Try to use the least memory possible.
+	results := r.Ctx.Index.Search(query)
+	if limit == 0 {
+		limit = len(results)
 	}
-	sendJsonResponse(w, &resp)
+
+	songIds := make([]api.SongID, 0, limit)
+	i := 0
+	for id := range results {
+		songIds = append(songIds, id)
+		i++
+		if i > limit {
+			break
+		}
+	}
+
+	return songIds, nil
 }
 
-// Search returns full search results.
-func Search(w http.ResponseWriter, r *djRequest) {
-	// Assemble the search query.
-	query := r.Request.FormValue("q")
-	if query == "" {
-		http.Error(w, "Empty search query", http.StatusBadRequest)
+// SearchIds runs a search query and returns only song IDs.
+func SearchIds(w http.ResponseWriter, r *djRequest) {
+	ids, err := doSearch(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
+	sendJsonResponse(w, &api.SearchIdsResponse{Results: ids})
+}
 
-	// Run search (and create a list).
-	results := r.Ctx.Index.Search(query)
-	songIds := make([]api.SongID, 0, len(results))
-	for id, _ := range results {
-		songIds = append(songIds, id)
+// Search returns full search results.
+func Search(w http.ResponseWriter, r *djRequest) {
+	ids, err := doSearch(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
 	}
 
-	songs, err := db_client.ParallelFetchSongs(r.Ctx.Db, songIds)
+	songs, err := db_client.ParallelFetchSongs(r.Ctx.Db, ids)
 	if err != nil {
 		log.Printf("Search(): %s", err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
-	resp := api.SearchResponse{
-		Results: songs,
-	}
-	sendJsonResponse(w, &resp)
+	sendJsonResponse(w, &api.SearchResponse{Results: songs})
 }
 
 // ArtistAutocomplete returns artist autocompletion results.