PageSourceSearch

https://doc.routerly.ai/assets/js/fb4ab676.e28e7f9a.js

js routerly.ai collected 2026-10-05 14:49:04 UTC 13,197 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkwebsite=globalThis.webpackChunkwebsite||[]).push([[473],{2680(e,t,n){n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>a,frontMatter:()=>r,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"concepts/budgets-and-limits","title":"Budgets & Limits","description":"Routerly has a three-level budget hierarchy that lets you control spending at the platform, project, and individual-token levels. Budgets can be configured for any metric \u2014 cost, call count, or token usage \u2014 over a rolling or calendar window.","source":"@site/../docs/concepts/budgets-and-limits.md","sourceDirName":"concepts","slug":"/concepts/budgets-and-limits","permalink":"/next/concepts/budgets-and-limits","draft":false,"unlisted":false,"editUrl":"https://github.com/Inebrio/Routerly/edit/main/../docs/concepts/budgets-and-limits.md","tags":[],"version":"current","sidebarPosition":6,"frontMatter":{"title":"Budgets & Limits","sidebar_position":6},"sidebar":"docsSidebar","previous":{"title":"Routing","permalink":"/next/concepts/routing"},"next":{"title":"Notifications","permalink":"/next/concepts/notifications"}}');var d=n(4848),i=n(8453);const r={title:"Budgets & Limits",sidebar_position:6},o="Budgets & Limits",l={},c=[{value:"Budget Hierarchy",id:"budget-hierarchy",level:2},{value:"Budget Levels",id:"budget-levels",level:2},{value:"Global budget",id:"global-budget",level:3},{value:"Project budget",id:"project-budget",level:3},{value:"Per-token budget",id:"per-token-budget",level:3},{value:"Spend groups (org / team)",id:"spend-groups-org--team",level:3},{value:"Metrics",id:"metrics",level:2},{value:"Window Types",id:"window-types",level:2},{value:"Period windows",id:"period-windows",level:3},{value:"Rolling windows",id:"rolling-windows",level:3},{value:"Limit Modes (per-token budgets)",id:"limit-modes-per-token-budgets",level:2},{value:"Configuring Budgets",id:"configuring-budgets",level:2},{value:"Dashboard",id:"dashboard",level:3},{value:"CLI",id:"cli",level:3},{value:"What Happens When a Budget Is Exhausted",id:"what-happens-when-a-budget-is-exhausted",level:2},{value:"Notifications",id:"notifications",level:2}];function h(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(t.header,{children:(0,d.jsx)(t.h1,{id:"budgets--limits",children:"Budgets & Limits"})}),"\n",(0,d.jsx)(t.p,{children:"Routerly has a three-level budget hierarchy that lets you control spending at the platform, project, and individual-token levels. Budgets can be configured for any metric \u2014 cost, call count, or token usage \u2014 over a rolling or calendar window."}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"budget-hierarchy",children:"Budget Hierarchy"}),"\n",(0,d.jsx)(t.pre,{children:(0,d.jsx)(t.code,{children:"Global budget\n\u2514\u2500\u2500 Project budget\n    \u2514\u2500\u2500 Per-token budget\n"})}),"\n",(0,d.jsxs)(t.p,{children:["A request must pass ",(0,d.jsx)(t.strong,{children:"all"})," applicable budget checks before Routerly forwards it to a provider. If any budget is exhausted, Routerly returns ",(0,d.jsx)(t.code,{children:"503 Service Unavailable"})," with a descriptive message."]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"budget-levels",children:"Budget Levels"}),"\n",(0,d.jsx)(t.h3,{id:"global-budget",children:"Global budget"}),"\n",(0,d.jsx)(t.p,{children:"Applies to all requests across all projects. Useful for setting a hard ceiling on total platform spending."}),"\n",(0,d.jsxs)(t.p,{children:["Configure via ",(0,d.jsx)(t.strong,{children:"Dashboard \u2192 Settings \u2192 Budgets"})," or in ",(0,d.jsx)(t.code,{children:"settings.json"}),"."]}),"\n",(0,d.jsx)(t.h3,{id:"project-budget",children:"Project budget"}),"\n",(0,d.jsxs)(t.p,{children:["Applies to all requests through a specific project. Configure per project via the ",(0,d.jsx)(t.strong,{children:"General"})," tab in the project settings."]}),"\n",(0,d.jsx)(t.h3,{id:"per-token-budget",children:"Per-token budget"}),"\n",(0,d.jsxs)(t.p,{children:["Applies to requests made with a specific project token. Configured in the project's ",(0,d.jsx)(t.strong,{children:"Tokens"})," tab. Per-token limits are useful when different applications share a project and you want to isolate their spending."]}),"\n",(0,d.jsx)(t.h3,{id:"spend-groups-org--team",children:"Spend groups (org / team)"}),"\n",(0,d.jsx)(t.p,{children:"Spend groups add an org- and team-level tier on top of the model/project/token\nlimits, giving a full c
1ascade:"}),"\n",(0,d.jsx)(t.pre,{children:(0,d.jsx)(t.code,{children:"Organisation group\n\u2514\u2500\u2500 Team group\n    \u2514\u2500\u2500 Project (spendGroupId) \u2192 per-token budget\n"})}),"\n",(0,d.jsxs)(t.p,{children:["A project joins a group via its ",(0,d.jsx)(t.code,{children:"spendGroupId"}),", and groups nest via\n",(0,d.jsx)(t.code,{children:"parentGroupId"}),". Usage is attributed to a group by the projects that belong to\nit and to its descendant groups. When a request runs through a project in a\ngroup, every group in the parent chain must have budget remaining, in addition\nto the per-model/project/token checks. ",(0,d.jsx)(t.strong,{children:"Child group limits cannot exceed their\nparent's matching limit"})," \u2014 this is validated when creating or updating a group."]}),"\n",(0,d.jsxs)(t.p,{children:["Spend groups use the same ",(0,d.jsx)(t.code,{children:"Limit"})," shape (metrics, period/rolling windows) as the\nother levels. Manage them via the ",(0,d.jsx)(t.code,{children:"/api/spend-groups"})," endpoints (see the\n",(0,d.jsx)(t.a,{href:"/next/api/management#spend-groups",children:"Management API reference"}),")."]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"metrics",children:"Metrics"}),"\n",(0,d.jsx)(t.p,{children:"Each budget limit tracks one metric:"}),"\n",(0,d.jsxs)(t.table,{children:[(0,d.jsx)(t.thead,{children:(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.th,{children:"Metric"}),(0,d.jsx)(t.th,{children:"Description"})]})}),(0,d.jsxs)(t.tbody,{children:[(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"cost"})}),(0,d.jsx)(t.td,{children:"USD cost calculated from token prices"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"calls"})}),(0,d.jsx)(t.td,{children:"Total number of API requests"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"input_tokens"})}),(0,d.jsx)(t.td,{children:"Total input tokens consumed"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"output_tokens"})}),(0,d.jsx)(t.td,{children:"Total output tokens generated"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"total_tokens"})}),(0,d.jsx)(t.td,{children:"Sum of input and output tokens"})]})]})]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"window-types",children:"Window Types"}),"\n",(0,d.jsx)(t.p,{children:"Budgets reset based on the configured window type."}),"\n",(0,d.jsx)(t.h3,{id:"period-windows",children:"Period windows"}),"\n",(0,d.jsx)(t.p,{children:"Reset at the start of each calendar period:"}),"\n",(0,d.jsxs)(t.table,{children:[(0,d.jsx)(t.thead,{children:(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.th,{children:"Window"}),(0,d.jsx)(t.th,{children:"Resets"})]})}),(0,d.jsxs)(t.tbody,{children:[(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"hourly"})}),(0,d.jsx)(t.td,{children:"Top of each hour"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"daily"})}),(0,d.jsx)(t.td,{children:"Midnight (UTC)"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"weekly"})}),(0,d.jsx)(t.td,{children:"Monday midnight (UTC)"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"monthly"})}),(0,d.jsx)(t.td,{children:"1st of each month"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"yearly"})}),(0,d.jsx)(t.td,{children:"January 1st"})]})]})]}),"\n",(0,d.jsx)(t.h3,{id:"rolling-windows",children:"Rolling windows"}),"\n",(0,d.jsx)(t.p,{children:"Track usage over a sliding time window:"}),"\n",(0,d.jsxs)(t.table,{children:[(0,d.jsx)(t.thead,{children:(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.th,{children:"Window"}),(0,d.jsx)(t.th,{children:"Period"})]})}),(0,d.jsxs)(t.tbody,{children:[(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_second"})}),(0,d.jsx)(t.td,{children:"Last 1 second"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_minute"})}),(0,d.jsx)(t.td,{children:"Last 60 seconds"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_hour"})}),(0,d.jsx)(t.td,{children:"Last 3,600 seconds"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_day"})}),(0,d.jsx)(t.td,{children:"Last 86,400 seconds"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_week"})}),(0,d.jsx)(t.td,{children:"Last 7 days"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"rolling_month"})}),(0,d.jsx)(t.td,{children:"Last 30 days"})]})]})]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"limit-modes-per-token-budgets",children:"Limit Modes (per-token budgets)"}),"\n",(0,d.jsxs)(t.p,{children:["When a per-token budget is configured, the ",(0,d.jsx)(t.code,{children:"mode"})," field controls how it interacts with the parent project budget:"]}),"\n",(0,d.jsxs)(t.table,{children:[(0,d.jsx)(t.thead,{children:(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.th,{children:"Mode"}),(0,d.jsx)(t.th,{children:"Behaviour"})]})}),(0,d.jsxs)(t.tbody,{children:[(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"replace"})}),(0,d.jsx)(t.td,{children:"The per-token limit overrides the project limit entirely for this token"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"extend"})}),(0,d.jsx)(t.td,{children:"The per-token limit stacks on top of the project limit (both must pass)"})]}),(0,d.jsxs)(t.tr,{children:[(0,d.jsx)(t.td,{children:(0,d.jsx)(t.code,{children:"disable"})}),(0,d.jsx)(t.td,{children:"No budget limit for this token, regardless of project limits"})]})]})]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"configuring-budgets",children:"Configuring Budgets"}),"\n",(0,d.jsx)(t.h3,{id:"dashboard",children:"Dashboard"}),"\n",(0,d.jsxs)(t.p,{children:[(0,d.jsx)(t.strong,{children:"Project budget:"})," Open the project \u2192 ",(0,d.jsx)(t.strong,{children:"General"})," tab \u2192 Budget section.\n",(0,d.jsx)(t.strong,{children:"Per-token budget:"})," Open the project \u2192 ",(0,d.jsx)(t.strong,{children:"Tokens"})," tab \u2192 click the edit icon next to a token.\n",(0,d.jsx)(t.strong,{children:"Global budget:"})," Open ",(0,d.jsx)(t.strong,{children:"Settings \u2192 Budgets"}),"."]}),"\n",(0,d.jsx)(t.h3,{id:"cli",children:"CLI"}),"\n",(0,d.jsx)(t.p,{children:"You can set a project-level daily or monthly cost budget when adding a model to a project:"}),"\n",(0,d.jsx)(t.pre,{children:(0,d.jsx)(t.code,{className:"language-bash",children:"routerly project add-model \\\n  --slug my-app \\\n  --model gpt-5-mini \\\n  --daily-budget 5.00 \\\n  --monthly-budget 50.00\n"})}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"what-happens-when-a-budget-is-exhausted",children:"What Happens When a Budget Is Exhausted"}),"\n",(0,d.jsxs)(t.ul,{children:["\n",(0,d.jsxs)(t.li,{children:["Routerly returns ",(0,d.jsx)(t.strong,{children:"HTTP 503"})," with a JSON error body:","\n",(0,d.jsx)(t.pre,{children:(0,d.jsx)(t.code,{className:"language-json",children:'{"error":"budget_exceeded","message":"Monthly cost limit for project \'my-app\' reached ($50.00)"}\n'})}),"\n"]}),"\n",(0,d.jsx)(t.li,{children:"The response is immediate \u2014 no provider API call is made."}),"\n",(0,d.jsx)(t.li,{children:"Once the budget window resets (e.g. at the start of next month), requests are accepted again automatically."}),"\n"]}),"\n",(0,d.jsx)(t.hr,{}),"\n",(0,d.jsx)(t.h2,{id:"notifications",children:"Notifications"}),"\n",(0,d.jsxs)(t.p,{children:["You can configure Routerly to send an alert when a budget reaches a configured threshold (e.g. 80% used) or when it is exhausted. See ",(0,d.jsx)(t.a,{href:"/next/concepts/notifications",children:"Concepts: Notifications"})," for setup."]})]})}function a(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,d.jsx)(t,{...e,children:(0,d.jsx)(h,{...e})}):h(e)}},8453(e,t,n){n.d(t,{R:()=>r,x:()=>o});var s=n(6540);const d={},i=s.createContext(d);function r(e){const t=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(d):e.components||d:r(e.components),s.createElement(i.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.