← Back to API Security for AI-Assisted Development
API4:20234 min read read

Unrestricted Resource Consumption

Add rate limits, payload caps, and cost controls so APIs cannot be abused for DoS or budget exhaustion.

URL: trusteed.io/academy/api-security/unrestricted-resource-consumption

Unrestricted Resource Consumption occurs when an API doesn't limit how much CPU, memory, bandwidth, storage, or downstream cost a single request — or a single client — can consume. Without limits, a legitimate-looking request pattern can degrade service for everyone (denial of service) or generate runaway infrastructure and third-party API costs, a risk that has become significantly more expensive since AI/LLM API calls are billed per token and per request.

What Is Unrestricted Resource Consumption?

This category covers any missing ceiling on resource usage: unbounded pagination (?limit=999999999), unlimited file upload sizes, unthrottled request rates per client, unbounded query complexity (especially in GraphQL, where a deeply nested query can multiply database load exponentially), no timeout on slow or hanging operations, and — increasingly relevant — no cap on calls to metered downstream services like LLM APIs, SMS providers, or email-sending services, where an attacker triggering repeated calls directly increases your bill.

Why AI Coding Assistants Get This Wrong

Pagination, file upload, and third-party API wrapper code is almost always demonstrated without limits in tutorials, because the example is about showing the feature working, not about defending it. An assistant asked to "add pagination to this endpoint" will typically implement limit and offset as pass-through query parameters with no maximum enforced — which works perfectly in every test and demo, and becomes a resource exhaustion vector the first time someone requests limit=10000000.

Vulnerable Pattern

// No cap on page size, no timeout, no rate limit
app.get('/api/products', async (req, res) => {
  const limit = parseInt(req.query.limit) || 20; // client controls limit with no ceiling
  const products = await Product.find().limit(limit);
  res.json(products);
});

// Wrapper around a metered LLM API with no per-user budget or rate limit
app.post('/api/summarize', authenticate, async (req, res) => {
  const result = await openai.chat.completions.create({
    model: 'gpt-5',
    messages: [{ role: 'user', content: req.body.text }], // no length cap on input either
  });
  res.json(result);
});

Secure Pattern

const rateLimit = require('express-rate-limit');

const MAX_LIMIT = 100;

app.get('/api/products', async (req, res) => {
  const limit = Math.min(parseInt(req.query.limit) || 20, MAX_LIMIT); // hard ceiling
  const products = await Product.find().limit(limit).maxTimeMS(5000); // query timeout
  res.json(products);
});

const summarizeLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 20, // 20 summarization calls per user per hour
  keyGenerator: (req) => req.user.id, // per-user, not per-IP
});

app.post('/api/summarize', authenticate, summarizeLimiter, async (req, res) => {
  const MAX_INPUT_CHARS = 8000;
  if (req.body.text.length > MAX_INPUT_CHARS) {
    return res.status(413).json({ error: 'Input too long' });
  }
  const result = await Promise.race([
    openai.chat.completions.create({
      model: 'gpt-5',
      messages: [{ role: 'user', content: req.body.text }],
      max_tokens: 500, // cap output cost too
    }),
    timeout(15000), // hard timeout on the downstream call
  ]);
  res.json(result);
});

Copy-Paste Rules for Cursor, Claude Code, and Codex

---
description: Resource consumption limits — pagination, rate limiting, payload size, cost caps
alwaysApply: true
---

# Resource Consumption Rules

- Every paginated endpoint MUST enforce a maximum page size server-side,
  regardless of what the client requests (e.g., `Math.min(requested, MAX_LIMIT)`).
  Never pass a client-supplied limit directly to a query with no ceiling.
- Every endpoint accepting file uploads or large request bodies MUST enforce
  an explicit maximum size at the framework/middleware level, not just
  application logic.
- Every database query and outbound HTTP/API call MUST have an explicit
  timeout. Never leave a query or fetch call to run unbounded.
- Rate limiting MUST be applied per authenticated user/API key where
  authentication is available, not only per IP address, since IPs are
  easily rotated or shared (NAT, mobile carriers, VPNs).
- Any endpoint that calls a metered or cost-bearing downstream service (LLM
  APIs, SMS/email providers, payment processors) MUST have both a rate limit
  and an explicit cap on the size/cost of each individual call (e.g.,
  `max_tokens`, message length limits, attachment size limits).
- For GraphQL APIs, enforce query depth limiting and query complexity
  analysis — do not allow unbounded nested queries.
- When generating any endpoint that accepts a count, limit, size, or amount
  parameter from the client, always apply a server-side maximum in the same
  pass, not as a follow-up.

Verification Checklist

  • All paginated endpoints enforce a maximum page size server-side
  • File upload endpoints enforce explicit size limits at the middleware level
  • All database queries and outbound calls have explicit timeouts
  • Rate limiting is applied per authenticated identity, not IP alone
  • Metered downstream calls (LLM, SMS, email, payment) have both rate limits and per-call cost caps
  • GraphQL query depth/complexity limits are enforced if applicable
  • Load and abuse testing validates these limits hold under attack conditions — see Trusteed's WAAP platform for application-layer DDoS defense and rate limiting

Frequently asked questions

Should rate limits be per-IP or per-user?

Per-authenticated-user (or per-API-key) is more reliable than per-IP, since IP-based limiting is easily bypassed via proxy rotation, shared NAT, or mobile carrier IP pooling. Use per-IP as a secondary, coarser layer for unauthenticated endpoints only.

How does this apply specifically to AI/LLM-powered features?

LLM API calls are billed per token, so an unthrottled endpoint that wraps an LLM call is effectively an unthrottled billing endpoint. Cap both call frequency (rate limiting) and per-call size (input length, `max_tokens`) — both dimensions matter for cost control.

Does a Web Application Firewall (WAF) solve resource consumption issues on its own?

A WAF can help with layer 7 DDoS mitigation and basic rate limiting at the edge, but application-level limits (pagination ceilings, query timeouts, per-user cost caps) still need to be implemented in code — a WAF is a complementary layer, not a substitute.