Skip to content

Restful Pagination

DodaTech 2 min read

title: "RESTful Pagination — Page-Based and Cursor-Based Pagination" description: "RESTful pagination provides page-based or cursor-based navigation for large collections, using consistent query parameters and metadata in responses." date: 2026-06-28 lastmod: 2026-06-28 weight: 17 tags: [apis, restful] }

RESTful pagination splits large resource collections into manageable pages using standard query parameters with consistent response metadata for navigation.

What You'll Learn

  • Page-based pagination
  • Cursor-based pagination
  • Pagination metadata conventions

Why It Matters

Without pagination, large collections overwhelm clients and servers. Consistent pagination conventions let clients handle any size dataset efficiently.

Code Examples

# Page-based pagination
@app.route('/users')
def list_users():
    page = request.args.get('page', 1, type=int)
    limit = min(request.args.get('limit', 20, type=int), 100)
    offset = (page - 1) * limit

    total = db.count_users()
    total_pages = (total + limit - 1) // limit

    users = db.get_users(limit=limit, offset=offset)

    return jsonify({
        "data": [u.to_dict() for u in users],
        "pagination": {
            "page": page,
            "limit": limit,
            "total": total,
            "total_pages": total_pages,
            "has_next": page < total_pages,
            "has_prev": page > 1
        }
    })

# Cursor-based pagination
@app.route('/orders')
def list_orders():
    cursor = request.args.get('cursor')
    limit = min(request.args.get('limit', 20, type=int), 100)

    if cursor:
        cursor_id = decode_cursor(cursor)
        orders = db.execute(
            "SELECT * FROM orders WHERE id > ? ORDER BY id LIMIT ?",
            [cursor_id, limit + 1]
        )
    else:
        orders = db.execute(
            "SELECT * FROM orders ORDER BY id LIMIT ?",
            [limit + 1]
        )

    has_more = len(orders) > limit
    orders = orders[:limit]
    next_cursor = encode_cursor(orders[-1]['id']) if has_more else None

    return jsonify({
        "data": orders,
        "pagination": {
            "next_cursor": next_cursor,
            "has_more": has_more
        }
    })
app.get('/api/users', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = Math.min(parseInt(req.query.limit) || 20, 100);
  const offset = (page - 1) * limit;

  const { users, total } = db.getUsers(limit, offset);

  res.json({
    data: users,
    pagination: {
      page,
      limit,
      total,
      totalPages: Math.ceil(total / limit),
      hasNext: page * limit < total,
      hasPrev: page > 1
    }
  });
});

Common Mistakes

1. No Default Pagination

Returning all results when no pagination params provided.

2. No Limit on Page Size

Clients request 10,000 items per page, overwhelming the server.

3. Inconsistent Parameter Names

Mix of page, offset, limit, per_page across endpoints.

4. Missing Pagination Metadata

Clients can't determine total results or navigate pages.

5. No Cursor Encoding

Exposing raw database IDs in cursor values.

Practice Questions

  1. What is the default page size recommendation?
  2. What parameters control page-based pagination?
  3. What parameters control cursor-based pagination?
  4. Why use cursor pagination over page-based?
  5. What metadata should pagination responses include?

Answers:

  1. 20-30 items per page, with a maximum of 100.
  2. page and limit (or per_page).
  3. cursor and limit.
  4. Stable results for real-time data and better performance at scale.
  5. Page, limit, total, total_pages, has_next, has_prev (or cursor equivalents).

Challenge: Implement both page-based and cursor-based pagination for a REST endpoint. Let clients choose which to use.

FAQ

What is the standard parameter name for page size?

: limit or per_page. Choose one and use it consistently.

Should I include total count for cursor pagination?

: Optional. Cursor pagination works without total, but adding it helps clients.

How do I handle pagination in POST requests?

: POST requests typically don't paginate. Use GET for list endpoints.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro