Wazuh REST API 4.14 - Complete Reference Guide

The Wazuh 4.14 REST API provides programmatic access to every platform capability: agent management, alert retrieval, rule and decoder administration, cluster monitoring, and security management through RBAC. The API runs on port 55000 of the Wazuh Manager server and uses JWT authentication.

Authentication

Obtaining a JWT Token

All API requests require a JWT token obtained through the authentication endpoint:

TOKEN=$(curl -sk -u wazuh-wui:YOUR_PASSWORD \
  -X POST "https://localhost:55000/security/user/authenticate?raw=true")

The raw=true parameter returns the token string without a JSON wrapper.

Using the Token

Pass the token in the Authorization header:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?limit=5"

Token Expiration

The JWT token is valid for 15 minutes (900 seconds) by default. To change the expiration, use the security configuration endpoint:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X PUT "https://localhost:55000/security/config" \
  -H "Content-Type: application/json" \
  -d '{"auth_token_exp_timeout": 3600}'

Python SDK - Authentication

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

WAZUH_API = "https://localhost:55000"
CREDENTIALS = ("wazuh-wui", "YOUR_PASSWORD")

def get_token() -> str:
    """Obtain a JWT token from the Wazuh API."""
    response = requests.post(
        f"{WAZUH_API}/security/user/authenticate?raw=true",
        auth=CREDENTIALS,
        verify=False,
    )
    response.raise_for_status()
    return response.text

def api_request(method: str, endpoint: str, **kwargs) -> dict:
    """Execute an authenticated API request."""
    token = get_token()
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.request(
        method,
        f"{WAZUH_API}{endpoint}",
        headers=headers,
        verify=False,
        **kwargs,
    )
    response.raise_for_status()
    return response.json()

Pagination, Filtering, and Sorting

Pagination

All list endpoints support pagination:

ParameterDescriptionDefaultMaximum
offsetNumber of items to skip0-
limitNumber of items to return500100000
curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?offset=0&limit=10"

Filtering

The API supports filtering via query parameters:

# Filter agents by status
curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?status=active"

# Filter by multiple parameters
curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?status=active&os.platform=ubuntu"

Search

The search parameter performs a full-text search:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?search=web-server"

Sorting

The sort parameter controls result ordering:

# Sort by name (ascending)
curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?sort=+name"

# Sort by ID (descending)
curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?sort=-id"

Field Selection

The select parameter limits returned fields:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?select=id,name,status,os.name"

Endpoints: Agents

GET /agents

Retrieve the agent list with filtering support:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents?status=active&limit=5" | jq '.data.affected_items[] | {id, name, status}'

Response:

{
  "data": {
    "affected_items": [
      {"id": "001", "name": "web-server-01", "status": "active"},
      {"id": "002", "name": "db-server-01", "status": "active"}
    ],
    "total_affected_items": 2,
    "total_failed_items": 0,
    "failed_items": []
  }
}

GET /agents/{agent_id}

Detailed agent information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/agents/001" | jq '.data.affected_items[0]'

DELETE /agents

Remove agents (supports bulk deletion):

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X DELETE "https://localhost:55000/agents?agents_list=005,006&status=disconnected&older_than=7d"

PUT /agents/{agent_id}/restart

Restart an agent:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X PUT "https://localhost:55000/agents/001/restart"

PUT /agents/group/{group_id}

Assign agents to a group:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X PUT "https://localhost:55000/agents/group/web-servers" \
  -H "Content-Type: application/json" \
  -d '{"agents_list": ["001", "002"]}'

Python SDK - Agent Operations

# List active agents
agents = api_request("GET", "/agents", params={"status": "active", "limit": 100})
for agent in agents["data"]["affected_items"]:
    print(f"Agent {agent['id']}: {agent['name']} ({agent['status']})")

# Restart an agent
api_request("PUT", "/agents/001/restart")

# Get agent OS information
agent_info = api_request("GET", "/agents/001", params={"select": "os.name,os.version,os.platform"})

Endpoints: Manager

GET /manager/info

Wazuh Manager server information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/manager/info" | jq '.data.affected_items[0]'

GET /manager/status

Status of all Wazuh daemons:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/manager/status" | jq '.data.affected_items[0]'

GET /manager/configuration

Current manager configuration:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/manager/configuration?section=global"

GET /manager/logs

Manager logs:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/manager/logs?limit=20&sort=-timestamp"

GET /manager/stats

Event processing statistics:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/manager/stats?date=$(date +%Y-%m-%d)"

