API Reference

Manage targets, trigger scans, and retrieve findings programmatically.

Overview

The Secably EASM API is a RESTful JSON API. All endpoints are under:

https://secably.com/api/v1/easm/

All requests and responses use application/json. Dates are ISO 8601 format in UTC.

API access requires the Pro plan or higher. View plans.

Authentication

Two authentication methods are supported:

API Key Recommended

Create API keys in Dashboard → API Keys. Pass the key in the X-API-Key header:

curl -H "X-API-Key: easm_your_key_here" \
  https://secably.com/api/v1/easm/targets/

API keys support scoped permissions:

ScopeDescription
targets:readList and view targets, assets, findings, alerts
targets:writeCreate, update, and delete targets
scans:triggerTrigger on-demand scans

JWT Token

Obtain a token pair via the auth endpoint:

curl -X POST https://secably.com/api/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "..."}'

# Response:
{"access": "eyJ...", "refresh": "eyJ..."}

# Use the access token:
curl -H "Authorization: Bearer eyJ..." \
  https://secably.com/api/v1/easm/targets/

Errors

Errors return a JSON object with an error field:

{"error": "Target limit reached (10). Upgrade your plan."}
CodeMeaning
400Bad request — invalid parameters
401Unauthorized — missing or invalid credentials
403Forbidden — plan limit reached or insufficient scope
404Not found
409Conflict — e.g., scan already in progress
429Rate limited

Rate Limits

PlanLimit
Pro100 requests/hour
Business100 requests/hour
MSSP100 requests/hour

Rate limit headers (X-RateLimit-Remaining) are included in responses.


Targets

GET /api/v1/easm/targets/

List all active targets in your organization.

Response
[
  {
    "id": 1,
    "domain": "example.com",
    "status": "active",
    "scan_schedule": "daily",
    "risk_score": 34,
    "risk_grade": "B",
    "subdomain_count": 20,
    "open_port_count": 6,
    "vulnerability_count": 8,
    "critical_count": 0,
    "high_count": 2,
    "medium_count": 4,
    "low_count": 2,
    "last_scan_at": "2026-03-13T22:52:00Z",
    "created_at": "2026-03-01T10:00:00Z",
    "client": "Acme Corp"
  }
]
POST /api/v1/easm/targets/

Add a new target domain to monitor.

Parameters
FieldTypeRequiredDescription
domainstringYesRoot domain to monitor (e.g., example.com)
scan_schedulestringNomanual, hourly, daily, or weekly. Default: manual
client_idintegerNoAssign to a client (MSSP organizations only)
Example
curl -X POST https://secably.com/api/v1/easm/targets/ \
  -H "X-API-Key: easm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com", "scan_schedule": "daily"}'

# 201 Created
{"id": 5, "domain": "example.com", "status": "active"}
Errors
  • 400 — Invalid domain format, IP address, or domain already exists
  • 403 — Target limit reached for your plan
GET /api/v1/easm/targets/{id}/

Retrieve a single target with full details.

curl -H "X-API-Key: easm_your_key" \
  https://secably.com/api/v1/easm/targets/1/
PUT /api/v1/easm/targets/{id}/

Update target settings.

FieldTypeDescription
scan_schedulestringmanual, hourly, daily, weekly
statusstringactive or paused
curl -X PUT https://secably.com/api/v1/easm/targets/1/ \
  -H "X-API-Key: easm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"scan_schedule": "hourly"}'
DELETE /api/v1/easm/targets/{id}/

Delete a target and all associated data (subdomains, ports, findings, alerts). This action is irreversible.

curl -X DELETE -H "X-API-Key: easm_your_key" \
  https://secably.com/api/v1/easm/targets/1/

# 204 No Content

Scans

POST /api/v1/easm/targets/{id}/scan/

Trigger an on-demand scan for a target.

FieldTypeDescription
scan_typestringquick (default) or deep. Deep scans run all nuclei templates.
curl -X POST https://secably.com/api/v1/easm/targets/1/scan/ \
  -H "X-API-Key: easm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"scan_type": "deep"}'

# 200 OK
{
  "scan_id": "easm_a1b2c3d4e5f67890",
  "status": "running",
  "target": "example.com",
  "scan_type": "deep"
}
Errors
  • 409 — Scan already in progress for this target
  • 502 — Failed to dispatch scan to scanner node

Assets

GET /api/v1/easm/targets/{id}/assets/

