diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py
index 38ba354d050b74031cb736c66453c3532e41f22c..1c1488b7a359890f9a4bb3835a72a7383d2791e6 100644
--- a/src/_zkapauthorizer/_storage_server.py
+++ b/src/_zkapauthorizer/_storage_server.py
@@ -41,7 +41,7 @@ from attr.validators import instance_of, provides
 from challenge_bypass_ristretto import SigningKey, TokenPreimage, VerificationSignature
 from eliot import start_action
 from foolscap.api import Referenceable
-from prometheus_client import CollectorRegistry
+from prometheus_client import CollectorRegistry, Counter
 from twisted.internet.defer import Deferred
 from twisted.internet.interfaces import IReactorTime
 from twisted.python.reflect import namedAny
@@ -172,11 +172,22 @@ class ZKAPAuthorizerStorageServer(Referenceable):
     _original = attr.ib(validator=provides(RIStorageServer))
     _pass_value = pass_value_attribute()
     _signing_key = attr.ib(validator=instance_of(SigningKey))
-    _registry = attr.ib(default=attr.Factory(CollectorRegistry))
+    _registry = attr.ib(
+        default=attr.Factory(CollectorRegistry),
+        validator=attr.validators.instance_of(CollectorRegistry),
+    )
     _clock = attr.ib(
         validator=provides(IReactorTime),
         default=attr.Factory(partial(namedAny, "twisted.internet.reactor")),
     )
+    _metric_spending_successes = attr.ib(init=False)
+
+    def __attrs_post_init__(self):
+        self._metric_spending_successes = Counter(
+            "zkapauthorizer_server_spending_successes",
+            "ZKAP Spending Successes Counter",
+            registry=self._registry,
+        )
 
     def remote_get_version(self):
         """
@@ -204,6 +215,7 @@ class ZKAPAuthorizerStorageServer(Referenceable):
             passes,
             self._signing_key,
         )
+        self._metric_spending_successes.inc(len(validation.valid))
 
         # Note: The *allocate_buckets* protocol allows for some shares to
         # already exist on the server.  When this is the case, the cost of the
diff --git a/src/_zkapauthorizer/tests/test_storage_server.py b/src/_zkapauthorizer/tests/test_storage_server.py
index cdff293f8e7cd638f7b4cb3df1263196584061d5..3e7ce1b0a0aeb8e2d1f46d530db4e0627a97deed 100644
--- a/src/_zkapauthorizer/tests/test_storage_server.py
+++ b/src/_zkapauthorizer/tests/test_storage_server.py
@@ -162,7 +162,7 @@ class PassValidationTests(TestCase):
             self.anonymous_storage_server,
             self.pass_value,
             self.signing_key,
-            self.clock,
+            clock=self.clock,
         )
 
     def setup_example(self):
@@ -559,3 +559,47 @@ class PassValidationTests(TestCase):
             actual_sizes,
             Equals(expected_sizes),
         )
+
+    @given(
+        storage_index=storage_indexes(),
+        renew_secret=lease_renew_secrets(),
+        cancel_secret=lease_cancel_secrets(),
+        sharenums=sharenum_sets(),
+        size=sizes(),
+    )
+    def test_immutable_spending_metrics(
+        self, storage_index, renew_secret, cancel_secret, sharenums, size
+    ):
+        """
+        When ZKAPs are spent to call *allocate_buckets* the number of passes spent is recorded as a metric.
+        """
+        expected = required_passes(
+            self.storage_server._pass_value, [size] * len(sharenums)
+        )
+        valid_passes = make_passes(
+            self.signing_key,
+            allocate_buckets_message(storage_index),
+            list(RandomToken.create() for i in range(expected)),
+        )
+
+        before_count = self.storage_server._metric_spending_successes._value.get()
+        alreadygot, allocated = self.storage_server.doRemoteCall(
+            "allocate_buckets",
+            (),
+            dict(
+                passes=valid_passes,
+                storage_index=storage_index,
+                renew_secret=renew_secret,
+                cancel_secret=cancel_secret,
+                sharenums=sharenums,
+                allocated_size=size,
+                canary=LocalReferenceable(None),
+            ),
+        )
+
+        after_count = self.storage_server._metric_spending_successes._value.get()
+
+        self.assertThat(
+            after_count - before_count,
+            Equals(expected),
+        )