Skip to main content

Guard API

A REST API for starting security scans, retrieving reports and reading platform statistics. All responses are JSON.

Base URL

https://guard.report

Rate limits

Per IP address
3 requests per hour
Per domain
1 scan per 24 hours

Responses

Successful requests return the resource itself as a JSON object. Failed requests return an error message with a 4xx or 5xx status; 429 means a rate limit was reached.

{
  "error": "Invalid URL"
}

Scores

securityScore
Security score, 0–100, higher is better. Preferred field. The average of the security checks that completed, capped at 49 while a critical finding stands and at 75 for partial scans. NIS2 and AI readings are not included.
riskScore
The same value as securityScore, kept for existing integrations. Despite the name, higher is better.
riskLevel
Derived from the score: critical 0–25, high 26–49, medium 50–75, low 76–100.
scoring
How the score was produced: model (currently 3), the modules averaged, the caps applied and whether the scan was partial. Absent on reports scored before model 2; compare scores only within one model.
modules.*.score
Each check on its own 0–100 scale, higher is better. Only present as a result when status is "completed"; a check that did not run is not a pass or a fail.

Quick start

Start a scan, poll until the report is finished, then read the score.

// 1. Start a scan
const scanResponse = await fetch('https://guard.report/api/analyze', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://example.com' })
});
const { domain } = await scanResponse.json();

// 2. Poll until the report is finished (a scan typically takes 30–60 seconds)
let report;
do {
  await new Promise(resolve => setTimeout(resolve, 5000));
  const reportResponse = await fetch(`https://guard.report/api/reports/${domain}`);
  report = await reportResponse.json();
} while (!['completed', 'failed'].includes(report.status));

// 3. Read the score
console.log('Security score:', report.securityScore, report.riskLevel);

Scanning

POST/api/analyze

Start a scan of a website. If a scan of the domain is already running, the response points to that report.

Parameters

urlstring
Required. Website URL including the protocol, such as https://example.com. Reports are per registrable domain: a subdomain or path is scanned as https://example.com (www is kept).
rescanboolean
Optional. Start a new scan even when a recent report exists.

Request

curl -X POST https://guard.report/api/analyze \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

Response

{
  "domain": "example.com",
  "reportId": "123e4567-e89b-12d3-a456-426614174000",
  "message": "Analysis started"
}

GET/api/reports/[domain]

Latest report for a domain. While the report is being prepared the endpoint answers 202 with status "initializing"; a running scan reports "pending" or "analyzing"; a domain that has never been scanned answers 404.

Response

{
  "domain": "example.com",
  "url": "https://example.com",
  "status": "completed",
  "securityScore": 85,
  "riskScore": 85,
  "riskLevel": "low",
  "scoring": {
    "model": 3,
    "modules": ["sslTls", "securityHeaders", "emailSecurity", "dnsHealth", "networkSecurity", "webHygiene", "gdpr"],
    "caps": [],
    "partial": false
  },
  "summary": "…",
  "modules": {
    "securityHeaders": { "score": 80, "status": "completed" },
    "sslTls": { "score": 95, "status": "completed" }
  }
}

Statistics

GET/api/stats

Platform counters for recent scans. gdprStrongOutsideView and nis2StrongOutsideView count sites whose indicative outside-view score is 80 or more; they are not compliance figures. gdprCompliant and nis2Compliant are deprecated aliases with the same values.

Response

{
  "totalScans": 1200,
  "activeScans": 0,
  "gdprStrongOutsideView": 240,
  "nis2StrongOutsideView": 3,
  "criticalIssues": 15,
  "scansLastWeek": 40,
  "topLocations": [{ "country": "Latvia", "count": 12 }],
  "latestScore": 75,
  "scoreTrend": "up"
}

GET/api/stats/detailed

Aggregates for scans started within a time range. averageSecurityScore (preferred) and averageRiskScore are the same 0–100 average, higher is better, over full scans only; partial scans are left out.

Parameters

rangestring
Optional. 24h, 7d, 30d or 90d. Defaults to 30d.

Response

{
  "totalScans": 1200,
  "completedScans": 1150,
  "averageSecurityScore": 55,
  "averageRiskScore": 55,
  "issueDistribution": { "critical": 10, "high": 300, "medium": 350, "low": 280 },
  "topIssues": [{ "title": "DNSSEC Not Enabled", "module": "dnsHealth", "count": 40, "severity": "medium" }],
  "moduleStats": { "sslTls": { "avgScore": 48, "failureRate": 62 } },
  "industryBreakdown": [{ "industry": "Technology", "count": 20, "avgScore": 57 }],
  "countryBreakdown": [{ "country": "Latvia", "count": 12, "avgScore": 60 }]
}

Directory

GET/api/directory/paginated

Search and filter the public report directory.

Parameters

pagenumber
Optional. Page number. Defaults to 1.
limitnumber
Optional. Reports per page. Defaults to 50, maximum 200.
searchstring
Optional. Full-text search, such as a domain or company name.
industrystring
Optional. Industry name exactly as listed, such as Technology.
countrystring
Optional. Country name exactly as listed, such as Latvia.
riskLevelstring
Optional. critical, high, medium or low.
minScorenumber
Optional. Lowest security score to include, 0–100.
maxScorenumber
Optional. Highest security score to include, 0–100.
sortBystring
Optional. date, score, domain or industry. Defaults to date. score with sortOrder=asc lists the weakest sites first.
sortOrderstring
Optional. asc or desc. Defaults to desc.

Request

curl "https://guard.report/api/directory/paginated?riskLevel=critical&limit=20"

Response

{
  "reports": [
    {
      "reportId": "123e4567-e89b-12d3-a456-426614174000",
      "domain": "example.com",
      "securityScore": 85,
      "riskScore": 85,
      "riskLevel": "low",
      "createdAt": "2026-01-15T10:30:00Z",
      "businessInfo": { "industry": "Technology", "country": "Latvia" }
    }
  ],
  "pagination": { "page": 1, "limit": 20, "totalCount": 412, "totalPages": 21, "hasMore": true }
}

GET/api/directory

The most recent reports, newest first, without filters or pagination.

Parameters

limitnumber
Optional. Number of reports. Defaults to 50, maximum 200.

Response

{
  "reports": [{ "reportId": "…", "domain": "example.com", "securityScore": 85, "riskScore": 85, "riskLevel": "low" }]
}