WAF API

Manage web application firewall policies, browse managed rulesets, test a policy against requests before saving it, and read firewall analytics.

This page as Markdown

The web application firewall runs inside your application through the SDK. A WAF policy decides what it inspects, what it blocks and how: managed rulesets, your own rules, allow and deny lists, rate limits, outbound (egress) controls and behavioral scoring. Each policy applies to the buckets and environments you choose, and connected SDKs receive changes without a restart. See WAF policies for building a policy in the app.

Plan: needs a plan that includes the Web application firewall. Listing, reading and deleting existing policies, and reading analytics, keep working if the plan changes; servers stop enforcing policies when the plan no longer includes the firewall. The number of policies depends on the plan. Permissions: any member, or a user or administrator API token, can read policies, rulesets and analytics and run the simulator. Creating, changing and deleting a policy needs an owner or admin, or an administrator API token.

The WAF policy object

  • sid (string, required): The policy’s identifier (waf_ followed by 27 letters and digits).

  • accountSid (string, required): The account.

  • description (string | null): A short description.

  • enabled (boolean): Whether the policy is active. Default true.

  • mode (string): off, detect (record what would be blocked, block nothing) or block (enforce). Default detect.

  • paranoiaLevel (integer): 1 to 4. Higher levels turn on stricter rules, catching more attacks with more false positives. Default 1.

  • anomalyThreshold (integer): The total rule score at or above which a request is blocked. Default 5.

  • failMode (string): What happens if the firewall itself errors: open (let the request through, the default) or closed (refuse it).

  • blockResponseSid (string | null): The block response the policy uses, or null for a response of its own. While set, defaultBlock follows that response. On a write, omit it to keep what the policy has, or send null with a defaultBlock to give the policy its own.

  • defaultBlock (object): The response a blocked request gets, which your servers enforce: status (default 403), body (default Forbidden, no <), contentType (auto, json, problem+json or text; omitted means auto) and headers (only Retry-After, Cache-Control and RateLimit-*). For a policy that names a blockResponseSid, it is that response’s content and is ignored on a write.

  • managedRuleset (object): The managed ruleset the policy uses: id (default apitraffic-core), version (latest follows new releases; a version number pins one) and enabled.

  • managedRulesetStatus (object | null): What the ruleset currently resolves to: id, version, resolvedVersion, name, enabled and activeRuleCount (managed rules running after paranoia and tuning).

  • disabledRules (string[]): Managed rule ids turned off for this policy.

  • ruleExclusions (object[]): Scoped exceptions for false positives. Each names a ruleId or category, and can be limited to targets, argument, header or cookie names (case-insensitive) and a pathPattern regular expression.

  • rules (object[]): Your own rules: id, category, message, severity (default 4), score (default 5), paranoia (1 to 4), enabled, action (score, block, pass or tag; default score), targets, operator, negate, transforms and tags. A criteria operator’s query uses the search syntax with the request fields the firewall sees, at most 4,096 characters and no more than 256 spaces in a row. A query your SDK can’t evaluate as written is refused with 400: one that doesn’t parse, has a like pattern that isn’t a valid regular expression, or that older SDKs can’t parse or read differently (for example a bare value starting with AND or OR, which they cut in two: quote the value). One stored before is kept while unchanged and sent to your servers turned off, with a criteria-rule-sent-disabled warning.

  • lists (object[]): Allow and deny lists: id, kind (ip, country, asn, userAgent or path), effect (allow or deny), values and message.

  • rateLimits (object[]): Rate limits.

