List the services available from an endpoint.
diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go
index e9384e1..899b91c 100644
--- a/openstack/identity/v3/services/requests.go
+++ b/openstack/identity/v3/services/requests.go
@@ -1,8 +1,11 @@
 package services
 
 import (
+	"strconv"
+
 	"github.com/racker/perigee"
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/utils"
 )
 
 // Create adds a new service of the requested type to the catalog.
@@ -30,3 +33,37 @@
 
 	return &resp.Service, nil
 }
+
+// ListOpts allows you to query the List method.
+type ListOpts struct {
+	ServiceType string
+	PerPage     int
+	Page        int
+}
+
+// List enumerates the services available to a specific user.
+func List(client *gophercloud.ServiceClient, opts ListOpts) (*ServiceListResult, error) {
+	q := make(map[string]string)
+	if opts.ServiceType != "" {
+		q["type"] = opts.ServiceType
+	}
+	if opts.Page != 0 {
+		q["page"] = strconv.Itoa(opts.Page)
+	}
+	if opts.PerPage != 0 {
+		q["perPage"] = strconv.Itoa(opts.PerPage)
+	}
+	u := getListURL(client) + utils.BuildQuery(q)
+
+	var resp ServiceListResult
+	_, err := perigee.Request("GET", u, perigee.Options{
+		MoreHeaders: client.Provider.AuthenticatedHeaders(),
+		Results:     &resp,
+		OkCodes:     []int{200},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &resp, nil
+}
diff --git a/openstack/identity/v3/services/requests_test.go b/openstack/identity/v3/services/requests_test.go
index 6592fc6..cd96daf 100644
--- a/openstack/identity/v3/services/requests_test.go
+++ b/openstack/identity/v3/services/requests_test.go
@@ -55,3 +55,65 @@
 		t.Errorf("Service type was unexpected [%s]", result.Type)
 	}
 }
+
+func TestListSinglePage(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", "1111")
+
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, `
+			{
+				"links": {
+					"next": null,
+					"previous": null
+				},
+				"services": [
+					{
+						"description": "Service One",
+						"id": "1234",
+						"name": "service-one",
+						"type": "identity"
+					},
+					{
+						"description": "Service Two",
+						"id": "9876",
+						"name": "service-two",
+						"type": "compute"
+					}
+				]
+			}
+		`)
+	})
+
+	client := gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{
+			TokenID: "1111",
+		},
+		Endpoint: testhelper.Endpoint(),
+	}
+
+	result, err := List(&client, ListOpts{})
+	if err != nil {
+		t.Fatalf("Error listing services: %v", err)
+	}
+
+	if result.Pagination.Next != nil {
+		t.Errorf("Unexpected next link: %s", result.Pagination.Next)
+	}
+	if result.Pagination.Previous != nil {
+		t.Errorf("Unexpected previous link: %s", result.Pagination.Previous)
+	}
+	if len(result.Services) != 2 {
+		t.Errorf("Unexpected number of services: %s", len(result.Services))
+	}
+	if result.Services[0].ID != "1234" {
+		t.Errorf("Unexpected service: %#v", result.Services[0])
+	}
+	if result.Services[0].ID == "9876" {
+		t.Errorf("Unexpected service: %#v", result.Services[1])
+	}
+}
diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go
index baf8d6b..aba3aa1 100644
--- a/openstack/identity/v3/services/results.go
+++ b/openstack/identity/v3/services/results.go
@@ -1,5 +1,7 @@
 package services
 
+import "github.com/rackspace/gophercloud"
+
 // ServiceResult is the result of a list or information query.
 type ServiceResult struct {
 	Description *string `json:"description,omitempty"`
@@ -7,3 +9,10 @@
 	Name        string  `json:"name"`
 	Type        string  `json:"type"`
 }
+
+// ServiceListResult is a paged collection of Services.
+type ServiceListResult struct {
+	gophercloud.Pagination
+
+	Services []ServiceResult `json:"services"`
+}
diff --git a/openstack/utils/utils.go b/openstack/utils/utils.go
index f97f3d1..1d09d9e 100644
--- a/openstack/utils/utils.go
+++ b/openstack/utils/utils.go
@@ -60,6 +60,10 @@
 
 // BuildQuery constructs the query section of a URI from a map.
 func BuildQuery(params map[string]string) string {
+	if len(params) == 0 {
+		return ""
+	}
+
 	query := "?"
 	for k, v := range params {
 		query += k + "=" + v + "&"
diff --git a/pagination.go b/pagination.go
new file mode 100644
index 0000000..f100651
--- /dev/null
+++ b/pagination.go
@@ -0,0 +1,11 @@
+package gophercloud
+
+// Pagination stores information that's necessary to enumerate through pages of results.
+type Pagination struct {
+
+	// Next is the full URL to the next page of results, or nil if this is the last page.
+	Next *string `json:"next,omitempty"`
+
+	// Previous is the full URL to the previous page of results, or nil if this is the first page.s
+	Previous *string `json:"previous,omitempty"`
+}