diff --git a/docs/source/interface.rst b/docs/source/interface.rst
index e790bd827c6d3e92438980cb4158d081d54a3fd5..2c5e990616b1f30b4dd88e7ba3f05540fc8ac5fe 100644
--- a/docs/source/interface.rst
+++ b/docs/source/interface.rst
@@ -117,4 +117,14 @@ This endpoint accepts no request body.
 
 The response is **OK** with ``application/json`` content-type response body like::
 
-  {"total": <integer>, "unblinded-tokens": [<unblinded token string>, ...]}
+  { "total": <integer>
+  , "unblinded-tokens": [<unblinded token string>, ...]
+  , "lease-maintenance-spending": <spending object>
+  }
+
+The ``<spending object>`` may be ``null`` if the lease maintenance process has never run.
+If it has run,
+``<spending object>`` has two properties:
+
+ * ``when``: associated with an ISO8601 datetime string giving the approximate time the process ran
+ * ``count``: associated with a number giving the number of passes which would need to be spent to renew leases on all stored objects seen during the lease maintenance activity
diff --git a/src/_zkapauthorizer/resource.py b/src/_zkapauthorizer/resource.py
index fb7e61aab584103c48ffee628e8835d28b73d9bb..961aea71b7f268e0c23924ded4504795ea136cca 100644
--- a/src/_zkapauthorizer/resource.py
+++ b/src/_zkapauthorizer/resource.py
@@ -154,8 +154,18 @@ class _UnblindedTokenCollection(Resource):
                 in unblinded_tokens
                 if token > position
             ), limit)),
+            u"lease-maintenance-spending": self._lease_maintenance_activity(),
         })
 
+    def _lease_maintenance_activity(self):
+        activity = self._store.get_latest_lease_maintenance_activity()
+        if activity is None:
+            return activity
+        return {
+            u"when": activity.finished.isoformat(),
+            u"count": activity.passes_required,
+        }
+
 
 
 class _VoucherCollection(Resource):
diff --git a/src/_zkapauthorizer/tests/test_client_resource.py b/src/_zkapauthorizer/tests/test_client_resource.py
index 653574423654bd9c5d0cb73da3c37f4b01406d4d..aef17914a1fa31a84a0b543581ef35d9ab3d4223 100644
--- a/src/_zkapauthorizer/tests/test_client_resource.py
+++ b/src/_zkapauthorizer/tests/test_client_resource.py
@@ -47,6 +47,7 @@ from testtools import (
 from testtools.matchers import (
     MatchesStructure,
     MatchesAll,
+    MatchesAny,
     MatchesPredicate,
     AllMatch,
     HasLength,
@@ -56,6 +57,7 @@ from testtools.matchers import (
     Equals,
     Always,
     GreaterThan,
+    Is,
 )
 from testtools.twistedsupport import (
     CaptureTwistedLogs,
@@ -65,6 +67,10 @@ from testtools.content import (
     text_content,
 )
 
+from aniso8601 import (
+    parse_datetime,
+)
+
 from fixtures import (
     TempDir,
 )
@@ -124,6 +130,11 @@ from ..resource import (
     from_configuration,
 )
 
+from ..storage_common import (
+    BYTES_PER_PASS,
+    required_passes,
+)
+
 from .strategies import (
     tahoe_configs,
     client_unpaidredeemer_configurations,
@@ -268,9 +279,10 @@ class UnblindedTokenTests(TestCase):
     @given(tahoe_configs(), vouchers(), integers(min_value=0, max_value=100))
     def test_get(self, get_config, voucher, num_tokens):
         """
-        When the unblinded token collection receives a **GET**, the response is the
-        total number of unblinded tokens in the system and the unblinded tokens
-        themselves.
+        When the unblinded token collection receives a **GET**, the response is
+        the total number of unblinded tokens in the system, the unblinded
+        tokens themselves, and information about tokens spent on recent lease
+        maintenance activity.
         """
         tempdir = self.useFixture(TempDir())
         config = get_config(tempdir.join(b"tahoe"), b"tub.port")
@@ -374,6 +386,7 @@ class UnblindedTokenTests(TestCase):
                         IsInstance(unicode),
                     ),
                 ),
+                matches_lease_maintenance_spending(),
             ),
         )
 
