SecRecon REST API
The SecRecon API lets you trigger security scans, poll results, and integrate SSRF/CVE findings directly into your CI/CD pipelines, asset management systems, or custom dashboards. All endpoints are served over HTTPS and return JSON.
Base URL: https://secrecon.dev · All paths below are relative to this base.
Plan requirement: Programmatic API access (/api/v1/*) requires a Business or Enterprise plan. Dashboard and agent endpoints use session cookies.
Authentication
All /api/v1/* requests must include an API key in the X-API-Key header. Generate keys in Dashboard → Settings → API Access .
Every request must include:
Copy
X-API-Key : sra_your_api_key_here
Content-Type : application/json
Keys are prefixed sra_ and shown once at creation. Store them in environment variables or a secrets manager — never in source code.
Generating an API Key
Open Dashboard in the sidebar.
Click Settings in the left navigation.
Scroll to API Access .
Click Generate New API Key .
Copy the key immediately — it will not be shown again.
Plans & Rate Limits
Plan Monthly Scans API Access Reports Stored
Professional 3 ✗ Not available 10
Business 5 ✓ Included 50
Enterprise Unlimited ✓ Included Unlimited
When a Business account exhausts its monthly credits the API returns HTTP 429. Enterprise accounts are never rate-limited on scan count.
Error Handling
All errors return a JSON body with a detail field describing the problem.
Status Meaning
400 Bad request — missing or invalid fields (e.g. no target)
401 Missing X-API-Key header
403 Invalid key, wrong plan tier, or target blocked by scope policy
429 Monthly scan credit exhausted (Business plan)
500 Internal error — contact support with the scan_id if available
// Example error body
{
"detail" : "API access requires a Business or Enterprise plan (current: professional)."
}
Endpoints
Queues a security scan against the specified target. Consumes one scan credit. Returns immediately with a scan_id — poll the status URL for results.
Request Body
Field Type Description
targetrequired string Domain or full URL to scan — e.g. example.com or https://api.example.com
scan_modeoptional string "quick" (default) or "full". Full mode enables subdomain crawling and active CVE verification.
Response 202 Accepted
{
"scan_id" : "a1b2c3d4e5f6..." , // WebSocket / status polling ID
"db_scan_id" : 42 , // Permanent report ID
"target" : "https://example.com" ,
"status" : "running" ,
"plan" : "business" ,
"status_url" : "/api/scan/a1b2c3.../status" ,
"results_url" : "/api/scan/42" ,
"message" : "Scan started. Poll status_url for progress."
}
Returns the live status of a running scan. Use the scan_id (not db_scan_id) returned by POST /api/v1/scan. No authentication needed for scans owned by the key holder.
Response 200
{
"status" : "complete" , // "starting" | "running" | "complete" | "error"
"phase" : "synthesis" ,
"current_message" : "Generating report..." ,
"current_progress" : 95 ,
"vulnerabilities_found" : 3 ,
"services_found" : 8 ,
"db_scan_id" : 42
}
Returns the complete scan report including all findings, CVEs, KEV matches, OAST interactions, and the AI-generated narrative. Use the db_scan_id (integer) from the scan response.
Key fields in response
Field Type Description
findings array All vulnerabilities found, each with severity, cve_id, KEV_Status, confidence_score, oast_verified
kev_count int Number of findings matching the CISA Known Exploited Vulnerabilities catalog
cve_count int Total CVEs identified across all services
risk_score string "A"–"F" overall risk grade
report_text string AI-generated full narrative security report
oast_interactions array Out-of-band DNS/HTTP callbacks received during scan
Returns all assets discovered across all your scans — domains, subdomains, IPs, and sensitive paths. Requires session cookie (same as dashboard).
Supports optional query param ?type=domain|subdomain|ip|sensitive_path to filter by asset type.
Returns the current OAST (out-of-band) callback count — DNS and HTTP hits received by the Interactsh listener for the current session.
{
"active" : true ,
"oast_url" : "https://abc123.oast.pro" ,
"dns" : 2 ,
"http" : 1 ,
"total" : 3
}
Code Examples
cURL — Start a scan & poll for results
Copy
# 1. Start scan
SCAN =$(curl -s -X POST https://secrecon.dev/api/v1/scan \
-H "X-API-Key: sra_your_key" \
-H "Content-Type: application/json" \
-d '{"target":"https://example.com","scan_mode":"quick"}' )
SCAN_ID =$(echo $SCAN | python3 -c "import sys,json; print(json.load(sys.stdin)['scan_id'])" )
DB_ID =$(echo $SCAN | python3 -c "import sys,json; print(json.load(sys.stdin)['db_scan_id'])" )
# 2. Poll status until complete
while true; do
STATUS=$(curl -s https://secrecon.dev/api/scan/$SCAN_ID/status | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])" )
echo "Status: $STATUS"
[ "$STATUS" = "complete" ] && break
sleep 15
done
# 3. Fetch full results
curl -s https://secrecon.dev/api/scan/$DB_ID | python3 -m json.tool
Python
Copy
import requests, time
API_KEY = "sra_your_key"
BASE = "https://secrecon.dev"
HEADERS = {"X-API-Key" : API_KEY, "Content-Type" : "application/json" }
# Start scan
resp = requests.post(f"{BASE}/api/v1/scan" , json={"target" : "https://example.com" }, headers=HEADERS)
resp.raise_for_status()
data = resp.json()
scan_id = data["scan_id" ]
db_id = data["db_scan_id" ]
print(f"Scan started: {scan_id}" )
# Poll until done
while True:
s = requests.get(f"{BASE}/api/scan/{scan_id}/status" , headers=HEADERS).json()
print(f"Phase: {s.get('phase')} — {s.get('current_message','')}" )
if s["status" ] in ("complete" , "error" ):
break
time.sleep(15 )
# Fetch results
results = requests.get(f"{BASE}/api/scan/{db_id}" , headers=HEADERS).json()
findings = results.get("findings" , [])
kev_findings = [f for f in findings if f.get("KEV_Status" ) == "Active_Exploit" ]
print(f"Risk: {results.get('risk_score')} | Findings: {len(findings)} | KEV: {len(kev_findings)}" )
Node.js
Copy
const API_KEY = "sra_your_key" ;
const BASE = "https://secrecon.dev" ;
const headers = { "X-API-Key" : API_KEY, "Content-Type" : "application/json" };
async function runScan(target) {
// Start
const start = await fetch(`${BASE}/api/v1/scan`, {
method: "POST" , headers, body: JSON.stringify({ target, scan_mode: "quick" })
});
const { scan_id, db_scan_id } = await start.json();
// Poll
let done = false ;
while (!done) {
await new Promise(r => setTimeout(r, 15000 ));
const s = await (await fetch(`${BASE}/api/scan/${scan_id}/status`, { headers })).json();
console.log(`Phase: ${s.phase} — ${s.current_message}`);
done = ["complete" , "error" ].includes(s.status);
}
// Results
const res = await (await fetch(`${BASE}/api/scan/${db_scan_id}`, { headers })).json();
const kev = res.findings?.filter(f => f.KEV_Status === "Active_Exploit" ) ?? [];
console.log(`Risk: ${res.risk_score} | Findings: ${res.findings?.length} | CISA KEV: ${kev.length}`);
}
runScan("https://example.com" );
CI/CD Pipeline (GitHub Actions)
Copy
# .github/workflows/security-scan.yml
name : SecRecon Security Scan
on :
push :
branches : [main ]
schedule :
- cron : "0 2 * * 1" # Weekly on Monday 02:00 UTC
jobs :
scan :
runs-on : ubuntu-latest
steps :
- name : Start SecRecon Scan
id : scan
run : |
RESP=$(curl -sf -X POST https://secrecon.dev/api/v1/scan \
-H "X-API-Key: ${{ secrets.SECRECON_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"target":"${{ vars.SCAN_TARGET }}","scan_mode":"full"}')
echo "scan_id=$(echo $RESP | jq -r .scan_id)" >> $GITHUB_OUTPUT
echo "db_id=$(echo $RESP | jq -r .db_scan_id)" >> $GITHUB_OUTPUT
- name : Wait for completion
run : |
for i in $(seq 1 40); do
STATUS=$(curl -sf https://secrecon.dev/api/scan/${{ steps.scan.outputs.scan_id }}/status | jq -r .status)
echo "Attempt $i: $STATUS"
[ "$STATUS" = "complete" ] && exit 0
sleep 30
done; exit 1
- name : Fail if critical KEV findings
run : |
KEV=$(curl -sf https://secrecon.dev/api/scan/${{ steps.scan.outputs.db_id }} \
| jq '[.findings[] | select(.KEV_Status=="Active_Exploit")] | length')
echo "CISA KEV findings: $KEV"
[ "$KEV" -gt "0" ] && echo "::error::$KEV CISA KEV finding(s) detected!" && exit 1 || exit 0