# Spend limits

Budgets and spend limits can be attached to multiple levels to enforce your spending policies and rules. Limits can be added at the organization, API keys, users, apps and teams. Each limit has a daily, weekly and monthly window and is defined in USD. Limits and teams can be managed directly from the dashboard or via the API.

Amounts are exact USD in every SDK, never a float.

## Setup

The examples below use `api` and `limits` from here. Every client reads `STREAM_API_KEY` and `STREAM_API_SECRET` from the environment, and spend limits are server side only.

```ts label="JavaScript"
import { Client, USD } from "@stream-io/calypso";

const api = new Client();
const limits = api.spendLimits;
```

```python label="Python"
from vision_agents.plugins import stream

api = stream.Client()
limits = api.spend_limits
```

```ruby label="Ruby"
require "getstream/vision_agents"

api = GetStream::VisionAgents::Client.new
limits = api.spend_limits
```

```php label="PHP"
use GetStream\VisionAgents\{Backend, Client, Usd};
use GetStream\VisionAgents\Generated\{
    LimitInterval,
    LimitSubject,
};

$api = new Client();
$limits = $api->spendLimits;
```

```go label="Go"
api, _ := client.New(stream.Backend{})
limits := api.SpendLimits()
```

```csharp label="C#"
using GetStream.VisionAgents;

using var api = new VisionAgentsClient();
var limits = api.SpendLimits;
```

```rust label="Rust"
use vision_agents::types::{
    CreateSessionRequest, LimitInterval, LimitSubject,
};
use vision_agents::{
    Client, ClientOptions, Limit, LimitFilter, Reset,
    TeamLimit, Tools, Usd,
};

let api = Client::from_env()?;
let limits = api.spend_limits();
```

## Organization limits

```ts label="JavaScript"
// The organization's ceiling
await limits.create({
  subject: "organization",
  interval: "monthly",
  amount: USD.dollars(20_000),
});
```

```python label="Python"
# The organization's ceiling
await limits.create(
    "organization",
    interval="monthly",
    usd=20_000,
)
```

```ruby label="Ruby"
# The organization's ceiling
limits.create(:organization, interval: :monthly, amount: 20_000)
```

```php label="PHP"
// The organization's ceiling
$limits->create(
    LimitSubject::Organization,
    LimitInterval::Monthly,
    Usd::dollars(20_000),
);
```

```go label="Go"
// The organization's ceiling
limits.Create(ctx, client.Limit{
	Subject:  client.Organization,
	Interval: client.Monthly,
	Amount:   20_000 * client.Dollar,
})
```

```csharp label="C#"
// The organization's ceiling
await limits.CreateAsync(new LimitOptions
{
    Subject = LimitSubject.Organization,
    Interval = LimitInterval.Monthly,
    Amount = Usd.Dollars(20_000),
});
```

```rust label="Rust"
// The organization's ceiling
let ceiling = Limit::new(
    LimitSubject::Organization,
    LimitInterval::Monthly,
    Usd::dollars(20_000),
);
limits.create(&ceiling).await?;
```

## API key limits

```ts label="JavaScript"
// Default: every key of app 1234 may spend $50 a day
await limits.create({
  subject: "api_key",
  appId: "1234",
  interval: "daily",
  amount: USD.dollars(50),
});

// One key gets its own limit, replacing the default
await limits.create({
  subject: "api_key",
  appId: "1234",
  apiKey: "mz7kq2xa9b",
  interval: "daily",
  amount: USD.dollars(10),
  note: "CI key",
});

// Later: raise it. Find the row first if you lack its id
const [found] = await limits.list({
  subject: "api_key",
  apiKey: "mz7kq2xa9b",
  interval: "daily",
});
await limits.setAmount(found!.id, USD.dollars(25));

// Delete it: the key falls back to the $50 default
await limits.delete(found!.id);
```

```python label="Python"
# Default: every key of app 1234 may spend $50 a day
await limits.create(
    "api_key",
    app_id="1234",
    interval="daily",
    usd=50,
)

# One key gets its own limit, replacing the default
await limits.create(
    "api_key",
    app_id="1234",
    api_key="mz7kq2xa9b",
    interval="daily",
    usd=10,
    note="CI key",
)

# Later: raise it. Find the row first if you lack its id
[found] = await limits.list(
    subject="api_key",
    api_key="mz7kq2xa9b",
    interval="daily",
)
await limits.update(found.id, usd=25)

# Delete it: the key falls back to the $50 default
await limits.delete(found.id)
```