rate limit

  • id (string): Identifier.

  • by (string): What is counted: ip, header, path, tag or composite, with key naming the header or tag.

  • pathPattern (string): Only paths matching this regular expression.

  • limit (integer): Requests allowed per window.

  • windowSeconds (integer): The window.

  • mode (string): distributed (default), strict or local.

  • scope (string): instance (default): each server counts on its own. fleet-approx: each server allows its share of limit across the servers running the firewall, assuming load is spread evenly.

  • status (integer): Response status when the limit is hit. Default 429.

  • message (string): Response message.

  • limits (object): Request size limits: maxUrlLength, maxHeaderCount, maxHeaderValueLength, maxBodyBytes, maxBodyInspectBytes (8192 to 1048576, default 131072), inspectionLimitScore (0 to 1000, default 0), customerPatternBudgetMs (5 to 500, default 25) and allowedMethods.

  • geo (object): The headers your edge sets with the client’s country (countryHeader, for example cf-ipcountry) and network (asnHeader), for country and ASN lists. A country header is preferred where a request has it; without it, servers look the client IP up in the IP ranges of the policy’s countries (see Block or allow countries).

  • egress (object): Outbound controls: what your application may send, and to where.

egress

  • mode (string): off (default), detect or block.

  • inspectScope (string): Which calls have their payloads inspected: model (recognized AI providers and vector stores, the default) or all. inspectDestinations adds hosts (exact, or *.suffix), up to 200.

  • allowedDestinations (string[]): When set, calls in allowScope (model or all) may only go to these hosts. Up to 200.

  • blockedDestinations (string[]): Hosts that are always refused. Up to 200.

  • blockSecrets (boolean): Detect credentials in outbound payloads. Default true.

  • blockBulkPii (boolean): Detect bulk personal data, at bulkPiiThreshold (default 25) distinct records. Default false.

  • maxBodyBytes (integer): Refuse outbound payloads larger than this. Default 5242880 (5 MB); 0 turns the cap off.

  • inspectResponses (boolean): Scan model and tool responses for indirect prompt injection (detection only). Default false.

  • failMode (string): open (default) or closed.

  • behavior (object): Scoring of client behavior for authorization abuse (object-id enumeration, probing, fan-out), evaluated outside the request path.

