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",
  "vulnerabilities": [
    {
      "rule_id": "SEC001",
      "level": "high",
      "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": "medium",
      "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
{
  "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",
  "markdown": "You are a senior Application-Security, Web-Application, and Cloud-Reliability engineer.\n# Security Issues and Vulnerabilities \n\n**Total Issues**: 2\n\n## Vulnerabilities\n\n### High Severity\n\n**SEC001: Hardcoded API key detected**\n- **File**: src/auth.js:42\n- **Description**: API keys should be stored in environment variables\n- **Suggestion**: Move the API key to an environment variable\n\n### Medium Severity\n\n**SEC002: SQL injection vulnerability**\n- **File**: src/database.js:78\n- **Description**: User input is directly concatenated into SQL query\n- **Suggestion**: Use parameterized queries or prepared statements\n"
}

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",
  "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
vulnerabilitiesarrayArray of vulnerability objects

Vulnerability Object Fields

FieldTypeDescription
rule_idstringUnique identifier for the security rule
levelstringSeverity level: critical, high, medium, low
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):
    """Wait for scan completion with polling"""
    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")

# Usage
try:
    results = wait_for_scan_completion('your-scan-id', 'RFabc-your-api-key-here')
    print(f"Found {len(results['vulnerabilities'])} vulnerabilities")
except Exception as e:
    print(f"Error: {e}")

Polling Strategy

For long-running scans, implement a polling strategy:
#!/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 -s -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