# How to Prevent Hitting Usage Quotas with LLM Calls in n8n

- Tool: n8n
- Difficulty: Advanced
- Time required: 35-45 minutes
- Compatibility: n8n 1.30+, any LLM provider (OpenAI, Anthropic, Mistral), PostgreSQL for tracking, Code node
- Last updated: March 2026

## TL;DR

LLM usage quotas (rate limits, monthly spending caps, token limits) cause n8n workflows to fail with 429 or quota-exceeded errors. Prevent this by implementing request rate limiting with the Wait node, tracking token usage in a database, setting up spending alerts before hitting hard limits, and using fallback models when your primary provider is throttled.

## Why LLM Quota Management Matters for n8n Workflows

Every LLM provider enforces usage limits: OpenAI has requests-per-minute (RPM) and tokens-per-minute (TPM) limits, Anthropic has requests-per-minute limits per model, and most providers have monthly spending caps. When your n8n workflow exceeds these limits, API calls fail with 429 (Too Many Requests) or quota-exceeded errors, breaking your automation. This tutorial shows how to build a comprehensive quota management system that tracks usage, enforces rate limits within your workflow, alerts before hitting caps, and gracefully falls back to alternative models when limits are reached.

## Before you start

- A running n8n instance (self-hosted or cloud) on version 1.30 or later
- LLM API credentials (OpenAI, Anthropic, or Mistral)
- A PostgreSQL database for usage tracking
- An email or Slack credential for alerts
- Understanding of your LLM provider's rate limits and pricing

## Step-by-step guide

### 1. Understand your provider's quota structure

Before implementing quota management, document your exact limits. OpenAI has RPM (requests per minute), TPM (tokens per minute), and RPD (requests per day) limits that vary by tier and model. Anthropic has requests per minute per model. Mistral has requests per minute and tokens per minute. Check your provider's dashboard for your current tier limits. Also note your monthly spending cap — most providers let you set one, and hitting it kills all API calls instantly.

```
// Common LLM provider limits (Tier 1 / default)
// OpenAI GPT-4o: 500 RPM, 30,000 TPM, $100/mo default cap
// OpenAI GPT-4o-mini: 500 RPM, 200,000 TPM
// Anthropic Claude 3.5 Sonnet: 50 RPM, 40,000 TPM
// Mistral Large: 30 RPM, varies by plan

// Check your actual limits:
// OpenAI: platform.openai.com → Settings → Limits
// Anthropic: console.anthropic.com → Settings → Limits
// Mistral: console.mistral.ai → Billing
```

**Expected result:** You have documented your exact RPM, TPM, and monthly spending limits for each LLM provider you use.

### 2. Implement request rate limiting with Wait node

The simplest way to avoid hitting RPM limits is to add a Wait node before your LLM call that enforces a minimum delay between requests. Calculate the delay as 60000 / RPM (e.g., for 50 RPM, wait 1200ms between requests). For batch processing with the SplitInBatches node, this is critical — without throttling, a batch of 100 items will fire 100 API calls simultaneously, instantly hitting RPM limits.

```
// Rate limiting strategy using Code node + Wait node

// Code node before LLM: calculate required delay
const staticData = $getWorkflowStaticData('global');
const RPM_LIMIT = 50; // Your provider's RPM limit
const MIN_DELAY_MS = Math.ceil(60000 / RPM_LIMIT); // 1200ms for 50 RPM

const now = Date.now();
const lastCallTime = staticData.lastLlmCallTime || 0;
const elapsed = now - lastCallTime;
const waitTime = Math.max(0, MIN_DELAY_MS - elapsed);

staticData.lastLlmCallTime = now + waitTime;

return [{
  json: {
    ...($input.first().json),
    _waitMs: waitTime
  }
}];

// Wait node after Code node:
// Wait Amount: {{ $json._waitMs }}
// Unit: Milliseconds
```

**Expected result:** Requests to the LLM are spaced at least MIN_DELAY_MS apart, preventing RPM limit violations.

### 3. Track token usage in a database

Create a PostgreSQL table to track token usage per provider, per day. After every LLM call, log the token counts from the API response. A scheduled aggregation query can then compare daily usage against your limits and trigger alerts. This also provides a historical record for cost analysis and budgeting.

```
-- PostgreSQL: Create usage tracking table
CREATE TABLE IF NOT EXISTS llm_usage_tracking (
  id SERIAL PRIMARY KEY,
  provider VARCHAR(50) NOT NULL,
  model VARCHAR(100),
  prompt_tokens INTEGER DEFAULT 0,
  completion_tokens INTEGER DEFAULT 0,
  total_tokens INTEGER DEFAULT 0,
  estimated_cost_usd NUMERIC(10, 6),
  workflow_id VARCHAR(255),
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_usage_provider_date
  ON llm_usage_tracking(provider, created_at);
```

**Expected result:** The llm_usage_tracking table exists and is ready to receive token usage data after every LLM call.

### 4. Log usage after every LLM call

Add a Code node after each LLM node that extracts token usage from the API response and calculates the estimated cost. Different providers return usage data in different formats — the Code node normalizes them. Connect the Code node output to a Postgres node that inserts the usage record. Enable 'Continue On Fail' on the Postgres node so a database error does not block the main workflow.

```
// Code node — JavaScript
// Extract and normalize token usage from LLM response

const response = $input.first().json;

// Provider-specific token extraction
let provider = 'unknown';
let model = 'unknown';
let promptTokens = 0;
let completionTokens = 0;

if (response.usage?.prompt_tokens !== undefined) {
  // OpenAI / Mistral format
  provider = response.model?.includes('gpt') ? 'openai' : 'mistral';
  model = response.model || 'unknown';
  promptTokens = response.usage.prompt_tokens;
  completionTokens = response.usage.completion_tokens;
} else if (response.usage?.input_tokens !== undefined) {
  // Anthropic format
  provider = 'anthropic';
  model = response.model || 'unknown';
  promptTokens = response.usage.input_tokens;
  completionTokens = response.usage.output_tokens;
}

// Cost estimation (approximate, per 1M tokens)
const COSTS = {
  'gpt-4o': { input: 2.50, output: 10.00 },
  'gpt-4o-mini': { input: 0.15, output: 0.60 },
  'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },
  'mistral-large-latest': { input: 2.00, output: 6.00 }
};

const pricing = COSTS[model] || { input: 5.00, output: 15.00 };
const estimatedCost = (promptTokens * pricing.input + completionTokens * pricing.output) / 1000000;

return [{
  json: {
    provider,
    model,
    prompt_tokens: promptTokens,
    completion_tokens: completionTokens,
    total_tokens: promptTokens + completionTokens,
    estimated_cost_usd: estimatedCost,
    workflow_id: $workflow.id
  }
}];
```

**Expected result:** Every LLM call's token usage and estimated cost are logged to PostgreSQL for tracking and alerting.

### 5. Set up spending alerts

Create a scheduled workflow that runs every 6 hours. It queries the usage table for the current month's total spending per provider and compares it against your spending cap. If spending exceeds 80% of the cap, it sends a warning. If spending exceeds 95%, it sends a critical alert. This gives you time to react before hitting the hard limit and having all API calls fail.

```
-- Postgres node query: Monthly spending check
SELECT
  provider,
  SUM(total_tokens) as monthly_tokens,
  SUM(estimated_cost_usd) as monthly_cost_usd,
  COUNT(*) as total_calls
FROM llm_usage_tracking
WHERE created_at >= date_trunc('month', NOW())
GROUP BY provider;

-- Code node after query: Check against limits
// const SPENDING_CAPS = {
//   openai: 100,      // $100/month
//   anthropic: 50,    // $50/month
//   mistral: 30       // $30/month
// };
// const WARNING_THRESHOLD = 0.80; // 80%
// const CRITICAL_THRESHOLD = 0.95; // 95%
```