behavior

  • enabled (boolean): Default false.

  • action (string): detect (record findings, the default) or block (deny the client on every server).

  • threshold (integer): Score at which a client is denied, 1 to 500. Default 70.

  • windowSeconds (integer): The window each client is scored over, 30 to 86400. Default 300.

  • denySeconds (integer): How long a deny lasts, 30 to 86400. Default 900.

  • enumerationThreshold (integer): Distinct object ids on one endpoint before enumeration scores. Default 30.

  • authFailureThreshold (integer): 401 and 403 responses in the window before authorization failures score. Default 15.

  • notFoundThreshold (integer): 404 responses in the window before id probing scores. Default 40.

  • fanOutThreshold (integer): Distinct endpoints in the window before fan-out scores. Default 40.

  • velocityThreshold (integer): Requests in the window before volume scores. Default 600.

  • sensitiveVelocityThreshold (integer): Requests to sensitive paths before velocity scores. Default 30.

  • sensitivePaths (string[]): Path prefixes treated as sensitive, for example /v1/admin. Up to 100.

  • testCases (object[]): Example requests with the outcome you expect (expect: block or allow), up to 50, run by the simulator. Never sent to SDKs. Each has id, name, expect and request (method, url, headers, body, ip).

  • buckets (string[]): The buckets the policy applies to, or ["*"].

  • environments (string[]): The environments the policy applies to, or ["*"].

  • configVersion (number): Goes up with every change. Servers report the version they run, so you can see when a change has reached the fleet.

  • warnings (object[]): Present when older SDK versions enforce part of the policy differently, for example IPv6 egress entries that SDK 0.1.0 and 0.1.1 do not enforce. The policy is saved as sent. A criteria-rule-sent-disabled warning names an enabled criteria rule, stored before the API checked such rules, that your SDK can’t evaluate as written (for example, it uses a field that isn’t known before your API responds, its query doesn’t parse, a like pattern isn’t a valid regular expression, or older SDKs read it differently): the rule is sent to your servers turned off, although it still reads as enabled. A criteria-or-runs-as-and warning names an enabled criteria rule saved with OR between conditions before OR was refused: it runs as AND (every condition must match). A rule carries at most one of the two. Saving a policy refuses a new or changed criteria rule with either problem; an unchanged one (same query, negate and action) is kept. Country lookups add country-older-sdks (lookups by client IP need @apitraffic/waf 2.1.0 or later), country-ranges-too-large (the policy’s countries’ IP ranges are more than a policy can carry, so none are sent) and country-ranges-unavailable (the country data is not available yet, and the policy names no country header).

  • countryLookup (object): Whether servers can look countries up by client IP (available), and, when they can, whose data the IP ranges are: provider, providerVersion, license and attribution (text and url, the credit to show with it: “IP Geolocation by DB-IP”, https://db-ip.com).

  • createdAt (string, required): When the policy was created (ISO 8601).

List WAF policies

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies

Returns every WAF policy in the account.

Path parameters

  • accountSid (path, string, required): The account identifier.

Response

  • hasMore (boolean, required): Always false.

  • records (object[], required): The policies, as WAF policy objects.

bash
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN"
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { records } = await response.json();
json
{
  "hasMore": false,
  "records": [
    {
      "sid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
      "accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
      "description": "Acme Store WAF",
      "enabled": true,
      "mode": "block",
      "paranoiaLevel": 1,
      "anomalyThreshold": 5,
      "failMode": "open",
      "defaultBlock": { "status": 403, "body": "Blocked by ApiTraffic WAF" },
      "managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
      "managedRulesetStatus": { "id": "apitraffic-core", "version": "latest", "resolvedVersion": "1.1.2", "name": "ApiTraffic Core Rule Set", "enabled": true, "activeRuleCount": 212 },
      "disabledRules": [],
      "ruleExclusions": [],
      "rules": [],
      "lists": [
        { "id": "known-attackers", "kind": "ip", "effect": "deny", "values": ["203.0.113.66"], "message": "Known credential-stuffing source" }
      ],
      "rateLimits": [
        { "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "mode": "local", "status": 429, "scope": "instance", "message": "Too many sign-in attempts from this address" }
      ],
      "limits": {},
      "geo": {},
      "egress": { "mode": "off" },
      "behavior": { "enabled": false },
      "testCases": [],
      "buckets": ["*"],
      "environments": ["*"],
      "configVersion": 7,
      "createdAt": "2026-03-02T16:20:00.000Z"
    }
  ]
}

Get WAF policy

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}

Returns one WAF policy.

Path parameters

  • accountSid (path, string, required): The account identifier.

  • wafSid (path, string, required): The policy’s identifier (waf_…).

Response

A WAF policy object.

bash
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN"
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const policy = await response.json();
json
{
  "sid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
  "accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
  "description": "Acme Store WAF",
  "enabled": true,
  "mode": "block",
  "paranoiaLevel": 1,
  "anomalyThreshold": 5,
  "failMode": "open",
  "managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
  "buckets": ["*"],
  "environments": ["*"],
  "configVersion": 7,
  "createdAt": "2026-03-02T16:20:00.000Z"
}

The example shortens the policy; see List WAF policies for a full one.

Create WAF policy

POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies

Creates a WAF policy. Every field of the WAF policy object except sid, accountSid, managedRulesetStatus, configVersion, warnings, countryLookup and createdAt can be sent; anything you leave out takes its default. Start with mode: "detect", check what it would block in Get firewall overview, then switch to block.

Permissions: owner or admin, or an administrator API token. Limit: past the plan’s policy limit, the API answers 409 with code 2213.

Path parameters

  • accountSid (path, string, required): The account identifier.

Body parameters

  • mode (body, string, default detect): off, detect or block.

  • buckets (body, string[]): Bucket sids, or ["*"].

  • environments (body, string[]): Environment sids, or ["*"].

  • description (body, string | null): A short description.

  • … (body, various): Any other policy field, as described in The WAF policy object. Custom patterns (in rules, lists and exclusions) are checked when you save: they must compile and must not be able to backtrack catastrophically.

Response

The new WAF policy object.

bash
curl -X POST https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Acme Store WAF",
    "mode": "detect",
    "paranoiaLevel": 1,
    "anomalyThreshold": 5,
    "rateLimits": [
      { "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "status": 429 }
    ],
    "buckets": ["k3m9p2x"],
    "environments": ["prd7k2"]
  }'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    description: 'Acme Store WAF',
    mode: 'detect',
    rateLimits: [
      { id: 'login-per-ip', by: 'ip', pathPattern: '^/v1/auth/login$', limit: 10, windowSeconds: 60, status: 429 },
    ],
    buckets: ['k3m9p2x'],
    environments: ['prd7k2'],
  }),
});
const policy = await response.json();
json
{
  "sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
  "accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
  "description": "Acme Store WAF",
  "enabled": true,
  "mode": "detect",
  "paranoiaLevel": 1,
  "anomalyThreshold": 5,
  "failMode": "open",
  "defaultBlock": { "status": 403, "body": "Forbidden" },
  "managedRuleset": { "id": "apitraffic-core", "version": "latest", "enabled": true },
  "rateLimits": [
    { "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "mode": "distributed", "status": 429, "scope": "instance" }
  ],
  "buckets": ["k3m9p2x"],
  "environments": ["prd7k2"],
  "configVersion": 1,
  "createdAt": "2026-10-20T12:00:00.000Z"
}

The example shortens the response.

Update WAF policy

PUT https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}

