Testing out a new pagination idiom.
diff --git a/collections.go b/collections.go
index 443a000..38078f0 100644
--- a/collections.go
+++ b/collections.go
@@ -1,65 +1,87 @@
 package gophercloud
 
-import (
-	"errors"
-
-	"github.com/racker/perigee"
-)
+import "errors"
 
 var (
 	// ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist.
 	ErrPageNotAvailable = errors.New("The requested Collection page does not exist.")
 )
 
-// Collection describes the minimum functionality that any collection resource must implement to be able to use
-// the global paging and iteration functions.
-// Every resource that returns a list of multiple results must implement this functionality, whether or not it is paged.
-// In addition to the methods provided here, each collection should also provide an AsItem(Page) method that
-// casts the Page to its more specific type and returns the Page's contents as a slice.
+// Collection must be satisfied by the result type of any resource collection.
+// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated.
 type Collection interface {
 
-	// Pager returns one of the concrete Pager implementations from this package, or a custom one.
-	// The style of Pager returned determines how the collection is paged.
-	Pager() Pager
+	// NextPageURL generates the URL for the page of data that follows this collection.
+	// Return "" if no such page exists.
+	NextPageURL() string
 
-	// Concat the contents of another collection on to the end of this one.
-	// Return a new collection that contains elements from both.
+	// Concat creates a new Collection that contains all of the elements from this page and another page.
+	// It's used to aggregate results for the AllPages method.
 	Concat(Collection) Collection
 }
 