```ruby label="Ruby"
# Default: every key of app 1234 may spend $50 a day
limits.create(
  :api_key,
  app_id: "1234",
  interval: :daily,
  amount: 50,
)

# One key gets its own limit, replacing the default
limits.create(
  :api_key,
  app_id: "1234",
  api_key: "mz7kq2xa9b",
  interval: :daily,
  amount: 10,
  note: "CI key",
)

# Later: raise it. Find the row first if you lack its id
found = limits.list(
  subject: :api_key,
  api_key: "mz7kq2xa9b",
  interval: :daily,
).first
limits.set_amount(found["id"], 25)

# Delete it: the key falls back to the $50 default
limits.delete(found["id"])
```

```php label="PHP"
// Default: every key of app 1234 may spend $50 a day
$limits->create(
    LimitSubject::ApiKey,
    LimitInterval::Daily,
    Usd::dollars(50),
    appId: '1234',
);

// One key gets its own limit, replacing the default
$limits->create(
    LimitSubject::ApiKey,
    LimitInterval::Daily,
    Usd::dollars(10),
    appId: '1234',
    apiKey: 'mz7kq2xa9b',
    note: 'CI key',
);

// Later: raise it. Find the row first if you lack its id
$found = $limits->list(
    LimitSubject::ApiKey,
    interval: LimitInterval::Daily,
    apiKey: 'mz7kq2xa9b',
)[0];
$limits->setAmount($found->id, Usd::dollars(25));

// Delete it: the key falls back to the $50 default
$limits->delete($found->id);
```

```go label="Go"
// Default: every key of app 1234 may spend $50 a day
limits.Create(ctx, client.Limit{
	Subject:  client.APIKey,
	AppID:    "1234",
	Interval: client.Daily,
	Amount:   50 * client.Dollar,
})

// One key gets its own limit, replacing the default
limits.Create(ctx, client.Limit{
	Subject:  client.APIKey,
	AppID:    "1234",
	APIKey:   "mz7kq2xa9b",
	Interval: client.Daily,
	Amount:   10 * client.Dollar,
	Note:     "CI key",
})

// Later: raise it. Find the row first if you lack its id
found, _ := limits.List(ctx, client.LimitFilter{
	Subject:  client.APIKey,
	APIKey:   "mz7kq2xa9b",
	Interval: client.Daily,
})
limits.SetAmount(ctx, found[0].Id, 25*client.Dollar)

// Delete it: the key falls back to the $50 default
limits.Delete(ctx, found[0].Id)
```

```csharp label="C#"
// Default: every key of app 1234 may spend $50 a day
await limits.CreateAsync(new LimitOptions
{
    Subject = LimitSubject.ApiKey,
    AppId = "1234",
    Interval = LimitInterval.Daily,
    Amount = Usd.Dollars(50),
});

// One key gets its own limit, replacing the default
await limits.CreateAsync(new LimitOptions
{
    Subject = LimitSubject.ApiKey,
    AppId = "1234",
    ApiKey = "mz7kq2xa9b",
    Interval = LimitInterval.Daily,
    Amount = Usd.Dollars(10),
    Note = "CI key",
});

// Later: raise it. Find the row first if you lack its id
var rows = await limits.ListAsync(new LimitQuery
{
    Subject = LimitSubject.ApiKey,
    ApiKey = "mz7kq2xa9b",
    Interval = LimitInterval.Daily,
});
var found = rows[0];
await limits.SetAmountAsync(found.Id, Usd.Dollars(25));

// Delete it: the key falls back to the $50 default
await limits.DeleteAsync(found.Id);
```

```rust label="Rust"
// Default: every key of app 1234 may spend $50 a day
let fallback = Limit::new(
    LimitSubject::ApiKey,
    LimitInterval::Daily,
    Usd::dollars(50),
)
.app_id("1234");
limits.create(&fallback).await?;

// One key gets its own limit, replacing the default
let ci_key = Limit::new(
    LimitSubject::ApiKey,
    LimitInterval::Daily,
    Usd::dollars(10),
)
.app_id("1234")
.api_key("mz7kq2xa9b")
.note("CI key");
limits.create(&ci_key).await?;

// Later: raise it. Find the row first if you lack its id
let found = limits
    .list(&LimitFilter {
        subject: Some(LimitSubject::ApiKey),
        api_key: Some("mz7kq2xa9b".into()),
        interval: Some(LimitInterval::Daily),
        ..Default::default()
    })
    .await?;
limits.set_amount(found[0].id, Usd::dollars(25)).await?;

// Delete it: the key falls back to the $50 default
limits.delete(found[0].id).await?;
```