List all discovered assets for a target.

Query ParamDescription
typeFilter by asset type: subdomain, port, certificate, dns_record, technology, email_security
curl -H "X-API-Key: easm_your_key" \
  "https://secably.com/api/v1/easm/targets/1/assets/?type=subdomain"

[
  {
    "id": 42,
    "type": "subdomain",
    "value": "api.example.com",
    "metadata": {"ip": "104.21.50.23", "http_status": 200},
    "risk_score": 15,
    "is_active": true,
    "first_seen": "2026-03-01T10:05:00Z",
    "last_seen": "2026-03-13T22:52:00Z"
  }
]

Findings

GET /api/v1/easm/targets/{id}/findings/

List open vulnerability findings for a target.

Query ParamDescription
severityFilter: critical, high, medium, low, info
curl -H "X-API-Key: easm_your_key" \
  "https://secably.com/api/v1/easm/targets/1/findings/?severity=high"

[
  {
    "id": 7,
    "title": "Exposed Redis on Port 6379",
    "description": "Redis instance is accessible without authentication...",
    "severity": "high",
    "category": "port_service",
    "cve_id": null,
    "cvss_score": 7.5,
    "remediation": "Bind Redis to 127.0.0.1 and enable AUTH...",
    "status": "open",
    "first_detected": "2026-03-10T14:00:00Z",
    "last_detected": "2026-03-13T22:52:00Z"
  }
]

Alerts

GET /api/v1/easm/alerts/

List the 100 most recent alerts across all targets.

curl -H "X-API-Key: easm_your_key" \
  https://secably.com/api/v1/easm/alerts/

[
  {
    "id": 15,
    "target": "example.com",
    "alert_type": "new_subdomain",
    "severity": "info",
    "title": "New subdomain discovered: backup.example.com",
    "description": "...",
    "details": {"subdomain": "backup.example.com", "ip": "104.21.218.246"},
    "is_read": false,
    "created_at": "2026-03-13T22:55:00Z"
  }
]
Alert Types
TypeDescription
new_subdomainNew subdomain discovered
new_portNew open port detected
new_vulnerabilityNew vulnerability found
ssl_expirySSL certificate expiring soon
risk_changeRisk grade changed
service_changeService/technology changed

Webhooks

Events

Configure webhooks in Dashboard → Webhooks. Secably sends POST requests to your URL when events occur:

EventTriggered when
scan.completedA scan finishes
finding.newA new vulnerability is detected
finding.resolvedA finding changes to resolved
alert.createdA new alert fires
risk.changedA target's risk grade changes

Payload & Verification

Each delivery includes an HMAC-SHA256 signature in the X-Secably-Signature header. Verify it against your webhook secret:

# Python verification example
import hmac, hashlib

def verify_webhook(payload_body, signature, secret):
    expected = hmac.new(
        secret.encode(), payload_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Example payload:

{
  "event": "finding.new",
  "timestamp": "2026-03-13T22:55:00Z",
  "data": {
    "target": "example.com",
    "finding": {
      "id": 7,
      "title": "Exposed Redis on Port 6379",
      "severity": "high",
      "cvss_score": 7.5
    }
  }
}

Failed deliveries are retried 3 times with exponential backoff (1m, 5m, 30m).


Quick Start

# Python example — list targets & trigger a scan
import requests

API_KEY = "easm_your_key_here"
BASE = "https://secably.com/api/v1/easm"
headers = {"X-API-Key": API_KEY}

# List all targets
targets = requests.get(f"{BASE}/targets/", headers=headers).json()
for t in targets:
    print(f"{t['domain']} — Grade: {t['risk_grade']}, Vulns: {t['vulnerability_count']}")

# Add a new target
new = requests.post(f"{BASE}/targets/", headers=headers,
    json={"domain": "newdomain.com", "scan_schedule": "daily"}).json()
print(f"Created target #{new['id']}")

# Trigger a deep scan
scan = requests.post(f"{BASE}/targets/{new['id']}/scan/", headers=headers,
    json={"scan_type": "deep"}).json()
print(f"Scan {scan['scan_id']} is {scan['status']}")

# Get findings
findings = requests.get(f"{BASE}/targets/{new['id']}/findings/", headers=headers).json()
for f in findings:
    print(f"[{f['severity'].upper()}] {f['title']}")

Ready to integrate?

Create your API key and start automating your attack surface monitoring.

Get Started Free