Endpoints: Cluster

GET /cluster/status

Cluster status:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/cluster/status"

GET /cluster/nodes

List cluster nodes:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/cluster/nodes" | jq '.data.affected_items[] | {name, type, version}'

GET /cluster/healthcheck

Cluster health check:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/cluster/healthcheck"

GET /cluster/{node_id}/info

Specific node information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/cluster/node01/info"

Endpoints: Rules

GET /rules

List rules:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/rules?limit=10&level=12-15" | jq '.data.affected_items[] | {id, level, description}'

GET /rules/{rule_id}

Rule details:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/rules/5710"

GET /rules/groups

List rule groups:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/rules/groups"

GET /rules/files

List rule files:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/rules/files?status=custom"

PUT /rules/files/{filename}

Update a custom rule file:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X PUT "https://localhost:55000/rules/files/local_rules.xml" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @local_rules.xml

Endpoints: Decoders

GET /decoders

List decoders:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/decoders?limit=10&search=sshd"

GET /decoders/{decoder_name}

Specific decoder information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/decoders/sshd"

GET /decoders/files

List decoder files:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/decoders/files?status=custom"

PUT /decoders/files/{filename}

Update a custom decoder file:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X PUT "https://localhost:55000/decoders/files/local_decoder.xml" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @local_decoder.xml

Endpoints: SCA (Security Configuration Assessment)

GET /sca/{agent_id}

SCA results for an agent:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/sca/001" | jq '.data.affected_items[] | {policy_id, pass, fail, score}'

GET /sca/{agent_id}/checks/{policy_id}

Detailed check results for a policy:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/sca/001/checks/cis_ubuntu22-04" \
  | jq '.data.affected_items[] | select(.result == "failed") | {id, title, result}'

Python SDK - SCA Report

def get_sca_report(agent_id: str) -> list[dict]:
    """Generate an SCA compliance report for an agent."""
    policies = api_request("GET", f"/sca/{agent_id}")
    report = []
    for policy in policies["data"]["affected_items"]:
        total = policy["pass"] + policy["fail"] + policy.get("invalid", 0)
        report.append({
            "policy": policy["policy_id"],
            "score": f"{policy['score']}%",
            "passed": policy["pass"],
            "failed": policy["fail"],
            "total": total,
        })
    return report

Endpoints: Vulnerabilities

GET /vulnerability/{agent_id}

Detected vulnerabilities on an agent:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/vulnerability/001?limit=10&severity=Critical" \
  | jq '.data.affected_items[] | {cve, name, severity, version}'

GET /vulnerability/{agent_id}/summary/{field}

Vulnerability summary by field:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/vulnerability/001/summary/severity"

Python SDK - Vulnerability Report

def get_vulnerability_summary(agent_id: str) -> dict:
    """Get vulnerability summary grouped by severity."""
    vulns = api_request(
        "GET",
        f"/vulnerability/{agent_id}",
        params={"limit": 500},
    )

    summary = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0}
    for vuln in vulns["data"]["affected_items"]:
        severity = vuln.get("severity", "Low")
        summary[severity] = summary.get(severity, 0) + 1

    return summary

Endpoints: Syscollector (System Inventory)

GET /syscollector/{agent_id}/os

Operating system information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/os"

GET /syscollector/{agent_id}/packages

Installed packages:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/packages?limit=20&sort=-size"

GET /syscollector/{agent_id}/ports

Open ports:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/ports?state=listening"

GET /syscollector/{agent_id}/processes

Running processes:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/processes?limit=20&sort=-resident_size"

GET /syscollector/{agent_id}/netiface

Network interfaces:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/netiface"

GET /syscollector/{agent_id}/hardware

Hardware information:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/syscollector/001/hardware"

Wazuh 4.14 introduces extended syscollector fields, including detailed CPU, RAM, and disk subsystem data. Existing fields remain backward-compatible.

Endpoints: Security and RBAC

GET /security/users

List API users:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/security/users"

POST /security/users

Create a user:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X POST "https://localhost:55000/security/users" \
  -H "Content-Type: application/json" \
  -d '{"username": "analyst", "password": "SecureP@ss123!"}'

GET /security/roles

List RBAC roles:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/security/roles"

POST /security/roles

Create a role:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X POST "https://localhost:55000/security/roles" \
  -H "Content-Type: application/json" \
  -d '{"name": "read-only-analyst", "rule": {"FIND": {"r^agent": ["read"]}}}'

POST /security/user/{user_id}/roles

