PBN.LTD API docs
View as Markdown

Site security

For sites on Cloudflare: "I'm Under Attack" mode, the site's own firewall rules and blocking AI crawlers.

Security settings of a site

GET/api/v1/sites/{site_id}/security

Scope security:read

The same as the site's Security tab: whether "I'm Under Attack" is on (and when it switches itself off), the site's firewall rules and rate-limiting rules with the Cloudflare plan's limits, bot protection (AI crawlers blocked, bot fight mode), and the fields, operators and actions a new rule may use. 409 for a site that is not on Cloudflare.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/security" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/security", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "site_id": 123,
    "under_attack": {
      "available": true,
      "on": false,
      "external": false,
      "auto_off_at": null,
      "enabled_at": null,
      "level": "medium",
      "error": ""
    },
    "auto_off_choices": [
      "1h",
      "6h",
      "24h",
      "never"
    ],
    "bot_protection": {
      "ai": true,
      "fight": false,
      "status": "active",
      "label": "Active"
    },
    "rules": {
      "status": "ready",
      "plan": "free",
      "limits": {
        "custom": 5,
        "ratelimit": 1
      },
      "http_request_firewall_custom": [
        {
          "id": "a1b2",
          "name": "Block bad country",
          "action": "block",
          "enabled": true,
          "expression": "(ip.src.country in {\"XX\"})"
        }
      ],
      "http_ratelimit": []
    },
    "fields": {
      "country": {
        "label": "Country",
        "ops": [
          "in",
          "not_in"
        ]
      }
    },
    "ratelimit_fields": [
      "path",
      "method",
      "wp_login",
      "wp_admin",
      "verified_bot"
    ],
    "actions": {
      "http_request_firewall_custom": {
        "block": "Block"
      },
      "http_ratelimit": {
        "block": "Block"
      }
    },
    "login_path": "/wp-login.php",
    "changes_paused": false
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized

"I'm Under Attack" on or off

PUT/api/v1/sites/{site_id}/security/under-attack

Scope security:write

Every visitor gets a short browser check before the site loads while it is on - use it while the site is being hammered, then switch it off.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
onbodybooleanyestrue = on, false = off.
auto_offbodystring (one of: 1h, 6h, 24h, never)noWhen switching on: switch it off again by itself after this long. Default: never.

Example

curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/security/under-attack" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/security/under-attack", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/under-attack", {
  method: "PUT",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "available": true,
    "on": false,
    "external": false,
    "auto_off_at": null,
    "enabled_at": null,
    "level": "medium",
    "error": ""
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, unavailable, validation_failed

Change bot protection

PUT/api/v1/sites/{site_id}/security/bots

Scope security:write

The same as the two bot switches on the Security tab. The change is saved at once and applied at Cloudflare within a few minutes (status reads "applying", then "active").

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
settingbodystring (one of: ai, fight)yes"ai" = block the AI crawlers; "fight" = Cloudflare bot fight mode.
onbodybooleanyestrue = on, false = off.

Example

curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/security/bots" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/security/bots", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/bots", {
  method: "PUT",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "ai": true,
    "fight": false,
    "status": "active",
    "label": "Active"
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized

Add a firewall rule

POST/api/v1/sites/{site_id}/security/rules

Scope security:write

Adds a firewall rule (or a rate-limiting rule) to the site at Cloudflare, within the plan's limit. Returns every rule of the site.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
rulebodyobjectyesThe rule, exactly as the tab builds it: {"phase": "http_request_firewall_custom" (default) or "http_ratelimit", "name": "...", "action": one of actions[phase], "enabled": true, and EITHER "builder": {"match": "all""any", "conditions": [{"field": one of fields, "op": one of that field's ops, "value": "..." }]} OR "mode": "advanced" with "expression": a Cloudflare rule expression; a rate-limiting rule also takes "requests_per_period" (per 10 seconds per visitor)}. The same checks as the tab apply.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/security/rules" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/security/rules", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/rules", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

201

{
  "data": {
    "status": "ready",
    "plan": "free",
    "limits": {
      "custom": 5,
      "ratelimit": 1
    },
    "http_request_firewall_custom": [
      {
        "id": "a1b2",
        "name": "Block bad country",
        "action": "block",
        "enabled": true,
        "expression": "(ip.src.country in {\"XX\"})"
      }
    ],
    "http_ratelimit": []
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, unavailable, validation_failed

Change a firewall rule

PUT/api/v1/sites/{site_id}/security/rules/{rule_id}

Scope security:write

Replaces one rule (same shape as when adding it). Only rules made on this dashboard can be changed here.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
rule_idpathstringyesThe rule id (see GET .../security).
rulebodyobjectyesThe rule, exactly as the tab builds it: {"phase": "http_request_firewall_custom" (default) or "http_ratelimit", "name": "...", "action": one of actions[phase], "enabled": true, and EITHER "builder": {"match": "all""any", "conditions": [{"field": one of fields, "op": one of that field's ops, "value": "..." }]} OR "mode": "advanced" with "expression": a Cloudflare rule expression; a rate-limiting rule also takes "requests_per_period" (per 10 seconds per visitor)}. The same checks as the tab apply.

Example

curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id", {
  method: "PUT",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "status": "ready",
    "plan": "free",
    "limits": {
      "custom": 5,
      "ratelimit": 1
    },
    "http_request_firewall_custom": [
      {
        "id": "a1b2",
        "name": "Block bad country",
        "action": "block",
        "enabled": true,
        "expression": "(ip.src.country in {\"XX\"})"
      }
    ],
    "http_ratelimit": []
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized, unavailable, validation_failed

Switch a rule on or off, or move it

POST/api/v1/sites/{site_id}/security/rules/{rule_id}/{action}

Scope security:write

Rules run in order: the first that matches decides.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
rule_idpathstringyesThe rule id (see GET .../security).
actionpathstring (one of: enable, disable, up, down)yesenable / disable the rule, or move it up / down the order.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id/scan" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id/scan", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id/scan", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "status": "ready",
    "plan": "free",
    "limits": {
      "custom": 5,
      "ratelimit": 1
    },
    "http_request_firewall_custom": [
      {
        "id": "a1b2",
        "name": "Block bad country",
        "action": "block",
        "enabled": true,
        "expression": "(ip.src.country in {\"XX\"})"
      }
    ],
    "http_ratelimit": []
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized, unavailable, validation_failed

Delete a firewall rule

DELETE/api/v1/sites/{site_id}/security/rules/{rule_id}

Scope security:write · destructive

Removes one rule from the site at Cloudflare.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
rule_idpathstringyesThe rule id (see GET .../security).

Example

curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/security/rules/rule_id", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "status": "ready",
    "plan": "free",
    "limits": {
      "custom": 5,
      "ratelimit": 1
    },
    "http_request_firewall_custom": [
      {
        "id": "a1b2",
        "name": "Block bad country",
        "action": "block",
        "enabled": true,
        "expression": "(ip.src.country in {\"XX\"})"
      }
    ],
    "http_ratelimit": []
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized, unavailable