GET /api/static/scan
Check the status of a scan and retrieve results once completed.
Request
Headers:
x-api-key (required): Your Rafter API key
Query Parameters:
scan_id (required): The scan request ID to check
format (optional): Output format - json (default) or md
Example Requests
Check status (JSON format):
curl -H "x-api-key: RFabc-your-api-key-here" \
"https://rafter.so/api/static/scan?scan_id=b1b2c3d4-e5f6-7890-abcd-ef1234567890"
Get results in Markdown format (format=md):
curl -H "x-api-key: RFabc-your-api-key-here" \
"https://rafter.so/api/static/scan?scan_id=b1b2c3d4-e5f6-7890-abcd-ef1234567890&format=md"
Response
Scan Pending/Processing
Status: pending, queued, or processing
Status: completed
{
"status": "completed",
"repository_name": "myorg/myrepo",
"branch_name": "prod",
"scan_date": "2025-07-24T00:00:00.000+00:00",
"scan_id": "abcdabcd-abcd-abcd-abcd-abcdabcd",
"scan_mode": "fast",
"vulnerabilities": [
{
"rule_id": "SEC001",
"level": "error",
"file": "src/auth.js",
"line": 42,
"column": 15,
"message": "Hardcoded API key detected",
"description": "API keys should be stored in environment variables",
"suggestion": "Move the API key to an environment variable"
},
{
"rule_id": "SEC002",
"level": "warning",
"file": "src/database.js",
"line": 78,
"column": 8,
"message": "SQL injection vulnerability",
"description": "User input is directly concatenated into SQL query",
"suggestion": "Use parameterized queries or prepared statements"
}
]
}
Status: completed with format=md
When format=md is specified, the response includes a markdown field containing a structured security report designed for LLM-assisted remediation. The report uses role-priming, step-by-step instructions, and per-issue detail to produce high-quality analysis.
{
"status": "completed",
"repository_name": "myorg/myrepo",
"branch_name": "prod",
"scan_date": "2025-07-24T00:00:00.000+00:00",
"scan_id": "abcdabcd-abcd-abcd-abcd-abcdabcd",
"scan_mode": "fast",
"markdown": "You are a senior application-security, web-application, and cloud-reliability engineer. Implement production-grade solutions that scale. Never mock data, suppress linter security rules, or shortcut the fix. Think step-by-step.\n\n# Security Issues and Vulnerabilities\n\n**Total Issues:** 2\n\nThis report contains 2 security issues found in the repository. Each issue requires attention and remediation. Proceed one-by-one, thinking step-by-step to understand and remediate each.\n\n## Issues Summary\n\n### Issue 1\n**Rule ID:** a1b2c3d4\n**File:** src/auth.js\n**Line:** 42\n**Description:** Hardcoded API key detected\n\n### Issue 2\n**Rule ID:** e5f6a7b8\n**File:** src/database.js\n**Line:** 78\n**Description:** User input is directly concatenated into SQL query\n\nPlease analyze these 2 security vulnerabilities and provide:\n1. A comprehensive analysis of the security risks\n2. Prioritized remediation steps\n3. Code examples for fixes\n4. Prevention strategies for future development"
}
Rule IDs in the markdown report are hashed for consistency. The report format is optimized for feeding into LLMs—it includes a security-engineer role prompt, structured issue metadata, and a trailing analysis request.
Scan Failed
Status: failed
{
"status": "failed",
"error": "Repository access denied or not found"
}
No Vulnerabilities Found
Status: completed with no issues
{
"status": "completed",
"repository_name": "myorg/myrepo",
"branch_name": "prod",
"scan_date": "2025-07-24T00:00:00.000+00:00",
"scan_id": "abcdabcd-abcd-abcd-abcd-abcdabcd",
"scan_mode": "fast",
"vulnerabilities": []
}
Error Responses
Error (400 Bad Request):
{
"error": "Missing required parameter: scan_id"
}
Error (401 Unauthorized):
{
"error": "Invalid or inactive API key."
}
Error (404 Not Found):
{
"error": "Scan not found."
}
Error (500 Internal Server Error):
{
"error": "An unexpected error occurred."
}
Response Fields
Common Fields
| Field | Type | Description |
|---|
status | string | Scan status: pending, queued, processing, completed, failed |
Completed Scan Fields (JSON)
| Field | Type | Description |
|---|
repository_name | string | Repository name in format “org/repo” |
branch_name | string | Branch name that was scanned |
scan_date | string | ISO 8601 timestamp when scan was created |
scan_mode | string | Scan mode used: "fast" or "plus" |
vulnerabilities | array | Array of vulnerability objects |
Vulnerability Object Fields
| Field | Type | Description |
|---|
rule_id | string | Unique identifier for the security rule |
level | string | Severity level: error, warning, note |
file | string | File path where vulnerability was found |
line | integer | Line number in the file |
column | integer | Column number in the line |
message | string | Short description of the issue |
description | string | Detailed explanation of the vulnerability |
suggestion | string | Recommended fix or mitigation |
Rate Limiting
The API implements rate limiting to ensure fair usage:
- Rate Limit: 100 requests per minute per IP address
- Quota: Based on your subscription plan
Examples
JavaScript
async function getScanResults(scanId, format = 'json') {
const response = await fetch(
`https://rafter.so/api/static/scan?scan_id=${scanId}&format=${format}`,
{
headers: {
'x-api-key': 'RFabc-your-api-key-here'
}
}
);
const data = await response.json();
if (data.status === 'completed') {
if (format === 'json') {
console.log(`Found ${data.vulnerabilities.length} vulnerabilities`);
} else {
console.log('Markdown report generated');
}
} else {
console.log(`Scan status: ${data.status}`);
}
return data;
}
Python
import requests
import time
def wait_for_scan_completion(scan_id, api_key, max_wait=300):
start_time = time.time()
while time.time() - start_time < max_wait:
response = requests.get(
f'https://rafter.so/api/static/scan?scan_id={scan_id}',
headers={'x-api-key': api_key}
)
data = response.json()
if data['status'] == 'completed':
return data
elif data['status'] == 'failed':
raise Exception(f"Scan failed: {data.get('error', 'Unknown error')}")
print(f"Scan status: {data['status']}")
time.sleep(10)
raise Exception("Scan timed out")
# Enhanced polling strategy with failure handling in Bash
#!/bin/bash
SCAN_ID="b1b2c3d4-e5f6-7890-abcd-ef1234567890"
API_KEY="RFabc-your-api-key-here"
MAX_ATTEMPTS=15
for i in $(seq 1 $MAX_ATTEMPTS); do
RESPONSE=$(curl -fsS -H "x-api-key: $API_KEY" \
"https://rafter.so/api/static/scan?scan_id=$SCAN_ID")
STATUS=$(echo $RESPONSE | jq -r '.status')
if [ "$STATUS" = "completed" ]; then
echo "Scan completed!"
echo $RESPONSE | jq '.vulnerabilities | length' | xargs echo "Found vulnerabilities:"
break
elif [ "$STATUS" = "failed" ]; then
echo "Scan failed!"
echo $RESPONSE | jq -r '.error'
exit 1
else
echo "Attempt $i/$MAX_ATTEMPTS: Status is $STATUS"
sleep 10
fi
done