Assign a role to a user:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  -X POST "https://localhost:55000/security/user/100/roles?role_ids=2"

GET /security/policies

List RBAC policies:

curl -sk -H "Authorization: Bearer ${TOKEN}" \
  "https://localhost:55000/security/policies"

Error Codes

The Wazuh API uses standard HTTP codes alongside its own error codes.

HTTP Codes

CodeDescription
200Successful request
400Bad request (invalid parameters)
401Unauthorized (invalid token)
403Forbidden (insufficient RBAC permissions)
404Resource not found
405Method not allowed
429Rate limit exceeded
500Internal server error

Wazuh Error Codes

CodeDescriptionResolution
1000Wazuh Internal ErrorCheck manager logs
1001Error in API callVerify request parameters
1002Resource not foundConfirm the resource exists
1003Permission deniedReview RBAC roles and policies
1017Invalid credentialsVerify username and password
1760Agent already belongs to a groupAgent is already in the specified group

Error Handling in Python

import requests

def safe_api_request(method: str, endpoint: str, **kwargs) -> dict:
    """Execute an API request with error handling."""
    try:
        result = api_request(method, endpoint, **kwargs)
        if result.get("error", 0) != 0:
            error_msg = result.get("message", "Unknown error")
            raise RuntimeError(f"Wazuh API error: {error_msg}")
        return result
    except requests.exceptions.ConnectionError:
        raise RuntimeError("Cannot connect to Wazuh API")
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            raise RuntimeError("Authentication failed - check credentials")
        elif e.response.status_code == 403:
            raise RuntimeError("Access denied - check RBAC permissions")
        raise

Postman Collection

To test the API with Postman, set up a collection with the following structure.

Environment Variables

{
  "wazuh_url": "https://localhost:55000",
  "wazuh_user": "wazuh-wui",
  "wazuh_password": "YOUR_PASSWORD",
  "wazuh_token": ""
}

Pre-request Script (automatic token refresh)

const authUrl = pm.environment.get("wazuh_url") + "/security/user/authenticate?raw=true";

pm.sendRequest({
    url: authUrl,
    method: "POST",
    header: {
        "Authorization": "Basic " + btoa(
            pm.environment.get("wazuh_user") + ":" +
            pm.environment.get("wazuh_password")
        )
    }
}, function(err, response) {
    if (!err) {
        pm.environment.set("wazuh_token", response.text());
    }
});

Recommended Collection Structure

Wazuh API v4.14/
├── Authentication/
│   └── POST Authenticate
├── Agents/
│   ├── GET List Agents
│   ├── GET Agent Details
│   ├── PUT Restart Agent
│   └── DELETE Remove Agents
├── Manager/
│   ├── GET Info
│   ├── GET Status
│   └── GET Logs
├── Rules/
│   ├── GET List Rules
│   ├── GET Rule Details
│   └── PUT Update Rules File
├── Decoders/
│   ├── GET List Decoders
│   └── PUT Update Decoders File
├── SCA/
│   ├── GET Policies
│   └── GET Checks
├── Vulnerability/
│   └── GET Agent Vulnerabilities
├── Syscollector/
│   ├── GET OS Info
│   ├── GET Packages
│   ├── GET Ports
│   └── GET Processes
└── Security/
    ├── GET Users
    ├── POST Create User
    ├── GET Roles
    └── POST Assign Role

API Limits and Constraints

ParameterValue
Maximum limit100000 records
Default token lifetime900 seconds (15 minutes)
Rate limitingConfigurable in API settings
Maximum request body size10 MB
Concurrent connectionsServer configuration dependent

Troubleshooting

API Unreachable

  1. Check the API daemon status:
/var/ossec/bin/wazuh-control status | grep wazuh-apid
  1. Verify port 55000 is open:
ss -tlnp | grep 55000
  1. Review API logs:
tail -f /var/ossec/logs/api.log

401 Authentication Error

  • Confirm the user exists: GET /security/users
  • Verify the password is correct
  • Check that the token has not expired (default lifetime is 15 minutes)

403 Authorization Error

  • Review assigned roles: GET /security/users/{user_id}/roles
  • Confirm the RBAC policy permits the requested action
  • For debugging, temporarily use the wazuh user with full permissions

Slow API Responses

  • Use select to limit returned fields
  • Reduce limit for paginated requests
  • Apply filters to narrow the result set
  • Check the manager server load

For building custom integrations with the API, see the Custom Integration Development section. An overview of built-in integrations is available in the Integrations section.

Last updated on