## User limits

```ts label="JavaScript"
// Default: every user of app 1234 may spend $20 a day
await limits.create({
  subject: "user",
  appId: "1234",
  interval: "daily",
  amount: USD.dollars(20),
});

// A custom limit for one user, replacing the default
await limits.create({
  subject: "user",
  appId: "1234",
  userId: "user-42",
  interval: "daily",
  amount: USD.dollars(50),
});
```

```python label="Python"
# Default: every user of app 1234 may spend $20 a day
await limits.create(
    "user",
    app_id="1234",
    interval="daily",
    usd=20,
)

# A custom limit for one user, replacing the default
await limits.create(
    "user",
    app_id="1234",
    user_id="user-42",
    interval="daily",
    usd=50,
)
```

```ruby label="Ruby"
# Default: every user of app 1234 may spend $20 a day
limits.create(
  :user,
  app_id: "1234",
  interval: :daily,
  amount: 20,
)

# A custom limit for one user, replacing the default
limits.create(
  :user,
  app_id: "1234",
  user_id: "user-42",
  interval: :daily,
  amount: 50,
)
```

```php label="PHP"
// Default: every user of app 1234 may spend $20 a day
$limits->create(
    LimitSubject::User,
    LimitInterval::Daily,
    Usd::dollars(20),
    appId: '1234',
);

// A custom limit for one user, replacing the default
$limits->create(
    LimitSubject::User,
    LimitInterval::Daily,
    Usd::dollars(50),
    appId: '1234',
    userId: 'user-42',
);
```

```go label="Go"
// Default: every user of app 1234 may spend $20 a day
limits.Create(ctx, client.Limit{
	Subject:  client.User,
	AppID:    "1234",
	Interval: client.Daily,
	Amount:   20 * client.Dollar,
})

// A custom limit for one user, replacing the default
limits.Create(ctx, client.Limit{
	Subject:  client.User,
	AppID:    "1234",
	UserID:   "user-42",
	Interval: client.Daily,
	Amount:   50 * client.Dollar,
})
```

```csharp label="C#"
// Default: every user of app 1234 may spend $20 a day
await limits.CreateAsync(new LimitOptions
{
    Subject = LimitSubject.User,
    AppId = "1234",
    Interval = LimitInterval.Daily,
    Amount = Usd.Dollars(20),
});

// A custom limit for one user, replacing the default
await limits.CreateAsync(new LimitOptions
{
    Subject = LimitSubject.User,
    AppId = "1234",
    UserId = "user-42",
    Interval = LimitInterval.Daily,
    Amount = Usd.Dollars(50),
});
```

```rust label="Rust"
// Default: every user of app 1234 may spend $20 a day
let fallback = Limit::new(
    LimitSubject::User,
    LimitInterval::Daily,
    Usd::dollars(20),
)
.app_id("1234");
limits.create(&fallback).await?;

// A custom limit for one user, replacing the default
let user_42 = Limit::new(
    LimitSubject::User,
    LimitInterval::Daily,
    Usd::dollars(50),
)
.app_id("1234")
.user_id("user-42");
limits.create(&user_42).await?;
```

## Team limits

```ts label="JavaScript"
// Finance across the organization: one row per interval,
// each created or updated
const teams = api.teams;
await teams.setLimit("finance", {
  interval: "monthly",
  amount: USD.dollars(8_000),
});
await teams.setLimit("finance", {
  interval: "weekly",
  amount: USD.dollars(2_500),
});
await teams.setLimit("finance", {
  interval: "daily",
  amount: USD.dollars(600),
});
```

```python label="Python"
# Finance across the organization: one row per interval,
# each created or updated
teams = api.teams
await teams.set_limit("finance", interval="monthly", usd=8_000)
await teams.set_limit("finance", interval="weekly", usd=2_500)
await teams.set_limit("finance", interval="daily", usd=600)
```