Replaces a WAF policy. Send the whole policy, as for Create WAF policy: fields you leave out return to their defaults, except geo and testCases, which keep their values when omitted. The new version reaches connected SDKs without a restart, and configVersion goes up.

Permissions: owner or admin, or an administrator API token.

Path parameters

  • accountSid (path, string, required): The account identifier.

  • wafSid (path, string, required): The policy’s identifier.

Body parameters

The same as Create WAF policy.

Response

The updated WAF policy object.

bash
curl -X PUT https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Acme Store WAF",
    "mode": "block",
    "rateLimits": [
      { "id": "login-per-ip", "by": "ip", "pathPattern": "^/v1/auth/login$", "limit": 10, "windowSeconds": 60, "status": 429 }
    ],
    "buckets": ["k3m9p2x"],
    "environments": ["prd7k2"]
  }'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ';
const current = await (await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
})).json();
// Send back every field you can set, changing only the mode.
const { sid, accountSid: _account, managedRulesetStatus, configVersion, warnings, countryLookup, createdAt, ...policy } = current;
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ ...policy, mode: 'block' }),
});
const updated = await response.json();
json
{
  "sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
  "accountSid": "acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa",
  "description": "Acme Store WAF",
  "enabled": true,
  "mode": "block",
  "buckets": ["k3m9p2x"],
  "environments": ["prd7k2"],
  "configVersion": 2,
  "createdAt": "2026-10-20T12:00:00.000Z"
}

The example shortens the response.

Delete WAF policy

DELETE https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/{wafSid}

Deletes a WAF policy. Servers stop enforcing it when they next receive their configuration.

Permissions: owner or admin, or an administrator API token.

Path parameters

  • accountSid (path, string, required): The account identifier.

  • wafSid (path, string, required): The policy’s identifier.

Response

  • sid (string, required): The deleted policy’s identifier.

  • deleted (boolean, required): true.

bash
curl -X DELETE https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN"
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const wafSid = 'waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ';
await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/${wafSid}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
json
{
  "sid": "waf_2rS6tU8vW1xY3zA5bC7dE9fG2hJ",
  "deleted": true
}

Simulate WAF policy

POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-policies/simulate

Evaluates a policy, saved or not, against up to 250 requests and says what it would do with each. Nothing is saved or sent to servers. Pass baselineWafSid to compare against a saved policy and see which requests a change would newly block or let through. Rate limits are not counted in a simulation.

Plan: needs the Web application firewall.

Path parameters

  • accountSid (path, string, required): The account identifier.

Body parameters

  • policy (body, object, required): The policy to evaluate, with the same fields as Create WAF policy.

  • requests (body, object[], required): 1 to 250 requests: method (default GET), url (path and query string, for example /v1/search?q=1), headers, body (raw; JSON is parsed when the content type says so), ip (for IP lists) and id (echoed back).

  • baselineWafSid (body, string): A saved policy to compare with.

