API Reference

Trigger scans, retrieve findings, and integrate security data into your pipeline.

Authentication

All API v1 endpoints require an API key passed as a Bearer token. You can create API keys from your dashboard settings (Watchtower plan).

Authorization: Bearer avk_your_api_key_here

Keys use the avk_ prefix followed by 64 hex characters. The full key is shown once on creation and stored as a one-way hash. Rate limits are per-key, reset daily at UTC midnight.

Base URL

https://attackerview.com/api/v1

Response format

All endpoints return a consistent envelope.

// Success
{
  "data": { ... },
  "meta": {
    "requestId": "req_abc123...",
    "timestamp": "2026-01-15T10:30:00.000Z",
    "rateLimit": { "remaining": 994, "reset": "2026-01-16T00:00:00.000Z" }
  }
}

// Error
{
  "error": {
    "code": "DOMAIN_NOT_FOUND",
    "message": "Domain not found",
    "requestId": "req_abc123..."
  }
}

Trigger a scan

POST /scans

Start a new scan for a domain that already exists in your account. Returns immediately with a scan ID you can poll.

Request body

{
  "hostname": "example.com",
  "port": 443,
  "scheme": "https",
  "compare_to": "previous"
}
  • hostname string, required - The domain to scan. Must already exist in your account.
  • port number, optional - Port to target (1-65535). Defaults to 443.
  • scheme string, optional - http or https. Defaults to https.
  • compare_to string, optional - Set to "previous" to include a scan-to-scan diff when you later GET the scan result.

Response 201 Created

{
  "data": {
    "scanId": "clx...",
    "status": "queued",
    "hostname": "example.com"
  },
  "meta": { "requestId": "...", "timestamp": "...", "rateLimit": { ... } }
}

Errors

  • 404 - Domain not found in your account.
  • 409 - A scan is already running for this domain.
  • 429 - Rate limit exceeded (max 10 scans/domain/day).

Get scan result

GET /scans/:scanId

Returns the scan status, findings with compliance tags, and a severity summary. Poll this until status is "completed".

Query parameters

  • compare_to optional - Set to "previous" to include a diff against the previous completed scan. Also activates if compare_to was set when the scan was created.

Response 200 OK

{
  "data": {
    "scan": {
      "id": "clx...",
      "status": "completed",
      "hostname": "example.com",
      "createdAt": "2026-01-15T10:30:00Z",
      "updatedAt": "2026-01-15T10:31:12Z"
    },
    "findings": [
      {
        "id": "clx...",
        "checkId": "tls-protocol",
        "status": "open",
        "severity": "medium",
        "title": "Legacy TLS protocol supported",
        "evidence": "Server accepts TLS 1.0/1.1 connections",
        "compliance": [
          { "framework": "soc2", "controlId": "CC6.1" },
          { "framework": "pciDss", "controlId": "4.2.1" }
        ]
      }
    ],
    "summary": {
      "total": 5,
      "bySeverity": { "critical": 1, "high": 2, "medium": 1, "low": 1 }
    },
    "diff": {
      "comparedTo": "clx_previous_scan_id",
      "checks": {
        "new": 1, "newDetections": 0, "fixed": 2, "regressed": 0,
        "details": {
          "new": [{ "id": "csp-missing", "title": "No CSP header", "impact": "medium" }],
          "fixed": [...],
          "regressed": []
        }
      },
      "components": { "added": 1, "removed": 0, "versionChanged": 1 },
      "entryPoints": { ... }
    }
  },
  "meta": { ... }
}

The diff field is only present when compare_to=previous is active and the scan is completed.

Scan statuses

  • queued - Waiting to start.
  • running - Scan started, resolving DNS and TLS.
  • mapping - Crawling and analyzing.
  • completed - Done. Findings available.
  • failed - Scan errored out.
  • cancelled - Scan was stopped before completion.

Retest specific checks

POST /scans/:scanId/retest

Re-run specific checks against a completed scan. Useful for verifying a fix without running a full scan.

Request body

{
  "checkIds": ["tls-cert-expiring", "security-headers-hsts"]
}
  • checkIds string[], required - Check IDs to retest. These are resolved to analyzer groups server-side.

Response 202 Accepted

{
  "data": {
    "jobId": "clx...",
    "scanId": "clx...",
    "existed": false,
    "analyzerIds": ["tls", "security-headers"],
    "checkIds": ["tls-cert-expiring", "security-headers-hsts"]
  },
  "meta": { ... }
}

existed is true if an identical retest job was already queued (idempotent).

Errors

  • 400 - Missing or empty checkIds, or scan not completed.
  • 404 - Scan not found.

List domains

GET /domains

Returns all domains in your account with their latest completed scan and open finding counts.

Response 200 OK

{
  "data": {
    "domains": [
      {
        "hostname": "example.com",
        "createdAt": "2026-01-10T08:00:00Z",
        "latestScan": {
          "id": "clx...",
          "status": "completed",
          "createdAt": "2026-01-15T10:30:00Z"
        },
        "findings": {
          "open": { "high": 2, "medium": 3, "low": 1 }
        }
      }
    ]
  },
  "meta": { ... }
}