```ruby label="Ruby"
# Finance across the organization: one row per interval,
# each created or updated
teams = api.teams
teams.set_limit("finance", interval: :monthly, amount: 8_000)
teams.set_limit("finance", interval: :weekly, amount: 2_500)
teams.set_limit("finance", interval: :daily, amount: 600)
```

```php label="PHP"
// Finance across the organization: one row per interval,
// each created or updated
$teams = $api->teams;
$teams->setLimit(
    'finance',
    LimitInterval::Monthly,
    Usd::dollars(8_000),
);
$teams->setLimit(
    'finance',
    LimitInterval::Weekly,
    Usd::dollars(2_500),
);
$teams->setLimit(
    'finance',
    LimitInterval::Daily,
    Usd::dollars(600),
);
```

```go label="Go"
// Finance across the organization: one row per interval,
// each created or updated
teams := api.Teams()
teams.SetLimit(ctx, client.TeamLimit{
	Team:     "finance",
	Interval: client.Monthly,
	Amount:   8_000 * client.Dollar,
})
teams.SetLimit(ctx, client.TeamLimit{
	Team:     "finance",
	Interval: client.Weekly,
	Amount:   2_500 * client.Dollar,
})
teams.SetLimit(ctx, client.TeamLimit{
	Team:     "finance",
	Interval: client.Daily,
	Amount:   600 * client.Dollar,
})
```

```csharp label="C#"
// Finance across the organization: one row per interval,
// each created or updated
await api.Teams.SetLimitAsync(new TeamLimit
{
    Team = "finance",
    Interval = LimitInterval.Monthly,
    Amount = Usd.Dollars(8_000),
});
await api.Teams.SetLimitAsync(new TeamLimit
{
    Team = "finance",
    Interval = LimitInterval.Weekly,
    Amount = Usd.Dollars(2_500),
});
await api.Teams.SetLimitAsync(new TeamLimit
{
    Team = "finance",
    Interval = LimitInterval.Daily,
    Amount = Usd.Dollars(600),
});
```

```rust label="Rust"
// Finance across the organization: one row per interval,
// each created or updated
let teams = api.teams();
for (interval, amount) in [
    (LimitInterval::Monthly, Usd::dollars(8_000)),
    (LimitInterval::Weekly, Usd::dollars(2_500)),
    (LimitInterval::Daily, Usd::dollars(600)),
] {
    let limit = TeamLimit::new("finance", interval, amount);
    teams.set_limit(&limit).await?;
}
```

To associate an agent with a team, add the `team: finance` tag to its `agents.yaml` file. This is the same for every SDK.

```yaml title="agents.yaml"
name: my-agent
mode: text
llm: llm-fast

# associate this agent to the finance team
tags:
  team: finance
```

### Associate sessions to a team

You can attribute a session's usage to a team with the team field when creating it. It is server side only: a team signed into the user's token always wins, then the session's team, then the agent's tag.

```ts label="JavaScript"
const agent = api.agent("my-agent");
const session = await agent.sessions.create({ team: "finance" });
```

```python label="Python"
agent = api.agent("my-agent")
session = await agent.sessions.create(
    stream.SessionOptions(team="finance"),
)
```

```ruby label="Ruby"
agent = api.agent("my-agent")
session = agent.sessions.create(team: "finance")
```

```php label="PHP"
$agent = $api->agent('my-agent');
$session = $agent->sessions->create(team: 'finance');
```

```go label="Go"
agent := api.Agent("my-agent")
session, err := agent.Sessions.Create(ctx, client.SessionOptions{
	Team: "finance",
})
```

```csharp label="C#"
var agent = api.Agent("my-agent");
var session = await agent.ChatAsync(new SessionOptions
{
    Team = "finance",
});
```

```rust label="Rust"
let request = CreateSessionRequest {
    team: Some("finance".into()),
    ..Default::default()
};
let session = api
    .agent("my-agent")
    .sessions
    .create(request, Tools::new())
    .await?;
```

### Router usage

If you use the router directly, without an agent or session, you can still pass the team to the client constructor as an option. Everything that client does counts under the team.

```ts label="JavaScript"
const finance = new Client({ team: "finance" });
const router = finance.router({ config: "dentist_ai" });
```

```python label="Python"
from vision_agents.plugins import stream

finance = stream.Client(team="finance")
router = finance.router("dentist_ai")
```