Response

  • summary (object, required): evaluated, notEvaluated, blocked, matched (rules matched, below the threshold), allowed, newlyBlocked and noLongerBlocked (with a baseline, otherwise null), and truncated (true when the time limit stopped the run early).

  • results (object[], required): One entry per evaluated request: id, method, path, verdict (block, match or allow), score, threshold, ruleId and reason (what decided it), status (the block response status), matches (each rule that matched, with ruleId, category, message, severity, score, target, data, source and action), and with a baseline, baseline (verdict, score, ruleId) and changed.

bash
curl -X POST https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-policies/simulate \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "policy": { "mode": "block", "paranoiaLevel": 2 },
    "baselineWafSid": "waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH",
    "requests": [
      { "id": "traversal", "url": "/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd" },
      { "id": "normal", "url": "/v1/search?q=running+shoes" }
    ]
  }'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-policies/simulate`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    policy: { mode: 'block', paranoiaLevel: 2 },
    baselineWafSid: 'waf_2qR5sT7uV9wX1yZ3aB6cD8eF4gH',
    requests: [
      { id: 'traversal', url: '/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd' },
      { id: 'normal', url: '/v1/search?q=running+shoes' },
    ],
  }),
});
const { summary, results } = await response.json();
json
{
  "summary": {
    "evaluated": 2,
    "notEvaluated": 0,
    "blocked": 1,
    "matched": 0,
    "allowed": 1,
    "newlyBlocked": 0,
    "noLongerBlocked": 0,
    "truncated": false
  },
  "results": [
    {
      "id": "traversal",
      "method": "GET",
      "path": "/v1/products/..%2F..%2F..%2F..%2Fetc%2Fpasswd",
      "verdict": "block",
      "score": 10,
      "threshold": 5,
      "ruleId": "AT-LFI-001",
      "reason": "anomaly-threshold",
      "status": 403,
      "matches": [
        { "ruleId": "AT-LFI-001", "category": "lfi", "message": "Path traversal detected in the request path", "severity": 2, "score": 5, "target": "path", "data": "../", "source": "managed", "action": "score" }
      ],
      "baseline": { "verdict": "block", "score": 10, "ruleId": "AT-LFI-001" },
      "changed": false
    },
    {
      "id": "normal",
      "method": "GET",
      "path": "/v1/search",
      "verdict": "allow",
      "score": 0,
      "threshold": 5,
      "ruleId": null,
      "reason": null,
      "status": null,
      "matches": [],
      "baseline": { "verdict": "allow", "score": 0, "ruleId": null },
      "changed": false
    }
  ]
}

List managed rulesets

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-rulesets

Returns the managed rulesets a policy can use.

Plan: needs the Web application firewall.

Path parameters

  • accountSid (path, string, required): The account identifier.

Response

  • hasMore (boolean, required): Always false.

  • records (object[], required): One entry per ruleset: id, version, name, description, released, total (rules), byCategory and byParanoia (rule counts).

bash
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-rulesets \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN"
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-rulesets`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { records } = await response.json();
json
{
  "hasMore": false,
  "records": [
    {
      "id": "apitraffic-core",
      "version": "1.1.2",
      "name": "ApiTraffic Core Rule Set",
      "description": "Managed protection for the OWASP Top 10 injection families: SQLi, XSS, OS command execution, path traversal/LFI/RFI, SSRF, template injection, NoSQL injection, XXE, plus scanner, probe and protocol hygiene rules.",
      "released": "2026-10-07",
      "total": 240,
      "byCategory": { "sqli": 48, "xss": 41, "lfi": 22 },
      "byParanoia": { "1": 140, "2": 62, "3": 28, "4": 10 }
    }
  ]
}

Get managed ruleset

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-rulesets/{rulesetId}

Returns a managed ruleset with every rule in it, so you can choose rules to turn off or exclude.

Plan: needs the Web application firewall.

Path parameters

  • accountSid (path, string, required): The account identifier.

  • rulesetId (path, string, required): The ruleset id, for example apitraffic-core.

Query parameters

  • version (query, string): The version. Defaults to the newest.

Response

The ruleset’s fields, as in List managed rulesets, plus rules: each with id, category, message, severity, score, paranoia, action, targets and tags.

bash
curl https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-rulesets/apitraffic-core \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN"
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-rulesets/apitraffic-core`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const ruleset = await response.json();
json
{
  "id": "apitraffic-core",
  "version": "1.1.2",
  "name": "ApiTraffic Core Rule Set",
  "description": "Managed protection for the OWASP Top 10 injection families: SQLi, XSS, OS command execution, path traversal/LFI/RFI, SSRF, template injection, NoSQL injection, XXE, plus scanner, probe and protocol hygiene rules.",
  "released": "2026-10-07",
  "total": 240,
  "byCategory": { "sqli": 48, "xss": 41, "lfi": 22 },
  "byParanoia": { "1": 140, "2": 62, "3": 28, "4": 10 },
  "rules": [
    { "id": "AT-LFI-001", "category": "lfi", "message": "Path traversal detected in the request path", "severity": 2, "score": 5, "paranoia": 1, "action": "score", "targets": ["path", "args"], "tags": [] }
  ]
}