**Expected result:** Spending alerts fire at 80% and 95% of your monthly cap, giving you time to reduce usage or increase limits.

### 6. Implement fallback models for quota exhaustion

When your primary model hits a rate limit (429 error), automatically fall back to a cheaper or less-limited model instead of failing the workflow. Use the HTTP Request node's 'Retry On Fail' combined with an error handler that switches models. For example, fall back from GPT-4o to GPT-4o-mini, or from Claude 3.5 Sonnet to Claude 3.5 Haiku. The fallback provides a degraded but functional experience while your rate limit resets.

```
// Code node — JavaScript
// Fallback model selection after rate limit error

const FALLBACK_CHAIN = [
  { provider: 'openai', model: 'gpt-4o', priority: 1 },
  { provider: 'openai', model: 'gpt-4o-mini', priority: 2 },
  { provider: 'anthropic', model: 'claude-3-5-haiku-20241022', priority: 3 }
];

const staticData = $getWorkflowStaticData('global');
const rateLimitedModels = staticData.rateLimitedModels || {};
const now = Date.now();

// Clean expired rate limit flags (reset after 60 seconds)
for (const [model, timestamp] of Object.entries(rateLimitedModels)) {
  if (now - timestamp > 60000) {
    delete rateLimitedModels[model];
  }
}

// Find first available model
const available = FALLBACK_CHAIN.find(
  m => !rateLimitedModels[m.model]
);

if (!available) {
  // All models rate limited — wait and retry primary
  return [{ json: { _waitMs: 30000, model: FALLBACK_CHAIN[0].model, isFallback: false } }];
}

return [{
  json: {
    model: available.model,
    provider: available.provider,
    isFallback: available.priority > 1,
    fallbackLevel: available.priority
  }
}];
```

**Expected result:** When the primary model is rate-limited, the workflow automatically switches to a fallback model without failing.

## Complete code example

File: `quota-manager.js`

```javascript
// Complete Code node: LLM quota manager
// Place before LLM node to enforce rate limits and select models

const staticData = $getWorkflowStaticData('global');
const now = Date.now();

// Initialize tracking
if (!staticData.quotaManager) {
  staticData.quotaManager = {
    requestLog: [],       // Timestamps of recent requests
    rateLimited: {},      // Model → timestamp of last 429
    dailyTokens: {},      // Provider → token count today
    dailyReset: now       // When to reset daily counters
  };
}

const qm = staticData.quotaManager;

// Reset daily counters at midnight
if (now - qm.dailyReset > 86400000) {
  qm.dailyTokens = {};
  qm.dailyReset = now;
}

// Clean old request log (keep last 60 seconds)
qm.requestLog = qm.requestLog.filter(ts => now - ts < 60000);

// Configuration
const RPM_LIMIT = 45; // Stay 10% under actual limit
const DAILY_TOKEN_LIMIT = 1000000;
const FALLBACKS = [
  { provider: 'openai', model: 'gpt-4o' },
  { provider: 'openai', model: 'gpt-4o-mini' },
  { provider: 'anthropic', model: 'claude-3-5-haiku-20241022' }
];

// Check RPM
const currentRPM = qm.requestLog.length;
let waitMs = 0;

if (currentRPM >= RPM_LIMIT) {
  // Wait until oldest request falls out of the window
  const oldestInWindow = Math.min(...qm.requestLog);
  waitMs = 60000 - (now - oldestInWindow) + 100; // +100ms buffer
}

// Select model (skip rate-limited ones)
let selectedModel = FALLBACKS[0];
for (const model of FALLBACKS) {
  const limitedUntil = qm.rateLimited[model.model] || 0;
  if (now > limitedUntil) {
    selectedModel = model;
    break;
  }
}

// Record this request
qm.requestLog.push(now + waitMs);

// Check daily token limit
const dailyUsed = qm.dailyTokens[selectedModel.provider] || 0;
const quotaRemaining = DAILY_TOKEN_LIMIT - dailyUsed;

return [{
  json: {
    ...$input.first().json,
    _selectedModel: selectedModel.model,
    _selectedProvider: selectedModel.provider,
    _waitMs: waitMs,
    _currentRPM: currentRPM,
    _dailyTokensUsed: dailyUsed,
    _quotaRemaining: quotaRemaining,
    _isFallback: selectedModel !== FALLBACKS[0]
  }
}];
```

