{"slug":"set-per-user-spending-limits","title":"Set per-user spending limits","tags":["tailscale"],"agent_summary":"Last validated: Apr 9, 2026","trigger_phrases":[],"runnable":false,"markdown":"\r\n# Set per-user spending limits\r\n\r\nLast validated: Apr 9, 2026\r\n\r\nAperture by Tailscaleis currently [in alpha](https://tailscale.com/docs/reference/tailscale-release-stages#alpha).\r\n\r\nWhen multiple people use LLMs through Aperture, a single user can consume a disproportionate share of your AI budget. Per-user spending limits give each person an independent budget that refills automatically, so no one exhausts the team's resources.\r\n\r\nAperture uses quota buckets to enforce spending limits. Each bucket has a capacity (maximum balance), a refill rate, and a behavior when the limit is exceeded. Using the `<user>` template variable in the bucket name creates a separate bucket for each person who sends requests through Aperture.\r\n\r\n## [Prerequisites](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#prerequisites)\r\n\r\nBefore you begin, ensure you have the following:\r\n\r\n- An [Aperture instance](https://tailscale.com/docs/aperture/get-started) with at least one provider configured.\r\n- Admin access to the [Aperture configuration](https://tailscale.com/docs/aperture/configuration).\r\n\r\n## [Define a per-user quota bucket](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#define-a-per-user-quota-bucket)\r\n\r\nOpen the **Settings** page in the [Aperture dashboard](https://tailscale.com/docs/aperture/reference/dashboard) and add a `quotas` section to your configuration. The following example creates a per-user bucket with a $10 capacity that refills at $5 per day:\r\n\r\n```json\r\n\"quotas\": {\r\n  \"daily:<user>\": {\r\n    \"capacity\": \"$10.00\",\r\n    \"rate\": \"$5.00/day\",\r\n    \"on_exceed\": \"reject\"\r\n  }\r\n}\r\n```\r\n\r\nThe `<user>` template expands to each caller's Tailscale login name at request time. For example, when `alice@example.com` sends a request, Aperture charges the bucket `daily:alice@example.com`. When `bob@example.com` sends a request, Aperture charges `daily:bob@example.com`. Each bucket tracks its balance independently.\r\n\r\nThe `capacity` field sets the maximum balance the bucket can hold. This is also the starting balance. The `rate` field controls how fast the bucket refills. Supported time units are `min`, `hour`, `day`, `week`, and `month` (30 days).\r\n\r\nWhen `on_exceed` is set to `\"reject\"`, Aperture blocks requests that would bring the bucket below zero and returns HTTP 429 with a `Retry-After` header. Currently, `\"reject\"` is the only supported value for `on_exceed`.\r\n\r\n## [Attach the quota to a grant](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#attach-the-quota-to-a-grant)\r\n\r\nA quota bucket has no effect until a grant references it. Add a `quotas` entry to the capability in your `grants` section that should enforce the limit:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"*\"],\\\r\n    \"app\": {\\\r\n      \"tailscale.com/cap/aperture\": [\\\r\n        { \"role\": \"user\" },\\\r\n        {\\\r\n          \"models\": \"**\",\\\r\n          \"quotas\": [\\\r\n            {\"bucket\": \"daily:<user>\"}\\\r\n          ]\\\r\n        }\\\r\n      ]\\\r\n    }\\\r\n  }\\\r\n]\r\n```\r\n\r\nThis grant applies the per-user quota to all models for all users. When a request matches the grant, Aperture checks the user's bucket balance before forwarding the request.\r\n\r\n## [Scope a quota to specific models](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#scope-a-quota-to-specific-models)\r\n\r\nTo limit spending on expensive models while leaving others unmetered, create a quota that applies only to specific model patterns:\r\n\r\n```json\r\n\"quotas\": {\r\n  \"opus:<user>\": {\r\n    \"capacity\": \"$5.00\",\r\n    \"rate\": \"$2.50/day\",\r\n    \"on_exceed\": \"reject\"\r\n  }\r\n},\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"*\"],\\\r\n    \"app\": {\\\r\n      \"tailscale.com/cap/aperture\": [\\\r\n        { \"models\": \"**\" },\\\r\n        {\\\r\n          \"models\": \"*/claude-opus*\",\\\r\n          \"quotas\": [\\\r\n            {\"bucket\": \"opus:<user>\"}\\\r\n          ]\\\r\n        }\\\r\n      ]\\\r\n    }\\\r\n  }\\\r\n]\r\n```\r\n\r\nIn this configuration, requests to Opus models are metered against the `opus:<user>` bucket, while all other models have no spending limit.\r\n\r\n## [Verify the configuration](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#verify-the-configuration)\r\n\r\nAfter saving the configuration, confirm the quota is active:\r\n\r\n1. Send a test request through Aperture.\r\n2. Check the [quota status](https://tailscale.com/docs/aperture/how-to/check-and-refill-budgets) using the API (`GET /api/quotas`) or the **Models** page in the Aperture dashboard. The quota bucket for your user should appear with the correct capacity and a balance reduced by the cost of your test request.\r\n\r\nIf a request exceeds the quota, Aperture returns an error formatted to match the provider's native error format (for example, `rate_limit_error` for Anthropic, `insufficient_quota` for OpenAI). For troubleshooting details, refer to [Troubleshooting Aperture](https://tailscale.com/docs/aperture/troubleshooting).\r\n\r\n## [Next steps](https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits\\#next-steps)\r\n\r\n- [Set a team-wide budget](https://tailscale.com/docs/aperture/how-to/set-team-budget) to add an organization-level spending cap alongside per-user limits.\r\n- [Check and refill budgets](https://tailscale.com/docs/aperture/how-to/check-and-refill-budgets) to monitor bucket balances and manually add funds.\r\n- Refer to the [quotas configuration reference](https://tailscale.com/docs/aperture/configuration#quotas) for the complete field reference and additional examples.\r\n\r\n![Project Logo](https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed)\r\n\r\nAsk AI\r\n\r\nreCAPTCHA\r\n\r\nRecaptcha requires verification.\r\n\r\nprotected by **reCAPTCHA**\r\n","html":"<h1>Set per-user spending limits</h1>\n<p>Last validated: Apr 9, 2026</p>\n<p>Aperture by Tailscaleis currently <a href=\"https://tailscale.com/docs/reference/tailscale-release-stages#alpha\">in alpha</a>.</p>\n<p>When multiple people use LLMs through Aperture, a single user can consume a disproportionate share of your AI budget. Per-user spending limits give each person an independent budget that refills automatically, so no one exhausts the team's resources.</p>\n<p>Aperture uses quota buckets to enforce spending limits. Each bucket has a capacity (maximum balance), a refill rate, and a behavior when the limit is exceeded. Using the <code>&#x3C;user></code> template variable in the bucket name creates a separate bucket for each person who sends requests through Aperture.</p>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#prerequisites\">Prerequisites</a></h2>\n<p>Before you begin, ensure you have the following:</p>\n<ul>\n<li>An <a href=\"https://tailscale.com/docs/aperture/get-started\">Aperture instance</a> with at least one provider configured.</li>\n<li>Admin access to the <a href=\"https://tailscale.com/docs/aperture/configuration\">Aperture configuration</a>.</li>\n</ul>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#define-a-per-user-quota-bucket\">Define a per-user quota bucket</a></h2>\n<p>Open the <strong>Settings</strong> page in the <a href=\"https://tailscale.com/docs/aperture/reference/dashboard\">Aperture dashboard</a> and add a <code>quotas</code> section to your configuration. The following example creates a per-user bucket with a $10 capacity that refills at $5 per day:</p>\n<pre><code class=\"language-json\">\"quotas\": {\r\n  \"daily:&#x3C;user>\": {\r\n    \"capacity\": \"$10.00\",\r\n    \"rate\": \"$5.00/day\",\r\n    \"on_exceed\": \"reject\"\r\n  }\r\n}\n</code></pre>\n<p>The <code>&#x3C;user></code> template expands to each caller's Tailscale login name at request time. For example, when <code>alice@example.com</code> sends a request, Aperture charges the bucket <code>daily:alice@example.com</code>. When <code>bob@example.com</code> sends a request, Aperture charges <code>daily:bob@example.com</code>. Each bucket tracks its balance independently.</p>\n<p>The <code>capacity</code> field sets the maximum balance the bucket can hold. This is also the starting balance. The <code>rate</code> field controls how fast the bucket refills. Supported time units are <code>min</code>, <code>hour</code>, <code>day</code>, <code>week</code>, and <code>month</code> (30 days).</p>\n<p>When <code>on_exceed</code> is set to <code>\"reject\"</code>, Aperture blocks requests that would bring the bucket below zero and returns HTTP 429 with a <code>Retry-After</code> header. Currently, <code>\"reject\"</code> is the only supported value for <code>on_exceed</code>.</p>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#attach-the-quota-to-a-grant\">Attach the quota to a grant</a></h2>\n<p>A quota bucket has no effect until a grant references it. Add a <code>quotas</code> entry to the capability in your <code>grants</code> section that should enforce the limit:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"*\"],\\\r\n    \"app\": {\\\r\n      \"tailscale.com/cap/aperture\": [\\\r\n        { \"role\": \"user\" },\\\r\n        {\\\r\n          \"models\": \"**\",\\\r\n          \"quotas\": [\\\r\n            {\"bucket\": \"daily:&#x3C;user>\"}\\\r\n          ]\\\r\n        }\\\r\n      ]\\\r\n    }\\\r\n  }\\\r\n]\n</code></pre>\n<p>This grant applies the per-user quota to all models for all users. When a request matches the grant, Aperture checks the user's bucket balance before forwarding the request.</p>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#scope-a-quota-to-specific-models\">Scope a quota to specific models</a></h2>\n<p>To limit spending on expensive models while leaving others unmetered, create a quota that applies only to specific model patterns:</p>\n<pre><code class=\"language-json\">\"quotas\": {\r\n  \"opus:&#x3C;user>\": {\r\n    \"capacity\": \"$5.00\",\r\n    \"rate\": \"$2.50/day\",\r\n    \"on_exceed\": \"reject\"\r\n  }\r\n},\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"*\"],\\\r\n    \"app\": {\\\r\n      \"tailscale.com/cap/aperture\": [\\\r\n        { \"models\": \"**\" },\\\r\n        {\\\r\n          \"models\": \"*/claude-opus*\",\\\r\n          \"quotas\": [\\\r\n            {\"bucket\": \"opus:&#x3C;user>\"}\\\r\n          ]\\\r\n        }\\\r\n      ]\\\r\n    }\\\r\n  }\\\r\n]\n</code></pre>\n<p>In this configuration, requests to Opus models are metered against the <code>opus:&#x3C;user></code> bucket, while all other models have no spending limit.</p>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#verify-the-configuration\">Verify the configuration</a></h2>\n<p>After saving the configuration, confirm the quota is active:</p>\n<ol>\n<li>Send a test request through Aperture.</li>\n<li>Check the <a href=\"https://tailscale.com/docs/aperture/how-to/check-and-refill-budgets\">quota status</a> using the API (<code>GET /api/quotas</code>) or the <strong>Models</strong> page in the Aperture dashboard. The quota bucket for your user should appear with the correct capacity and a balance reduced by the cost of your test request.</li>\n</ol>\n<p>If a request exceeds the quota, Aperture returns an error formatted to match the provider's native error format (for example, <code>rate_limit_error</code> for Anthropic, <code>insufficient_quota</code> for OpenAI). For troubleshooting details, refer to <a href=\"https://tailscale.com/docs/aperture/troubleshooting\">Troubleshooting Aperture</a>.</p>\n<h2><a href=\"https://tailscale.com/docs/aperture/how-to/set-per-user-spending-limits#next-steps\">Next steps</a></h2>\n<ul>\n<li><a href=\"https://tailscale.com/docs/aperture/how-to/set-team-budget\">Set a team-wide budget</a> to add an organization-level spending cap alongside per-user limits.</li>\n<li><a href=\"https://tailscale.com/docs/aperture/how-to/check-and-refill-budgets\">Check and refill budgets</a> to monitor bucket balances and manually add funds.</li>\n<li>Refer to the <a href=\"https://tailscale.com/docs/aperture/configuration#quotas\">quotas configuration reference</a> for the complete field reference and additional examples.</li>\n</ul>\n<p><img src=\"https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed\" alt=\"Project Logo\"></p>\n<p>Ask AI</p>\n<p>reCAPTCHA</p>\n<p>Recaptcha requires verification.</p>\n<p>protected by <strong>reCAPTCHA</strong></p>\n"}