VettiGuard

Rate Limiting as a Service

Browse the complete reference in the browser, copy request examples, and follow the documented validation contract without opening packaged Markdown files.

Complete browser reference

Rate Limiting as a Service

VettiGuard Rate Limiting protects backend and mobile operations with atomic check-and-consume decisions. It combines token-bucket traffic control for short bursts with optional fixed-window quotas for longer usage caps.

Use it immediately before the protected work. Your application sends the action and only the scope identifiers required by your policy; VettiGuard evaluates every matching policy, consumes capacity atomically when appropriate, and returns an allow, throttle, or reject decision.

Keep VettiGuard private secrets on a trusted backend. Native mobile apps should call your backend where practical; if you use the native VettiGuard operation directly, use the registered mobile secret and application identity exactly as documented below.

When to use Rate Limiting

Rate Limiting is appropriate when you need to control how frequently an operation can happen.

Common examples include:

  • Login, registration, password-reset, and OTP requests.
  • Payment, transfer, checkout, and account-change APIs.
  • Search, catalog, scraping-sensitive, or enumeration-sensitive endpoints.
  • Expensive report or export initiation.
  • Public APIs consumed by API keys or authenticated subjects.
  • Mobile operations that should be bounded per installation, subject, or action.

For operations where the problem is the number of jobs executing simultaneously, use Concurrency Control. For unhealthy or saturated downstream providers, use Dependency Resilience.

How the decision works

A matching Rate Limiting policy can combine:

ControlPurpose
Token bucketAllows normal bursts while limiting sustained request rate.
Fixed quotaCaps usage over a minute, hour, day, or month.
Weighted costLets expensive operations consume more than one token.
Adaptive costCan increase token cost from a fresh authoritative API Protection assessment.
Hierarchical policiesApplies every matching workspace, application, route, subject, device, or custom-scope policy.

A request must satisfy every applicable live policy. The strictest applied outcome wins.

Policy rollout modes

Policies support a safe rollout model:

ModeBehaviour when capacity is exhausted
observeRecords the recommended restriction but applies allow. Simulation capacity is isolated from live capacity.
throttleReturns a throttle decision and bounded retry guidance.
enforceReturns a reject decision.

Start new production policies in Observe. Review genuine traffic before moving a policy into Throttle or Enforce.

The API operation itself can return HTTP 200 even when the service decision is throttle or reject, because the rate-limit check was processed successfully. Always inspect the decision field. An HTTP 429 from the VettiGuard endpoint is different: it means the VettiGuard API operation itself has hit its protective ingress limit.

Configure a policy

In the workspace console, open Protection & trust → Rate Limiting.

A policy defines:

  • A protected site or mobile application, or all applications in the workspace.
  • An action pattern such as payment.* or otp.send.
  • An HTTP method or *.
  • A scope.
  • Bucket capacity.
  • Refill amount and refill interval.
  • Default token cost.
  • Optional fixed quota and quota window.
  • Optional adaptive token cost.
  • Observe, Throttle, Enforce, or workspace-default mode.
  • The configured degraded behaviour if the distributed state service is temporarily unavailable.

Scope types

ScopeIdentifier sent by the integrationExample use
workspaceNoneGlobal workspace ceiling.
applicationNonePer protected site or mobile app.
subjectsubject_idPer authenticated customer/account.
devicedevice_idPer installation or privacy-safe device reference.
routeroutePer backend route.
customscope_keyA customer-defined opaque grouping key.

VettiGuard retains subject, device, route/custom scope values only as privacy-safe keyed hashes in rate-limit state/history.

Token bucket example

Suppose a payment policy has:

Capacity:       100 tokens
Refill:          10 tokens
Refill interval:  1 second
Default cost:     1 token

A client can temporarily burst up to the available bucket capacity, while sustained traffic settles around the configured refill rate.

Do not assume every request must cost exactly one token. An expensive operation can deliberately consume more:

profile.read       = 1 token
search.execute     = 3 tokens
report.generate    = 10 tokens
otp.send           = 20 tokens

Server endpoint

POST https://api.vettiguard.com/v1/rate-limit/check
Authorization: Bearer YOUR_PRIVATE_SECRET
Content-Type: application/json

The protected-site compatibility secret may also be sent as the secret JSON field, but new server integrations should prefer a scoped credential or Bearer secret held only on the trusted backend.

Server request

{
  "action": "payment.create",
  "method": "POST",
  "subject_id": "customer-1842",
  "device_id": "browser-device",
  "route": "/api/payments",
  "cost": 1,
  "api_protection_assessment_id": "70f05cc7-989e-41f0-9a37-c9884d323a67"
}

