AI agents can burn through money faster than you can say "runaway loop." Without hard spending limits, a simple bug or misunderstood prompt can turn a $10 task into a $1,000 nightmare. Here's how to implement spending limits that actually work.
Most developers try to implement spending limits in their application logic:
javascript
// This approach WILL fail
let totalSpent = 0;
const BUDGET_LIMIT = 100;
function makeAgentPurchase(amount) {
if (totalSpent + amount > BUDGET_LIMIT) {
throw new Error('Budget exceeded');
}
// What happens if this fails after the check?
totalSpent += amount;
return processPurchase(amount);
}
This fails because:
Hard limits are enforced by the payment processor, not your application. When you hit the limit, the card simply stops working. No race conditions, no bypass mechanisms, no exceptions.
Here's how to implement real hard limits:
javascript
class AgentPaymentManager {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://aipaymentproxy.com/api/v1';
}
async createLimitedCard(taskName, limitUsd) {
const response = await fetch(`${this.baseUrl}/cards`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
"label": `Agent: ${taskName}`,
"limit_usd": limitUsd
})
});
if (!response.ok) {
throw new Error(`Card creation failed: ${response.statusText}`);
}
return await response.json();
}
async checkSpending(cardId) {
const response = await fetch(`${this.baseUrl}/cards/${cardId}`, {
headers: {'Authorization': `Bearer ${this.apiKey}`}
});
const card = await response.json();
return {
limit: card.limit_usd,
spent: card.spent_usd,
remaining: card.limit_usd - card.spent_usd
};
}
async destroyCard(cardId) {
await fetch(`${this.baseUrl}/cards/${cardId}`, {
method: 'DELETE',
headers: {'Authorization': `Bearer ${this.apiKey}`}
});
}
}
Pattern 1: Task-Scoped Limits
Create a new card for each specific task:
javascript
async function runShoppingAgent(shoppingList, maxBudget) {
const paymentManager = new AgentPaymentManager(process.env.API_KEY);
// Create card with hard limit for this specific task
const card = await paymentManager.createLimitedCard(
`Shopping: ${shoppingList.join(', ')}`,
maxBudget
);
try {
// Agent can only spend up to maxBudget, guaranteed
const result = await agent.shop(shoppingList, card.number);
return result;
} finally {
// Clean up - destroy the card
await paymentManager.destroyCard(card.id);
}
}
Pattern 2: Time-Based Limits
For ongoing agents, recreate cards periodically:
javascript
class DailySpendingAgent {
constructor() {
this.currentCard = null;
this.dailyLimit = 50;
}
async ensureDailyCard() {
const today = new Date().toISOString().split('T')[0];
if (!this.currentCard || this.currentCard.date !== today) {
// Destroy yesterday's card
if (this.currentCard) {
await paymentManager.destroyCard(this.currentCard.id);
}
// Create today's card with fresh limit
const card = await paymentManager.createLimitedCard(
`Daily agent ${today}`,
this.dailyLimit
);
this.currentCard = { ...card, date: today };
}
return this.currentCard;
}
}
Pattern 3: Multi-Agent Isolation
Give each agent instance its own spending limit:
javascript
class AgentCluster {
async spawnAgent(taskType, budget) {
const card = await paymentManager.createLimitedCard(
`${taskType}-${Date.now()}`,
budget
);
return new Agent({
taskType,
paymentCard: card,
onComplete: () => paymentManager.destroyCard(card.id)
});
}
}
// Usage
const cluster = new AgentCluster();
const agents = await Promise.all([
cluster.spawnAgent('research', 25),
cluster.spawnAgent('shopping', 75),
cluster.spawnAgent('booking', 200)
]);
// Each agent has isolated, hard-limited spending
Hard limits prevent overspending, but you still need visibility:
javascript
class SpendingMonitor {
async checkAllCards() {
const cards = await this.getAllActiveCards();
for (const card of cards) {
const spending = await paymentManager.checkSpending(card.id);
// Alert when approaching limit
if (spending.remaining < spending.limit * 0.2) {
await this.sendAlert(`Card ${card.label} at 80% of limit`);
}
// Log for analysis
console.log(`${card.label}: $${spending.spent}/$${spending.limit}`);
}
}
}
Even with hard limits, you need emergency stops:
javascript
class EmergencyControls {
async killAllAgentSpending() {
const activeCards = await this.getAllActiveCards();
// Destroy all cards immediately
await Promise.all(
activeCards.map(card => paymentManager.destroyCard(card.id))
);
console.log(`Emergency stop: destroyed ${activeCards.length} cards`);
}
}
1. **Floating Point Precision**: Always use integers for cents, not dollars with decimals
2. **Currency Conversion**: Factor in exchange rates for international purchases
3. **Pending Transactions**: Limits include pending charges, not just completed ones
4. **Card Creation Limits**: Some services limit how many cards you can create per hour
Hard limits cost about $1 per card but consider:
Soft limits implemented in application code will eventually fail. Hard limits enforced at the payment level are the only reliable way to control AI agent spending.
The pattern is simple: create limited cards for specific tasks, let agents operate within those hard constraints, then clean up. Your agents get the autonomy they need, and you get the spending control you require.
Get your API key and make your first card creation call in minutes.
Get API Key — Free 14-day trial