API & Developer Docs
Trigger scans programmatically, integrate into CI pipelines, or let your AI agent run security checks. No API key needed.
Quick start
Preferred 2-step workflow
Scanning is asynchronous. Start a scan, then poll status with format=agent until the top-level status is completed or error. Terminal status responses include result inline.
Step 1 — Start the scan
curl -X POST https://vibecheckscan.com/api/scan/start \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
# Response
{"scanId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "queued": false}Step 2 — Poll status until complete
curl "https://vibecheckscan.com/api/scan/status?id=a1b2c3d4e5f6...&format=agent" # Stop when top-level status is "completed" or "error". # The final response includes result, so you usually do not need /api/scan/result.
Fallback — Fetch the result separately
curl "https://vibecheckscan.com/api/scan/result?id=a1b2c3d4e5f6..." # Use this if the terminal status response did not include result # (for example after a later lookup, or a different instance).
Endpoints
API reference
/api/scan/startRate limited: 5/min per IPInitiates an asynchronous scan. Returns a scanId immediately — the actual scan runs in the background.
Request body
{
"url": "https://example.com" // Required. Must be http or https. No private IPs.
}Response
{
"scanId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", // 32-char hex — save this
"queued": false // true if waiting for capacity
}/api/scan/status?id={scanId}Returns the current status of each module. Poll every 2–3 seconds until all modules report done or error.
{
"status": "running",
"modules": {
"headers": {"status": "done"}, // pending | running | done | error
"tls": {"status": "running"},
"cors": {"status": "pending"},
"apiSurface": {"status": "pending"}
}
}/api/scan/result?id={scanId}&format=fullRetrieves the completed report. Add ?format=summary for a condensed version with scores and counts only, or ?format=fix for a Markdown task list you can paste into a coding agent to investigate and fix the findings. Results expire after 24 hours.
{
"scanId": "a1b2c3d4...",
"url": "https://example.com",
"status": "completed",
"overallGrade": "B", // A B C D F
"overallScore": 82, // 0–100 weighted
"modules": {
"headers": {
"score": 27, "maxScore": 37, // dynamic denominator: unknown / not_applicable checks are excluded
"coverage": { "evaluatorVersion": "headers-eval/1", "responseContext": "HTML document, HTTP 200", "limitations": ["..."] },
"findings": {
// assessment: effective | weak | missing | invalid | unknown | not_applicable
"csp": {"assessment": "weak", "reason": "csp.script.unsafe_inline_no_nonce", "score": 5, "maxScore": 14, "present": false},
"hsts": {"assessment": "effective", "reason": "hsts.enforced", "score": 8, "maxScore": 8, "present": true},
"xFrameOptions": {"assessment": "missing", "reason": "framing.absent", "score": 0, "maxScore": 6, "present": false},
"xContentTypeOptions":{"assessment": "effective", "reason": "nosniff.on", "score": 5, "maxScore": 5, "present": true},
"referrerPolicy": {"assessment": "effective", "reason": "referrer.browser_default", "source": "browser_default", "score": 4, "maxScore": 4, "present": true},
"permissionsPolicy": {"assessment": "missing", "reason": "permissions.informational", "gradeEligible": false, "score": 0, "maxScore": 0, "present": false}
}
},
"tls": {
"score": 28, "maxScore": 30,
"findings": {
"protocol": {"version": "TLSv1.3", "score": 10},
"certExpiry": {"valid": true, "daysUntilExpiry": 180, "score": 8},
"trustChain": {"valid": true, "score": 5},
"cipherStrength":{"strength": "strong", "score": 5}
}
},
"cors": {
"score": 18, "maxScore": 20,
"findings": {
"wildcardOrigin": false,
"credentialsWithWildcard": false,
"safeOriginPolicy": true,
"localhostAllowed": false,
"nullOriginAllowed": false,
"dynamicAllowlistHits": [],
"probes": [...]
}
},
"apiSurface": {
"score": 9, "maxScore": 10,
"findings": [
{
"kind": "apiEndpoint",
"category": "expectedPublicEndpoint",
"path": "/api/health",
"normalizedPath": "/api/health",
"status": 200,
"contentType": "application/json",
"reason": "public_discovery_endpoint"
}
]
}
}
}/api/cve-2025-55182/checkRate limited: 5/min per IPPassively detects React Server Components and the React version, then checks that version against the OSV.dev advisory database. GET requests only. Returns status (not_vulnerable · uncertain · confirmed_vulnerable) and any matching advisories.
| Field | Meaning |
|---|---|
| status | not_vulnerable · uncertain · confirmed_vulnerable |
| advisorySource | osv · unavailable · not_queried |
| advisories[] | OSV advisory id, CVE aliases, severity, fixed versions, URL |
curl -X POST https://vibecheckscan.com/api/cve-2025-55182/check \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'Scoring
Grades and weights
The overall score is a weighted average across 5 modules. Each module scores 0–100 internally, then contributes to the weighted total.
headers26.25%Security Headers
CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy
tls22.5%TLS/SSL
Protocol version, cert expiry, trust chain, cipher strength
cors15%CORS Policy
Wildcard origins, credentials policy, localhost/null origin
apiSurface11.25%API Surface
Exposed .map files, API endpoints, sensitive files, client-bundle server-secret material
cve25% (confirmed only)React / Next CVEs
RSC detection + React version checked against OSV.dev; only a confirmed advisory match moves the grade
| Grade | Score range |
|---|---|
| A | 90 – 100 |
| B | 80 – 89 |
| C | 70 – 79 |
| D | 60 – 69 |
| F | below 60 |
Rate limits
Limits per IP
| Endpoint | Limit | Window |
|---|---|---|
| POST /api/scan/start | 5 requests | per minute |
| POST /api/cve-2025-55182/check | 5 requests | per minute |
| All other routes | 100 requests | per minute |
When rate limited: HTTP 429 with a Retry-After header (seconds to wait) and code: "rate_limit_exceeded" in the response body.
Errors
Error codes
All error responses include a human-readable error string, a machine-readable code, and a request_id for debugging.
{
"error": "URL is required", // human-readable, used by UI
"code": "missing_url", // machine-readable for agents
"request_id": "a1b2c3d4e5f6a1b2" // include this in bug reports
}| HTTP | code | Meaning | Fix |
|---|---|---|---|
| 400 | missing_url | No URL in request body | Add "url" field |
| 400 | invalid_url | URL failed validation | Must be http/https, public host, valid TLD |
| 400 | missing_scan_id | No id query param | Add ?id=<scanId> |
| 400 | invalid_scan_id | Malformed scanId | Use exact scanId from start response |
| 404 | scan_not_found | Scan not found or expired | Results expire after 24h; start a new scan |
| 413 | request_too_large | Body exceeds 1MB | Reduce request size |
| 429 | rate_limit_exceeded | Too many requests | Wait Retry-After seconds |
| 503 | service_unavailable | Temporary shutdown | Retry after a few seconds |
AI agents & LLMs
Machine-readable contracts
These endpoints give AI agents everything they need to discover and use the API without guessing.
Capability registry — full JSON schemas for every capability, implementation status, and polling guidance.
OpenAPI 3.1 spec — authoritative REST contract with request/response schemas for all endpoints.
Agent card — workflow description, rate limits, module weights, and links to all contracts.
Skill file (Markdown) — when to use, terminology, poll vs wait table, error recovery. For agent runtimes that support skill installation.
Plain-text overview — lightweight orientation for LLMs: what the site does, workflow summary, and links to machine contracts.
API catalog — RFC 9727 linkset describing the public API, OpenAPI contract, developer docs, and health endpoint.
Interpretation guide (Markdown) — module weights, finding categories, and per-finding remediation. Fetch once per conversation, not per call.
Agent skills index — machine-readable skill discovery document with installable skill artifacts.
Authentication status
The scan API is public today. We intentionally do not publish OAuth/OIDC discovery documents such as/.well-known/openid-configuration,/.well-known/oauth-authorization-server, or/.well-known/oauth-protected-resourcebecause there is no real issuer, token endpoint, or protected resource contract behind them yet.
Passive by design
All scanning uses GET, HEAD, and OPTIONS requests only. No fuzzing, no POST requests, no credential testing, no endpoint enumeration. Private and internal IP ranges are blocked server-side. You see exactly what a standard browser visitor sees.