Request fields

FieldRequiredDescription
actionYesStable protected action, for example payment.create.
methodNoHTTP method used for policy matching. Defaults to POST.
subject_idWhen a subject-scoped policy matchesOpaque application subject/account identifier.
device_idWhen a device-scoped policy matchesOpaque installation/device reference.
routeWhen a route-scoped policy matchesURL path only, for example /api/payments; do not send query strings.
scope_keyWhen a custom-scoped policy matchesOpaque customer-defined grouping key.
costNoRequested base cost. The configured policy cost remains the minimum.
api_protection_assessment_idNoFresh VettiGuard API Protection assessment used for authoritative adaptive cost.

Do not send passwords, access tokens, cookies, request bodies, full URLs with secrets, or sensitive personal data as scope identifiers.

Server response

{
  "success": true,
  "decision_id": "bca30ea6-72d7-42e2-a6d3-2c1b350551c5",
  "allowed": true,
  "recommended_decision": "allow",
  "decision": "allow",
  "retry_after_ms": 0,
  "retry_after_seconds": 0,
  "matched_policy_count": 3,
  "remaining": 74,
  "adaptive_multiplier": 1,
  "reason_codes": [],
  "policies": []
}

Response fields

FieldMeaning
successVettiGuard processed the rate-limit decision.
decision_idOpaque identifier for correlation and workspace analytics.
allowedConvenience boolean; true only when the applied decision is allow.
recommended_decisionWhat live capacity recommends, including in Observe mode.
decisionWhat your application should apply: allow, throttle, or reject.
retry_after_ms / retry_after_secondsBounded wait before retry when capacity is exhausted.
matched_policy_countNumber of policies that participated in the decision.
remainingLowest remaining token capacity across matching policies when available.
adaptive_multiplierHighest authoritative cost multiplier applied to the request.
reason_codesSafe machine-readable reasons such as token-bucket-exhausted or fixed-window-quota-exceeded.
policiesPer-policy decision detail suitable for trusted server diagnostics.

When retry guidance is present, VettiGuard can also return an HTTP Retry-After header. Use the body fields as the canonical integration contract for the service decision.

Handle the decision

A typical trusted-backend flow is:

Receive application request
        ↓
Authenticate / establish application context
        ↓
Call VettiGuard /rate-limit/check
        ↓
allow     → execute the protected operation
throttle  → defer or return controlled backoff
reject    → do not execute the protected operation

Do not perform the protected operation first and call Rate Limiting afterward. The check-and-consume operation is intentionally atomic so concurrent requests cannot all observe the same unconsumed capacity.

cURL example

curl -X POST "https://api.vettiguard.com/v1/rate-limit/check" \
  -H "Authorization: Bearer $VETTIGUARD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "payment.create",
    "method": "POST",
    "subject_id": "customer-1842",
    "route": "/api/payments",
    "cost": 1
  }'

PHP SDK

use VettiGuardSdk\VettiGuardVerifier;

$client = VettiGuardVerifier::production($_ENV['VETTIGUARD_SECRET']);
$decision = $client->checkRateLimit([
    'action' => 'payment.create',
    'method' => 'POST',
    'subject_id' => 'customer-1842',
    'route' => '/api/payments',
    'cost' => 1,
]);

if (($decision['decision'] ?? '') !== 'allow') {
    // Apply your application's controlled backoff or rejection response.
}

Node.js SDK

const { checkRateLimit } = require('@vettiguard/node');

const decision = await checkRateLimit({
  secret: process.env.VETTIGUARD_SECRET,
  action: 'payment.create',
  method: 'POST',
  subjectId: 'customer-1842',
  route: '/api/payments',
  cost: 1
});

if (decision.decision !== 'allow') {
  // Respect retry_after_seconds before retrying.
}

Python SDK

from vettiguard import VettiGuardClient

client = VettiGuardClient(secret="vg_secret_replace_me")
decision = client.rate_limit(
    action="payment.create",
    method="POST",
    subject_id="customer-1842",
    route="/api/payments",
    cost=1,
)

if decision.get("decision") != "allow":
    # Respect retry_after_seconds before retrying.
    pass

Go SDK

client, err := vettiguard.New("vg_secret_replace_me")
if err != nil {
    return err
}

decision, err := client.RateLimit(ctx, map[string]any{
    "action":     "payment.create",
    "method":     "POST",
    "subject_id": "customer-1842",
    "route":      "/api/payments",
    "cost":       1,
})
if err != nil {
    return err
}