## Common mistakes

- **Sending batch items to the LLM in parallel without rate limiting, instantly hitting RPM caps** — undefined Fix: Use SplitInBatches with a batch size of 1 and a Wait node between batches to enforce RPM spacing
- **Tracking only request count but not token usage, missing TPM limits** — undefined Fix: Track both requests per minute AND tokens per minute — some providers enforce both independently
- **Not setting a spending cap at the provider level, risking unlimited charges from a bug or loop** — undefined Fix: Set monthly spending limits in your OpenAI/Anthropic/Mistral dashboard immediately
- **Using the same model for all tasks regardless of complexity, wasting quota on simple tasks** — undefined Fix: Route simple tasks (classification, yes/no) to cheap models (GPT-4o-mini) and complex tasks to powerful models
- **Retrying 429 errors immediately without backing off, making the rate limit situation worse** — undefined Fix: Use exponential backoff: wait 1s, then 2s, then 4s. Or switch to a fallback model immediately.

## Best practices

- Set your n8n rate limit to 90% of your actual provider limit to leave headroom for manual API usage
- Track token usage in a database, not just static data, for historical analysis and cost reporting
- Set spending alerts at 80% of your monthly cap to give time for action
- Always set provider-side spending caps as a safety net in addition to n8n-side tracking
- Use fallback model chains so workflows degrade gracefully instead of failing completely
- Space batch processing requests with Wait nodes to stay within RPM limits
- Monitor daily and monthly token usage trends to right-size your provider tier
- Use cheaper models (GPT-4o-mini, Claude 3.5 Haiku) for low-complexity tasks to stretch your quota

## Frequently asked questions

### What happens when I hit my OpenAI spending cap?

All API calls immediately fail with a 429 error and a message about exceeding your billing limit. No requests go through until the next billing cycle or until you increase the cap in Settings → Limits on platform.openai.com.

### Can I increase my rate limits without paying more?

Yes, OpenAI automatically increases rate limits as your account ages and payment history builds. You can also request a limit increase through their support portal. Anthropic and Mistral have similar tier systems.

### How do I handle rate limits when multiple n8n workflows share the same API key?

Use a shared rate limiting mechanism: either a centralized Redis counter that all workflows check before making calls, or a shared PostgreSQL table that tracks requests per minute across all workflows.

### Is it cheaper to use one large prompt or multiple small prompts?

One large prompt is usually cheaper because you pay input token costs only once. Multiple small prompts repeat the system prompt and context in each call. However, one large prompt may hit token limits — balance cost against reliability.

### How accurate is the cost estimation in the tracking Code node?

The estimation uses published per-token pricing and is accurate for standard usage. It does not account for cached input tokens (OpenAI discount), batch API pricing, or promotional credits. Treat it as an approximation and verify against your provider's billing dashboard monthly.

### Can RapidDev help optimize LLM costs for high-volume n8n workflows?

Yes, RapidDev specializes in optimizing LLM costs for n8n workflows. Their team implements rate limiting, caching, model routing (expensive models for complex tasks, cheap models for simple ones), and token budget management. Clients typically see 40-60% cost reductions after optimization.

---

Source: https://www.rapidevelopers.com/n8n-tutorial/how-to-prevent-hitting-usage-quotas-with-llm-calls-in-n8n
© RapidDev — https://www.rapidevelopers.com/n8n-tutorial/how-to-prevent-hitting-usage-quotas-with-llm-calls-in-n8n