The examples are shortened, and the rule counts are illustrative.

Firewall analytics filters

The four analytics operations below take the same filters:

  • startTime (query, string, required): Start of the range (ISO 8601, UTC, ending in Z).

  • endTime (query, string, required): End of the range.

  • environmentSid (query, string): Only this environment.

  • bucketSid (query, string): Only this bucket.

  • policySid (query, string): Only detections made by this policy (the policy that actually ran).

  • ruleId (query, string): Only detections this rule contributed to.

  • category (query, string): Only detections whose deciding rule is in this category.

  • action (query, string): tag (matched below the threshold), would-block (would have been blocked in detect mode) or block.

  • endpoint (query, string): Only this endpoint. Also narrows the inspected traffic, so matchRate stays a share of that endpoint’s own requests.

Get firewall overview

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/overview

Returns firewall totals, a time series, the score distribution and the busiest rules.

Path parameters

  • accountSid (path, string, required): The account identifier.

Query parameters

The analytics filters, plus:

  • ruleLimit (query, integer, default 10): Rules in rules, 1 to 1000.

Response

  • summary (object, required): matchedRequests, blockedRequests (actually refused), wouldBlockRequests (would have been, if enforcing), taggedRequests (matched below the threshold), ruleMatches, distinctRules, distinctCategories, distinctEndpoints, averageScore, maxScore, p95Score, firstSeen, lastSeen, inspectedRequests (all inbound requests in the range), matchRate (percent of inspected requests that matched) and enforcementRate (percent of matches that blocked or would have).

  • timeseries (object[], required): Per period: bucket, the request counts above, ruleMatches, averageScore and maxScore.

  • scores (object[], required): The score distribution: score, matchedRequests, blockedRequests and wouldBlockRequests.

  • rules (object[], required): The busiest rules, as in List firewall rule hits.

  • degraded (boolean): Present and true when part of the answer could not be read; failed names the parts (timeseries, scores, rules).

bash
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/overview \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  --data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
  --data-urlencode 'endTime=2026-10-20T00:00:00.000Z'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ startTime: '2026-10-19T00:00:00.000Z', endTime: '2026-10-20T00:00:00.000Z' });
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/overview?${params}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { summary } = await response.json();
json
{
  "summary": {
    "matchedRequests": 318,
    "blockedRequests": 296,
    "wouldBlockRequests": 0,
    "taggedRequests": 22,
    "ruleMatches": 604,
    "distinctRules": 19,
    "distinctCategories": 5,
    "distinctEndpoints": 7,
    "averageScore": 9.4,
    "maxScore": 35,
    "p95Score": 20,
    "firstSeen": "2026-10-19T00:12:40.000Z",
    "lastSeen": "2026-10-19T23:51:07.000Z",
    "inspectedRequests": 10412,
    "matchRate": 3.05,
    "enforcementRate": 93.08
  },
  "timeseries": [],
  "scores": [],
  "rules": []
}

List firewall rule hits

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/rules

Ranks rules by the requests they fired on, with how often each one decided the outcome. Use it to find rules worth tuning.

Path parameters

  • accountSid (path, string, required): The account identifier.