```ruby label="Ruby"
finance = GetStream::VisionAgents::Client.new(team: "finance")
router = finance.router("dentist_ai")
```

```php label="PHP"
$finance = new Client(new Backend(team: 'finance'));
$router = $finance->router('dentist_ai');
```

```go label="Go"
routers, _ := stream.NewClient(stream.Backend{Team: "finance"})
router := routers.Router("dentist_ai")
```

```csharp label="C#"
using var finance = new VisionAgentsClient(new VisionAgentsOptions
{
    Team = "finance",
});
var router = finance.Router("dentist_ai");
```

```rust label="Rust"
let finance = Client::new(ClientOptions {
    team: Some("finance".into()),
    ..Default::default()
})?;
let router = finance.router("dentist_ai");
```

## Reset limits

You can use the API to reset usage for users and teams.

```ts label="JavaScript"
// Reset the weekly window for one user.
// Leave intervals out to reset every window
await limits.reset({
  subject: "user",
  appId: "1234",
  userId: "user-42",
  intervals: ["weekly"],
  reason: "refunded a broken session",
});

// Only today's window, for team finance
await limits.reset({
  subject: "team",
  team: "finance",
  intervals: ["daily"],
  reason: "a batch job ran twice",
});
```

```python label="Python"
# Reset the weekly window for one user.
# Leave intervals out to reset every window
await limits.reset(
    "user",
    app_id="1234",
    user_id="user-42",
    intervals=["weekly"],
    reason="refunded a broken session",
)

# Only today's window, for team finance
await limits.reset(
    "team",
    team="finance",
    intervals=["daily"],
    reason="a batch job ran twice",
)
```

```ruby label="Ruby"
# Reset the weekly window for one user.
# Leave intervals out to reset every window
limits.reset(
  :user,
  app_id: "1234",
  user_id: "user-42",
  intervals: [:weekly],
  reason: "refunded a broken session",
)

# Only today's window, for team finance
limits.reset(
  :team,
  team: "finance",
  intervals: [:daily],
  reason: "a batch job ran twice",
)
```

```php label="PHP"
// Reset the weekly window for one user.
// Leave the intervals out to reset every window
$limits->reset(
    LimitSubject::User,
    'refunded a broken session',
    [LimitInterval::Weekly],
    appId: '1234',
    userId: 'user-42',
);

// Only today's window, for team finance
$limits->reset(
    LimitSubject::Team,
    'a batch job ran twice',
    [LimitInterval::Daily],
    team: 'finance',
);
```

```go label="Go"
// Reset the weekly window for one user.
// Leave Intervals out to reset every window
limits.Reset(ctx, client.Reset{
	Subject:   client.User,
	AppID:     "1234",
	UserID:    "user-42",
	Intervals: []acceleration.LimitInterval{client.Weekly},
	Reason:    "refunded a broken session",
})

// Only today's window, for team finance
limits.Reset(ctx, client.Reset{
	Subject:   client.Team,
	Team:      "finance",
	Intervals: []acceleration.LimitInterval{client.Daily},
	Reason:    "a batch job ran twice",
})
```

```csharp label="C#"
// Reset the weekly window for one user.
// Leave Intervals out to reset every window
await limits.ResetAsync(new LimitReset
{
    Subject = LimitSubject.User,
    AppId = "1234",
    UserId = "user-42",
    Intervals = [LimitInterval.Weekly],
    Reason = "refunded a broken session",
});

// Only today's window, for team finance
await limits.ResetAsync(new LimitReset
{
    Subject = LimitSubject.Team,
    Team = "finance",
    Intervals = [LimitInterval.Daily],
    Reason = "a batch job ran twice",
});
```

```rust label="Rust"
// Reset the weekly window for one user.
// Leave intervals out to reset every window
let user = Reset::new(
    LimitSubject::User,
    "refunded a broken session",
)
.app_id("1234")
.user_id("user-42")
.intervals([LimitInterval::Weekly]);
limits.reset(&user).await?;

// Only today's window, for team finance
let team = Reset::new(LimitSubject::Team, "a batch job ran twice")
    .team("finance")
    .intervals([LimitInterval::Daily]);
limits.reset(&team).await?;
```

---

For the most recent version of this documentation, visit [https://getstream.io/agents/docs/javascript/spend-limits/](https://getstream.io/agents/docs/javascript/spend-limits/).