@@ -432,8 +445,56 @@ class UnblindedTokenTests(TestCase):
             ),
         )
 
+    @given(
+        tahoe_configs(),
+        lists(
+            lists(
+                integers(min_value=0, max_value=2 ** 63 - 1),
+                min_size=1,
+            ),
+        ),
+        datetimes(),
+    )
+    def test_latest_lease_maintenance_spending(self, get_config, size_observations, now):
+        """
+        The most recently completed record of lease maintenance spending activity
+        is reported in the response to a **GET** request.
+        """
+        tempdir = self.useFixture(TempDir())
+        config = get_config(tempdir.join(b"tahoe"), b"tub.port")
+        root = root_from_config(config, lambda: now)
+
+        # Put some activity into it.
+        total = 0
+        activity = root.store.start_lease_maintenance()
+        for sizes in size_observations:
+            total += required_passes(BYTES_PER_PASS, sizes)
+            activity.observe(sizes)
+        activity.finish()
+
+        agent = RequestTraversalAgent(root)
+        d = agent.request(
+            b"GET",
+            b"http://127.0.0.1/unblinded-token",
+        )
+        d.addCallback(readBody)
+        d.addCallback(
+            lambda body: loads(body)[u"lease-maintenance-spending"],
+        )
+        self.assertThat(
+            d,
+            succeeded(Equals({
+                "when": now.isoformat(),
+                "count": total,
+            })),
+        )
+
 
-def succeeded_with_unblinded_tokens_with_matcher(all_token_count, match_unblinded_tokens):
+def succeeded_with_unblinded_tokens_with_matcher(
+        all_token_count,
+        match_unblinded_tokens,
+        match_lease_maint_spending,
+):
     """
     :return: A matcher which matches a Deferred which fires with a response
         like the one returned by the **unblinded-tokens** endpoint.
@@ -443,6 +504,9 @@ def succeeded_with_unblinded_tokens_with_matcher(all_token_count, match_unblinde
 
     :param match_unblinded_tokens: A matcher for the ``unblinded-tokens``
         field of the response.
+
+    :param match_lease_maint_spending: A matcher for the
+        ``lease-maintenance-spending`` field of the response.
     """
     return succeeded(
         MatchesAll(
@@ -453,6 +517,7 @@ def succeeded_with_unblinded_tokens_with_matcher(all_token_count, match_unblinde
                     ContainsDict({
                         u"total": Equals(all_token_count),
                         u"unblinded-tokens": match_unblinded_tokens,
+                        u"lease-maintenance-spending": match_lease_maint_spending,
                     }),
                 ),
             ),
@@ -475,9 +540,42 @@ def succeeded_with_unblinded_tokens(all_token_count, returned_token_count):
         MatchesAll(
             HasLength(returned_token_count),
             AllMatch(IsInstance(unicode)),
-        )
+        ),
+        matches_lease_maintenance_spending(),
     )
 
+def matches_lease_maintenance_spending():
+    """
+    :return: A matcher which matches the value of the
+        *lease-maintenance-spending* key in the ``unblinded-tokens`` endpoint
+        response.
+    """
+    return MatchesAny(
+        Is(None),
+        ContainsDict({
+            u"when": matches_iso8601_datetime(),
+            u"amount": matches_positive_integer(),
+        }),
+    )
+
+def matches_positive_integer():
+    return MatchesAll(
+        IsInstance(int),
+        GreaterThan(0),
+    )
+
+def matches_iso8601_datetime():
+    """
+    :return: A matcher which matches unicode strings which can be parsed as an
+        ISO8601 datetime string.
+    """
+    return MatchesAll(
+        IsInstance(unicode),
+        AfterPreprocessing(
+            parse_datetime,
+            lambda d: Always(),
+        ),
+    )
 
 class VoucherTests(TestCase):
     """