Query parameters

The analytics filters, plus limit (1 to 1000, default 50).

Response

  • points (object[], required): One entry per rule: ruleId, category, message, severity, paranoia, ruleScore (score per hit), ruleAction, matchedRequests, blockedRequests, wouldBlockRequests, taggedRequests, decidedRequests (requests where this rule decided the outcome), averageScore, maxScore, distinctEndpoints, firstSeen and lastSeen.
bash
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/rules \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  --data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
  --data-urlencode 'endTime=2026-10-20T00:00:00.000Z' \
  --data-urlencode 'limit=10'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({ startTime: '2026-10-19T00:00:00.000Z', endTime: '2026-10-20T00:00:00.000Z', limit: '10' });
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/rules?${params}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
json
{
  "points": [
    {
      "ruleId": "AT-LFI-001",
      "category": "lfi",
      "message": "Path traversal detected in the request path",
      "severity": 2,
      "paranoia": 1,
      "ruleScore": 5,
      "ruleAction": "score",
      "matchedRequests": 74,
      "blockedRequests": 74,
      "wouldBlockRequests": 0,
      "taggedRequests": 0,
      "decidedRequests": 61,
      "averageScore": 12.3,
      "maxScore": 20,
      "distinctEndpoints": 2,
      "firstSeen": "2026-10-19T00:31:02.000Z",
      "lastSeen": "2026-10-19T23:40:19.000Z"
    }
  ]
}

Get firewall breakdown

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/breakdown

Groups firewall matches by one dimension, such as category, endpoint or policy.

Path parameters

  • accountSid (path, string, required): The account identifier.

Query parameters

The analytics filters, plus:

  • dimension (query, string, required): category, action, reason, severity, mode, policy, endpoint, path, method, statusCode, bucketSid or environmentSid.

  • limit (query, integer, default 25): Groups to return, 1 to 1000.

Response

  • dimension (string, required): The dimension used.

  • points (object[], required): One entry per group: key, matchedRequests, blockedRequests, wouldBlockRequests, taggedRequests, distinctRules and lastSeen.

bash
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/breakdown \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  --data-urlencode 'dimension=reason' \
  --data-urlencode 'startTime=2026-10-19T00:00:00.000Z' \
  --data-urlencode 'endTime=2026-10-20T00:00:00.000Z'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
  dimension: 'reason',
  startTime: '2026-10-19T00:00:00.000Z',
  endTime: '2026-10-20T00:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/breakdown?${params}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
json
{
  "dimension": "reason",
  "points": [
    { "key": "anomaly-threshold", "matchedRequests": 211, "blockedRequests": 189, "wouldBlockRequests": 0, "taggedRequests": 22, "distinctRules": 17, "lastSeen": "2026-10-19T23:51:07.000Z" },
    { "key": "rate-limit", "matchedRequests": 84, "blockedRequests": 84, "wouldBlockRequests": 0, "taggedRequests": 0, "distinctRules": 1, "lastSeen": "2026-10-19T03:24:55.000Z" },
    { "key": "ip-deny", "matchedRequests": 23, "blockedRequests": 23, "wouldBlockRequests": 0, "taggedRequests": 0, "distinctRules": 1, "lastSeen": "2026-10-19T03:22:10.000Z" }
  ]
}

List firewall matches

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-analytics/matches

Returns the matched requests themselves, newest first, so you can open them with Get request.

Path parameters

  • accountSid (path, string, required): The account identifier.

Query parameters

The analytics filters, plus limit (1 to 1000, default 50).

Response

  • points (object[], required): One entry per request: dt, sid (the request), method, path, endpoint, statusCode, durationMs, score, action, reason, ruleId (the deciding rule), category, ruleIds (every rule that contributed), bucketSid and environmentSid.
bash
curl -G https://api.apitraffic.io/v1/accounts/acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa/waf-analytics/matches \
  -H "Authorization: Bearer $APITRAFFIC_TOKEN" \
  --data-urlencode 'action=block' \
  --data-urlencode 'startTime=2026-10-19T03:00:00.000Z' \
  --data-urlencode 'endTime=2026-10-19T04:00:00.000Z'