if decision["decision"] != "allow" {
    // Apply controlled backoff or reject the protected work.
}

Native mobile endpoint

Native applications use:

POST https://api.vettiguard.com/v1/mobile/rate-limit/check

The request must authenticate with the registered mobile secret and include the application's exact platform and application identifier.

{
  "platform": "android",
  "app_id": "com.example.app",
  "action": "payment.create",
  "method": "POST",
  "subject_id": "customer-1842",
  "device_id": "installation-id",
  "route": "/api/payments",
  "cost": 1
}

The decision semantics are the same as the server endpoint. Never place a general protected-site server secret in a mobile binary.

Hierarchical policy example

You can create several policies that all match the same operation:

Workspace                50,000 requests/minute
Protected application    10,000 requests/minute
payment.create             1,000 requests/minute
Per subject                   20 requests/minute

VettiGuard evaluates every matching policy. The request is only fully admitted when all live constraints have capacity.

Fixed quotas

Token buckets control immediate traffic. Fixed quotas are useful for longer ceilings such as:

100 requests/minute
10,000 requests/hour
250,000 requests/day
5,000,000 requests/month

A quota is optional and is evaluated in addition to the token bucket. If either control is exhausted, the policy recommends restriction.

Adaptive token cost

When both the workspace and policy enable adaptive cost, you may supply a fresh api_protection_assessment_id from VettiGuard API Protection.

VettiGuard does not trust a caller-provided risk score. The assessment must:

  • Be an authoritative VettiGuard API Protection assessment.
  • Belong to the same workspace and protected application.
  • Be fresh enough for the adaptive-cost window.

Higher-risk requests can therefore consume capacity faster without allowing the caller to invent its own risk value.

No matching policy

If Rate Limiting is enabled but no policy matches the action/method/application, the check returns an allow decision with no-matching-policy in reason_codes.

Create explicit policies for every operation that you expect VettiGuard to govern.

Error handling

The most important transport/API errors are:

HTTPErrorMeaning
400missing-input-secretNo server/mobile secret was supplied.
400invalid-input-secretThe supplied secret is not valid.
400invalid-actionThe action is missing or malformed.
400missing-routeA route-scoped policy matched but no valid route was provided.
400invalid-subject_id / invalid-device_id / invalid-scope_keyA required opaque scope identifier is invalid.
403rate-limiting-disabledThe workspace has disabled Rate Limiting.
403invalid-app-identityNative mobile platform/application identity does not match registration.
429rate-limit-exceededThe VettiGuard API endpoint itself is receiving too many calls; back off.
503rate-limiting-unavailableThe Rate Limiting service is not currently ready.
503safe runtime errorVettiGuard could not complete the operation; follow your configured failure strategy.

Do not retry tight loops on 429 or 503. Use bounded exponential backoff with jitter and respect Retry-After when supplied.

Privacy and logging guidance

Safe integration practice:

  • Keep private VettiGuard secrets in server-side secret storage.
  • Use opaque subject/device/custom identifiers.
  • Send only a path for route; exclude query strings and credentials.
  • Do not use access tokens, session cookies, passwords, email bodies, payment data, or identity documents as scope keys.
  • Keep decision_id and X-Request-Id for correlation when useful.
  • Do not log private secrets or raw authentication material.

Rate Limiting vs other traffic controls

ServicePrimary question
Rate LimitingHow frequently may this operation happen?
Concurrency ControlHow many of these operations may execute simultaneously?
Dependency ResilienceShould this downstream dependency call happen right now?
API ProtectionDoes this request exhibit abuse, scraping, replay, or resource-risk signals?

These services can be used together. For example, API Protection can provide authoritative risk evidence that increases Rate Limiting token cost, while Concurrency Control protects expensive in-flight work and Dependency Resilience protects the provider called by that work.

Production rollout checklist

  1. Create the protected site or mobile application.
  2. Create one or more Rate Limiting policies for the protected actions.
  3. Keep the policies in Observe initially.
  4. Call the atomic check immediately before protected work.
  5. Verify that action, scope and cost values match the intended policy.
  6. Monitor recommended_decision, decision, remaining capacity and retry guidance.
  7. Confirm legitimate peak traffic is not being classified as exhausted capacity.
  8. Move selected policies to Throttle where controlled backoff is appropriate.
  9. Move mature policies to Enforce only after traffic baselines are understood.
  10. Continue monitoring policy decisions after deployment changes or traffic-pattern changes.

For the complete operation schema, use the API reference. For official backend clients, see Server SDK quickstarts.