-// EachPage iterates through a Collection one page at a time.
-// The handler function will be invoked with a Collection containing each page.
-// If the handler returns true, iteration will continue. If it returns false, no more pages will be fetched.
-func EachPage(first Collection, handler func(Collection) bool) error {
-	p := first.Pager()
-	var err error
-	current := first
+// Pager knows how to advance through a specific resource collection, one page at a time.
+type Pager struct {
+	initialURL string
 
+	advance func(string) (Collection, error)
+}
+
+// NewPager constructs a manually-configured pager.
+// Supply the URL for the first page and a function that requests a specific page given a URL.
+func NewPager(initialURL string, advance func(string) (Collection, error)) Pager {
+	return Pager{
+		initialURL: initialURL,
+		advance:    advance,
+	}
+}
+
+// NewSinglePager constructs a Pager that "iterates" over a single-paged Collection.
+// Supply a function that returns the only page.
+func NewSinglePager(only func() (Collection, error)) Pager {
+	consumed := false
+	single := func(_ string) (Collection, error) {
+		if !consumed {
+			consumed = true
+			return only()
+		}
+		return nil, ErrPageNotAvailable
+	}
+
+	return Pager{
+		initialURL: "",
+		advance:    single,
+	}
+}
+
+// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
+// Return "false" from the handler to prematurely stop iterating.
+func (p Pager) EachPage(handler func(Collection) bool) error {
+	currentURL := p.initialURL
 	for {
-		if !handler(current) {
-			return nil
-		}
-
-		if !p.HasNextPage() {
-			return nil
-		}
-
-		current, err = p.NextPage()
+		currentPage, err := p.advance(currentURL)
 		if err != nil {
 			return err
 		}
+
+		if !handler(currentPage) {
+			return nil
+		}
+
+		currentURL = currentPage.NextPageURL()
+		if currentURL == "" {
+			return nil
+		}
 	}
 }
 
-// AllPages consolidates all pages reachable from a provided starting point into a single mega-Page.
-// Use this only when you know that the full set will always fit within memory.
-func AllPages(first Collection) (Collection, error) {
-	megaPage := first
-	isFirst := true
+// AllPages accumulates every page reachable from a Pager into a single Collection, for convenience.
+func (p Pager) AllPages() (Collection, error) {
+	var megaPage Collection
 
-	err := EachPage(first, func(page Collection) bool {
-		if isFirst {
-			isFirst = false
+	err := p.EachPage(func(page Collection) bool {
+		if megaPage == nil {
+			megaPage = page
 		} else {
 			megaPage = megaPage.Concat(page)
 		}
@@ -69,34 +91,6 @@
 	return megaPage, err
 }
 
-// Pager describes a specific paging idiom for a Collection resource.
-// Generally, to use a Pager, the Collection must also implement a more specialized interface than Collection.
-// Clients should not generally interact with Pagers directly.
-// Instead, use the more convenient collection traversal methods: AllPages and EachPage.
-type Pager interface {
-
-	// HasNextPage returns true if a call to NextPage will return an additional Page of results.
-	HasNextPage() bool
-
-	// NextPage returns the next Page in the sequence.
-	// Panics if no page is available, so always check HasNextPage first.
-	NextPage() (Collection, error)
-}
-
-// SinglePager is used by collections that are not actually paged.
-// It has no additional interface requirements for its host Page.
-type SinglePager struct{}
-
-// HasNextPage always reports false.
-func (p SinglePager) HasNextPage() bool {
-	return false
-}
-
-// NextPage always returns an ErrPageNotAvailable.
-func (p SinglePager) NextPage() (Collection, error) {
-	return nil, ErrPageNotAvailable
-}
-
 // PaginationLinks stores the `next` and `previous` links that are provided by some (but not all) paginated resources.
 type PaginationLinks struct {
 
@@ -106,59 +100,3 @@
 	// Previous is the full URL to the previous page of results, or nil if this is the first page.
 	Previous *string `json:"previous,omitempty"`
 }
-
-// LinkCollection must be satisfied by a Page that uses a LinkPager.
-type LinkCollection interface {
-	Collection
-
-	// Service returns the client used to make further requests.
-	Service() *ServiceClient
-
-	// Links returns the pagination links from a single page.
-	Links() PaginationLinks
-
-	// Interpret an arbitrary JSON result as a new LinkCollection.
-	Interpret(interface{}) (LinkCollection, error)
-}
-
-// LinkPager implements paging for collections that provide a link structure in their response JSON.
-// It follows explicit `next` links and stops when the `next` link is "null".
-type LinkPager struct {
-	current LinkCollection
-}
-
-// NewLinkPager creates and initializes a pager for a LinkCollection.
-func NewLinkPager(first LinkCollection) *LinkPager {
-	return &LinkPager{current: first}
-}
-
-// HasNextPage checks the `next` link in the pagination data.
-func (p *LinkPager) HasNextPage() bool {
-	return p.current.Links().Next != nil
-}
-
-// NextPage follows the `next` link to construct the next page of data.
-func (p *LinkPager) NextPage() (Collection, error) {
-	url := p.current.Links().Next
-	if url == nil {
-		return nil, ErrPageNotAvailable
-	}
-
-	var response interface{}
-	_, err := perigee.Request("GET", *url, perigee.Options{
-		MoreHeaders: p.current.Service().Provider.AuthenticatedHeaders(),
-		Results:     &response,
-		OkCodes:     []int{200},
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	interpreted, err := p.current.Interpret(response)
-	if err != nil {
-		return nil, err
-	}
-
-	p.current = interpreted
-	return interpreted, nil
-}
diff --git a/collections_test.go b/collections_test.go
index 4b0fdc5..c83bd38 100644
--- a/collections_test.go
+++ b/collections_test.go
@@ -1,7 +1,6 @@
 package gophercloud
 
 import (
-	"errors"
 	"fmt"
 	"net/http"
 	"reflect"
@@ -16,25 +15,27 @@
 	results []int
 }
 
-func (c SinglePageCollection) Pager() Pager {
-	return SinglePager{}
+func (c SinglePageCollection) NextPageURL() string {
+	panic("NextPageURL should never be called on a single-paged collection.")
 }
 
 func (c SinglePageCollection) Concat(other Collection) Collection {
 	panic("Concat should never be called on a single-paged collection.")
 }
 
-func AsSingleInts(c Collection) []int {
+func ExtractSingleInts(c Collection) []int {
 	return c.(SinglePageCollection).results
 }
 
-var single = SinglePageCollection{
-	results: []int{1, 2, 3},
+func setupSinglePaged() Pager {
+	return NewSinglePager(func() (Collection, error) {
+		return SinglePageCollection{results: []int{1, 2, 3}}, nil
+	})
 }
 
 func TestEnumerateSinglePaged(t *testing.T) {
 	callCount := 0
-	EachPage(single, func(page Collection) bool {
+	err := setupSinglePaged().EachPage(func(page Collection) bool {
 		callCount++
 
 		expected := []int{1, 2, 3}
@@ -44,6 +45,9 @@
 		}
 		return true
 	})
+	if err != nil {
+		t.Fatalf("Unexpected error calling EachPage: %v", err)
+	}
 
 	if callCount != 1 {
 		t.Errorf("Callback was invoked %d times", callCount)
@@ -51,13 +55,13 @@
 }
 
 func TestAllSinglePaged(t *testing.T) {
-	r, err := AllPages(single)
+	r, err := setupSinglePaged().AllPages()
 	if err != nil {
 		t.Fatalf("Unexpected error when iterating pages: %v", err)
 	}
 
 	expected := []int{1, 2, 3}
-	actual := AsSingleInts(r)
+	actual := ExtractSingleInts(r)
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("Expected %v, but was %v", expected, actual)
 	}
@@ -68,73 +72,36 @@
 type LinkedCollection struct {
 	PaginationLinks
 
-	service *ServiceClient
 	results []int
 }
 
-func (c LinkedCollection) Pager() Pager {
-	return NewLinkPager(c)
+func (page LinkedCollection) NextPageURL() string {
+	n := page.PaginationLinks.Next
+	if n == nil {
+		return ""
+	}
+	return *n
 }
 
-func (c LinkedCollection) Concat(other Collection) Collection {
+func (page LinkedCollection) Concat(other Collection) Collection {
 	return LinkedCollection{
-		service: c.service,
+		service: page.service,
 		results: append(c.results, AsLinkedInts(other)...),
 	}
 }
 
-func (c LinkedCollection) Links() PaginationLinks {
-	return c.PaginationLinks
-}
-
-func (c LinkedCollection) Service() *ServiceClient {
-	return c.service
-}
-
-func (c LinkedCollection) Interpret(response interface{}) (LinkCollection, error) {
-	casted, ok := response.([]interface{})
-	if ok {
-		asInts := make([]int, len(casted))
-		for index, item := range casted {
-			f := item.(float64)
-			asInts[index] = int(f)
-		}
-
-		var nextURL *string
-		switch asInts[0] {
-		case 4:
-			u := testhelper.Server.URL + "/foo?page=3&perPage=3"
-			nextURL = &u
-		case 7:
-			// Leave nextURL as nil.
-		default:
-			return nil, fmt.Errorf("Unexpected resultset: %#v", asInts)
-		}
-
-		result := LinkedCollection{
-			PaginationLinks: PaginationLinks{Next: nextURL},
-			service:         c.service,
-			results:         asInts,
-		}
-		return result, nil
-	}
-	return nil, errors.New("Wat")
-}
-
 func AsLinkedInts(results Collection) []int {
 	return results.(LinkedCollection).results
 }
 
-func createLinked() LinkedCollection {
+func createLinked() Pager {
 	nextURL := testhelper.Server.URL + "/foo?page=2&perPage=3"
-	return LinkedCollection{
-		PaginationLinks: PaginationLinks{Next: &nextURL},
-		service: &ServiceClient{
-			Provider: &ProviderClient{TokenID: "1234"},
-			Endpoint: testhelper.Endpoint(),
-		},
-		results: []int{1, 2, 3},
-	}
+	return CreatePager(func(url) Collection {
+		LinkedCollection{
+			PaginationLinks: PaginationLinks{Next: &nextURL},
+			results:         []int{1, 2, 3},
+		}
+	})
 }
 
 func setupLinkedResponses(t *testing.T) {