js
const accountSid = 'acc_2mXq7RbT4kPz9WvN3cHd8LsYfQa';
const params = new URLSearchParams({
  action: 'block',
  startTime: '2026-10-19T03:00:00.000Z',
  endTime: '2026-10-19T04:00:00.000Z',
});
const response = await fetch(`https://api.apitraffic.io/v1/accounts/${accountSid}/waf-analytics/matches?${params}`, {
  headers: { Authorization: `Bearer ${process.env.APITRAFFIC_TOKEN}` },
});
const { points } = await response.json();
json
{
  "points": [
    {
      "dt": "2026-10-19T03:21:44.000Z",
      "sid": "req_2lM7nP9qR2sT4uV6wX8yZ1aB3cD",
      "method": "POST",
      "path": "/v1/auth/login",
      "endpoint": "/v1/auth/login",
      "statusCode": 429,
      "durationMs": 1,
      "score": 0,
      "action": "block",
      "reason": "rate-limit",
      "ruleId": "login-per-ip",
      "category": "rate-limit",
      "ruleIds": ["login-per-ip"],
      "bucketSid": "k3m9p2x",
      "environmentSid": "prd7k2"
    }
  ]
}

Block responses

A block response is what a request blocked by a policy receives, shared by the policies that name it with blockResponseSid. Changing one updates and pushes every policy that uses it.

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses

POST https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses

GET https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}

PUT https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}

DELETE https://api.apitraffic.io/v1/accounts/{accountSid}/waf-block-responses/{blockResponseSid}

Permissions: reading needs any account role; creating, changing and deleting need owner or admin, or an administrator API token. Creating and changing need a plan that includes the firewall; reading and deleting do not.

The block response object

  • sid (string, required): The block response’s identifier (wbr_…).

  • name (string, required): A name to pick it by, up to 100 characters.

  • description (string): What it is for, up to 500 characters.

  • status (integer, required): The HTTP status, 200 to 599 except 204, 205 and 304 (default 403).

  • contentType (string, required): auto (JSON when the body is a JSON object or array, else plain text), json, problem+json (RFC 9457) or text. A JSON type needs a body of that shape.

  • body (string, required): 1 to 4,096 bytes, without <. Each {context_id} is replaced, as a block is sent, with the blocked request’s context id (ctx_…, 31 characters, counted towards the limit), which finds the request with context_sid == ctx_….

  • headers (object, required): Only Retry-After, Cache-Control and RateLimit-*, each once, with a value.

  • usedBy (object[], required): The policies that use it: sid and description.

  • createdAt (string, required): When it was created.

  • updatedAt (string | null, required): When it last changed. Send it back as expectedUpdatedAt on an update to have the update refused if someone changed the response since.

A create takes name, description, status, contentType, body and headers. An update (PUT) changes only the fields it sends, plus the optional expectedUpdatedAt. A delete is refused while a policy uses the response, and answers sid and deleted.

Errors

Status Code Meaning
400 1000 A field is missing or invalid, for example an unknown mode, a custom pattern that is unsafe, or more than 250 requests to simulate. The message names the field.
403 2010 Creating, changing or deleting a policy without owner, admin or administrator access.
403 2221 The plan does not include the firewall.
403 2509 Changing a policy for the read-only sample bucket.
404 4000 No policy or ruleset with that id.
409 2213 The plan’s WAF policy limit is reached.
400 2684 A policy names a block response that does not exist (or was deleted meanwhile). Pick another one.
409 2680 A block response in use cannot be deleted; data.usedBy lists the policies. Also returned when policies are moving to and from it at that moment: try again.
409 2682 The account keeps the most block responses it can (100).
409 2683 A block response update was refused: it changed since expectedUpdatedAt (data.updatedAt is the stored version), or other saves kept changing it. Load it again and retry.
503 2681 A block response was saved, but not every policy using it reached your servers. Save it again (with data.updatedAt as expectedUpdatedAt) to retry.
503, 504 5101 to 5104 Analytics could not be read right now. Retry after Retry-After seconds.