> For the complete documentation index, see [llms.txt](https://govplane.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://govplane.gitbook.io/docs/documentation/resources/your-api-is-being-abused-heres-how-to-stop-it-in-real-time.md).

# Your API Is Being Abused — Here’s How to Stop It in Real Time

<figure><img src="/files/GV3kSdKqp1eMZwvS8Cjc" alt=""><figcaption></figcaption></figure>

APIs don’t fail loudly at first. They degrade. Latency creeps up, error rates fluctuate, and infrastructure costs start climbing before anyone notices a clear incident. By the time alerts fire, the root cause is often already in motion: uncontrolled API usage.

This rarely looks like a single, obvious attack. More often, it’s a mix of behaviors—automated login attempts, scraping activity, misconfigured clients, or simply traffic patterns that your system wasn’t designed to handle at scale. The common thread is not malicious intent alone, but lack of control at runtime.

Most systems rely on static rate limiting to deal with this. Limits are defined in API gateways, reverse proxies, or embedded directly in application code. That works as a baseline, but it assumes that the conditions under which those limits were defined will remain stable. In practice, they don’t.

***

**Don't have a Govplane account yet? Get started right now for free!**

<a href="https://app.govplane.com/signup" class="button primary">Start for free now →</a>

***

### The Limits of Static Rate Limiting

Static rate limiting is simple by design. You define a threshold—say, 100 requests per minute per IP—and the system enforces it. This is effective for predictable, uniform traffic, but it breaks down when behavior becomes more nuanced.

For example, an attacker distributing requests across thousands of IP addresses will never hit a per-IP limit. A partner integration scaling faster than expected might unintentionally trigger your limits and degrade its own functionality. Meanwhile, legitimate users may be affected simply because they share infrastructure or geography with abusive traffic.

**The deeper issue is not that rate limiting is wrong, but that it is too rigid.** When something changes in production, adapting requires updating configuration, redeploying services, or modifying infrastructure rules. That introduces delay, and delay is exactly what you can’t afford when your system is under pressure.

***

### Introducing Runtime Control

A more effective approach is to move control out of static configuration and into runtime decision-making.

Instead of defining behavior once and hoping it holds, you define policies that are evaluated on every request. These policies determine, in real time, whether a request should be allowed, denied, or throttled.

**What changes here is not the actions themselves, but the timing and flexibility. Decisions are no longer tied to deployments. They become part of the system’s execution flow.**

**This allows you to adjust behavior immediately as conditions evolve, without touching your codebase or redeploying infrastructure.**

***

### A Real Scenario: Login Abuse Under Load

Consider a SaaS application with a /login endpoint. At some point, you begin to observe a sharp increase in traffic. Requests are coming in at a much higher rate than usual, and many of them are repeated attempts on the same accounts.

A traditional rate limiter might restrict requests per IP address, but this quickly proves insufficient. Attackers rotate IPs, distribute traffic, and avoid triggering simple thresholds. At the same time, legitimate users may still be trying to log in, and you don’t want to block them unnecessarily.

**What you need is not just a limit, but context-aware control.** You want to consider multiple dimensions at once: the endpoint being accessed, the user account targeted, and the overall pattern of requests.

A runtime policy allows you to express exactly that.

***

### Example: A Policy for Login Protection

Below is a ready-to-import example of how such a policy might look:

```json
{
 "version": "1.0",
 "policies": [
      {
        "policyKey": "login-short-example",
        "status": "active",
        "snapshot": {
          "defaults": {
            "effect": "allow"
          },
          "rules": [
            {
              "id": "deny-blocked-ip",
              "effect": "deny",
              "condition": {
                "all": [
                  {
                    "equals": {
                      "target.service": "api"
                    }
                  },
                  {
                    "equals": {
                      "target.resource": "login"
                    }
                  },
                  {
                    "equals": {
                      "target.action": "authenticate"
                    }
                  },
                  {
                    "in": {
                      "context.ip": ["203.0.113.10", "198.51.100.7"]
                    }
                  }
                ]
              }
            },
            {
              "id": "throttle-by-user",
              "effect": "throttle",
              "condition": {
                "all": [
                  {
                    "equals": {
                      "target.service": "api"
                    }
                  },
                  {
                    "equals": {
                      "target.resource": "login"
                    }
                  },
                  {
                    "equals": {
                      "target.action": "authenticate"
                    }
                  },
                  {
                    "present": "context.userId"
                  }
                ]
              },
              "throttle": {
                "limit": 5,
                "windowSeconds": 60,
                "key": "user"
              }
            },
            {
              "id": "throttle-by-ip",
              "effect": "throttle",
              "condition": {
                "all": [
                  {
                    "equals": {
                      "target.service": "api"
                    }
                  },
                  {
                    "equals": {
                      "target.resource": "login"
                    }
                  },
                  {
                    "equals": {
                      "target.action": "authenticate"
                    }
                  }
                ]
              },
              "throttle": {
                "limit": 10,
                "windowSeconds": 60,
                "key": "ip"
              }
            }
          ]
        }
      }
  ]
}
```

This policy goes beyond simple rate limiting by combining blocking and throttling strategies within the same runtime evaluation model.

It introduces three layers of control:

* A deny rule that immediately blocks known abusive IPs before any further processing
* A per-IP throttle that mitigates high-volume traffic and generic flooding attempts
* A per-user throttle that targets more sophisticated patterns such as credential stuffing or repeated login attempts on specific accounts.&#x20;

The important detail is how these rules are evaluated. Decisions are made at runtime using request context (IP, user ID, endpoint), and the outcome is deterministic: the same input will always produce the same result.

Because the policy is externalized, it can evolve independently from your application. You can introduce new deny rules, adjust limits, or refine conditions without redeploying services, allowing you to react immediately to changing traffic patterns.

***

### Example: A basic implementation in Node.js

The following simplified example focuses on the enforcement flow.

The service evaluates the request against the active policy and receives a decision. If the result is deny, the request is immediately rejected. If it is throttle, the service applies the limit locally using the parameters returned by the policy.

Even in this minimal form, the key idea remains the same: the policy defines the behavior, and the application enforces it in real time.

```javascript
const result = client.evaluate({
  target: {
    service: "api",
    resource: "login",
    action: "authenticate",
  },
  context: {
    ip: req.ip,
    userId: req.currentUser.username,
  },
});

if (result.decision === "deny") {
  return reply.status(403).send({ error: "Forbidden" });
}

if (result.decision === "throttle") {
  const { limit, windowSeconds, key } = result.throttle;
  const bucketValue = key === "user" ? req.body?.email?.toLowerCase() : req.ip;
  const bucketKey = `${key}:${bucketValue}`;

  // Note: You'll need to create a function that checks, keeps track of requests and  
  // uses dynamic parameters the governance plane to determine the action to be taken.
  if (!checkThrottle(limit, windowSeconds, bucketKey)) {
    return reply
      .status(429)
      .header("Retry-After", String(windowSeconds))
      .send({ error: "Too Many Requests", limit, windowSeconds });
  }
}
```

***

**Don't have a Govplane account yet? Get started right now for free!**

<a href="https://app.govplane.com/signup" class="button primary">Start for free now →</a>

***

### How Evaluation Works

At runtime, each incoming request is evaluated against the active policy snapshot. The system checks the defined rules locally (no network dependency), evaluates their conditions against the request context, and produces a single outcome.

The key property here is determinism. Given the same request and the same policy, the result will always be the same. This makes the system predictable and debuggable, even though behavior can change dynamically over time.

There is no ambiguity or hidden state. Policies are explicit, and their effects are consistent.

***

### Why Local Evaluation Matters

Where policies are evaluated is just as important as how.

If every decision depends on an external service, you introduce latency and potential points of failure. Under load, this can become a bottleneck or even a single point of failure.

A more robust design evaluates policies locally, within the service itself or at the edge. Policies are distributed ahead of time, and decisions are made without any external calls.

This has several practical benefits. Latency remains stable because there are no network dependencies. The system continues to function even if the control plane is unavailable. And performance remains predictable under high load, since decision-making is contained within the request lifecycle.

***

### Static vs Runtime Control

The difference between traditional rate limiting and runtime policies becomes clearer when you consider how each responds to change.

Static rate limiting is configured once and enforced uniformly. It is simple and effective for baseline protection, but difficult to adapt. Runtime policies, on the other hand, are designed to evolve. They allow you to incorporate context, combine multiple signals, and adjust behavior without redeployments.

This is less about adding complexity and more about moving control to the right place—closer to where decisions actually need to be made.

***

### Why This Matters in Practice

From a scalability perspective, uncontrolled API usage translates directly into cost and instability. Excess traffic consumes compute resources, stresses databases, and can degrade performance for all users. Runtime policies allow you to enforce limits before these effects cascade through your system.

From a security perspective, predictability is a weakness. Static defenses can be studied and bypassed. When behavior can be adjusted dynamically, it becomes much harder for abusive actors to rely on fixed patterns.

In both cases, the ability to respond immediately—without redeploying—is what makes the difference.

***

### Where Govplane Fits

Govplane is built around this idea of runtime control. It provides a way to define policies centrally and distribute them to your services, where they are evaluated locally.

This approach separates control logic from application code. Instead of scattering rate limits and conditional checks across multiple services, you define them once and apply them consistently.

It’s not a replacement for your existing infrastructure, but an additional layer that gives you operational control over behavior in production.

***

### Final Thoughts

If your API is exposed, it will be used in ways you didn’t anticipate. Some of that usage will be benign, some of it won’t, but all of it will test the assumptions you made when defining your limits.

Static rate limiting provides a starting point, but it doesn’t give you the flexibility to respond when those assumptions break.

Runtime policies fill that gap. They allow you to adjust behavior as your system runs, using the full context of each request, without tying those decisions to deployments.

If you’re interested in trying this approach, Govplane offers a free plan and an extended trial. It’s enough to implement real policies and observe how your system behaves when control is no longer static, but part of runtime execution.

***

**Don't have a Govplane account yet? Get started right now for free!**

<a href="https://app.govplane.com/signup" class="button primary">Start for free now →</a>

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://govplane.gitbook.io/docs/documentation/resources/your-api-is-being-abused-heres-how-to-stop-it-in-real-time.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
