Skip to main content

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": "pending"
}

Scan Completed (JSON Format)

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"
    }
  ]
}

Scan Completed (Markdown Format - format=md)

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

FieldTypeDescription
statusstringScan status: pending, queued, processing, completed, failed

Completed Scan Fields (JSON)

FieldTypeDescription
repository_namestringRepository name in format “org/repo”
branch_namestringBranch name that was scanned
scan_datestringISO 8601 timestamp when scan was created
scan_modestringScan mode used: "fast" or "plus"
vulnerabilitiesarrayArray of vulnerability objects

Vulnerability Object Fields

FieldTypeDescription
rule_idstringUnique identifier for the security rule
levelstringSeverity level: error, warning, note
filestringFile path where vulnerability was found
lineintegerLine number in the file
columnintegerColumn number in the line
messagestringShort description of the issue
descriptionstringDetailed explanation of the vulnerability
suggestionstringRecommended 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