latestScan is null if no completed scan exists. findings.open only includes severities with at least one open finding.

Get domain detail

GET /domains/:hostname

Returns domain detail, latest scan, finding summary, and Watchtower monitoring status.

Response 200 OK

{
  "data": {
    "domain": {
      "hostname": "example.com",
      "createdAt": "2026-01-10T08:00:00Z",
      "watchtower": {
        "enabled": true,
        "nextCheckAt": "2026-01-16T09:00:00Z"
      }
    },
    "latestScan": {
      "id": "clx...",
      "status": "completed",
      "createdAt": "2026-01-15T10:30:00Z",
      "updatedAt": "2026-01-15T10:31:12Z"
    },
    "findings": {
      "totalOpen": 6,
      "bySeverity": { "high": 2, "medium": 3, "low": 1 }
    }
  },
  "meta": { ... }
}

Errors

  • 404 - Domain not found in your account.

List domain findings

GET /domains/:hostname/findings

Returns findings for a domain with compliance tags. Supports filtering and cursor-based pagination.

Query parameters

  • status optional - Filter by finding status (open, fixed, accepted).
  • severity optional - Filter by severity (critical, high, medium, low).
  • checkId optional - Filter by check ID (e.g. tls-cert-expiring).
  • limit optional - Results per page, 1-500. Default 100.
  • cursor optional - Pagination cursor from previous response.

Response 200 OK

{
  "data": {
    "findings": [
      {
        "id": "clx...",
        "checkId": "dmarc-missing",
        "status": "open",
        "severity": "high",
        "title": "No DMARC record found",
        "evidence": "No DMARC TXT record at _dmarc.example.com",
        "firstSeenAt": "2026-01-10T08:00:00Z",
        "lastSeenAt": "2026-01-15T10:31:12Z",
        "compliance": [
          { "framework": "iso27001", "controlId": "A.8.8" }
        ]
      }
    ],
    "nextCursor": "clx..."
  },
  "meta": { ... }
}

Pass nextCursor as the cursor param to get the next page. null when no more results.

Get surface graph

GET /surface/:hostname

Returns a domain's connection map: nodes are related domains, IPs, certificates, and nameservers; edges are the relationships between them.

Response 200 OK

{
  "data": {
    "nodes": [
      {
        "id": "clx...",
        "nodeType": "hostname",
        "key": "example.com",
        "label": "example.com",
        "properties": { ... },
        "firstSeenAt": "2026-01-10T08:00:00Z",
        "lastSeenAt": "2026-01-15T10:30:00Z"
      }
    ],
    "edges": [
      {
        "id": "clx...",
        "edgeType": "resolves_to",
        "sourceId": "clx_node_a",
        "targetId": "clx_node_b",
        "properties": { ... },
        "firstSeenAt": "2026-01-10T08:00:00Z",
        "lastSeenAt": "2026-01-15T10:30:00Z"
      }
    ]
  },
  "meta": { ... }
}

Node types

  • hostname - A domain or subdomain.
  • ip - An IP address.
  • certificate - A TLS certificate.
  • nameserver - A DNS nameserver.

Errors

  • 404 - Domain not found in your account.

Get discovery events

GET /surface/:hostname/drift

Returns discovery events for a domain, ordered by sequence number. Use cursor-based pagination to stream changes over time.

Query parameters

  • after optional - Cursor (eventSeq) from previous response. Returns events after this sequence number.
  • limit optional - Results per page, 1-200. Default 50.

Response 200 OK

{
  "data": {
    "events": [
      {
        "id": "clx...",
        "eventSeq": "12345",
        "eventType": "node.appeared",
        "eventVersion": 1,
        "payload": { "nodeType": "hostname", "key": "api.example.com" },
        "createdAt": "2026-01-15T10:30:00Z",
        "runId": "clx..."
      }
    ],
    "cursor": "12345"
  },
  "meta": { ... }
}

Pass cursor as the after param for the next page. When no more events exist, events is empty and cursor holds the last known position.

Errors

  • 400 - Invalid cursor format.
  • 404 - Domain not found in your account.

OpenAPI spec

GET /docs

Returns the full OpenAPI 3.0 spec as JSON. No authentication required.

Rate limits

Rate limits are per API key, reset daily at UTC midnight. Limits depend on your plan. When you hit the limit, the API returns 429 Too Many Requests.

PlanDaily requests
Watchtower1,000
Hunter10,000

Every response includes rate limit info in the meta.rateLimit object and in headers: X-RateLimit-Remaining and X-RateLimit-Reset.

CORS

All v1 endpoints support CORS with the Authorization header allowed. You can call the API from browser-based applications.

Quick start

Trigger a scan and poll for results:

# Trigger a scan
curl -X POST https://attackerview.com/api/v1/scans \
  -H "Authorization: Bearer avk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"hostname": "example.com"}'

# Check status (poll until status is "completed")
curl https://attackerview.com/api/v1/scans/SCAN_ID \
  -H "Authorization: Bearer avk_your_key"

# List all open findings for a domain
curl "https://attackerview.com/api/v1/domains/example.com/findings?status=open" \
  -H "Authorization: Bearer avk_your_key"

Or use the CLI or GitHub Actions for a simpler workflow.