Gödel's Sieve
API Docs
API Reference

Code Examples

Complete examples in Python and Node.js for scanning files and retrieving results.

Complete Scanning Workflow

These examples demonstrate the full workflow of uploading a file, polling for scan completion, and retrieving the results.

Python Example

Prerequisites

pip install requests

Complete Script

import requests
import time
import sys
from pathlib import Path

class GodelsSieveClient:
    def __init__(self, api_key: str, base_url: str):
        """Initialize the Gödel's Sieve API client."""
        self.api_key = api_key
        self.base_url = base_url.rstrip('/')
        self.headers = {'X-API-Key': api_key}
    
    def upload_file(self, file_path: str) -> dict:
        """Submit a file for scanning."""
        file_path = Path(file_path)
        
        if not file_path.exists():
            raise FileNotFoundError(f"File not found: {file_path}")
        
        if file_path.stat().st_size > 32 * 1024 * 1024:
            raise ValueError("File size exceeds 32MB limit")
        
        with open(file_path, 'rb') as f:
            files = {'file': (file_path.name, f)}
            response = requests.post(
                f'{self.base_url}/api/upload',
                headers=self.headers,
                files=files
            )
        
        response.raise_for_status()
        return response.json()
    
    def get_scan_status(self, scan_id: str) -> dict:
        """Get the status and results of a scan."""
        response = requests.get(
            f'{self.base_url}/api/scans/{scan_id}',
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()
    
    def wait_for_scan(self, scan_id: str, timeout: int = 300, poll_interval: int = 2) -> dict:
        """
        Wait for a scan to complete.
        
        Args:
            scan_id: The scan ID to monitor
            timeout: Maximum time to wait in seconds (default: 300)
            poll_interval: Time between status checks in seconds (default: 2)
        
        Returns:
            Final scan result
        
        Raises:
            TimeoutError: If scan doesn't complete within timeout
            RuntimeError: If scan fails
        """
        start_time = time.time()
        
        while True:
            elapsed = time.time() - start_time
            
            if elapsed > timeout:
                raise TimeoutError(f"Scan {scan_id} did not complete within {timeout} seconds")
            
            result = self.get_scan_status(scan_id)
            status = result.get('status')
            
            print(f"Status: {status} (elapsed: {elapsed:.1f}s)")
            
            if status == 'completed':
                return result
            elif status in ('failed', 'error'):
                error = result.get('error', 'Unknown error')
                raise RuntimeError(f"Scan failed: {error}")
            elif status == 'unsupported':
                raise RuntimeError('File type is not supported for analysis')
            elif status in ('queued', 'pending', 'extracting', 'processing'):
                time.sleep(poll_interval)
            else:
                raise RuntimeError(f"Unknown status: {status}")
    
    def scan_file(self, file_path: str, wait: bool = True) -> dict:
        """
        Submit a file for scanning, optionally waiting for results.
        
        Args:
            file_path: Path to the file to scan
            wait: Whether to wait for scan completion (default: True)
        
        Returns:
            Scan result (final if wait=True, initial if wait=False)
        """
        print(f"Submitting file: {file_path}")
        upload_result = self.upload_file(file_path)
        scan_id = upload_result['scanId']
        print(f"Scan submitted. Scan ID: {scan_id}")
        
        if wait:
            print("Waiting for scan to complete...")
            return self.wait_for_scan(scan_id)
        else:
            return upload_result


def main():
    # Configuration
    API_KEY = 'gds_your_api_key_here'
    API_HOST = 'https://api.sieve.godel-labs.ai'
    FILE_PATH = './suspicious.pdf'
    
    # Create client
    client = GodelsSieveClient(API_KEY, API_HOST)
    
    try:
        # Scan file and wait for results
        result = client.scan_file(FILE_PATH, wait=True)
        
        # Display results
        print("\n" + "="*50)
        print("SCAN RESULTS")
        print("="*50)
        print(f"File Name:      {result['fileName']}")
        print(f"File Hash:      {result['fileHash']}")
        print(f"Status:         {result['status']}")
        print(f"Threat Score:   {result['threatScore']}")
        print(f"Is Threat:      {result['isThreat']}")
        print(f"Severity:       {result['severity']}")
        print(f"Risk Category:  {result['riskCategory']}")
        print(f"Processing:     {result['processingTimeMs']}ms")
        
        if result.get('results'):
            print(f"\nDetailed Results:")
            for key, value in result['results'].items():
                print(f"  {key}: {value}")
        
        # Exit with appropriate code
        sys.exit(1 if result['isThreat'] else 0)
        
    except FileNotFoundError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except requests.HTTPError as e:
        print(f"API Error: {e.response.status_code} - {e.response.text}", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == '__main__':
    main()

Usage

# Set your API key
export GODELS_SIEVE_API_KEY="gds_your_api_key_here"

# Run the scanner
python scan_file.py

Example Output

Submitting file: ./suspicious.pdf
Scan submitted. Scan ID: 2b2f7b8b-7f2c-4b2d-9a5f-5c0c4c3c9d4e
Waiting for scan to complete...
Status: queued (elapsed: 0.0s)
Status: processing (elapsed: 2.1s)
Status: completed (elapsed: 4.3s)

==================================================
SCAN RESULTS
==================================================
File Name:      suspicious.pdf
File Hash:      a1b2c3d4e5f6...
Status:         completed
Threat Score:   0.12
Is Threat:      False
Severity:       low
Risk Category:  safe
Processing:     1532ms

Detailed Results:
  threat_score: 0.12
  is_threat: False
  severity: low

Node.js Example

Prerequisites

npm install axios form-data

Complete Script

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');

class GodelsSieveClient {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl.replace(/\/$/, '');
    this.headers = { 'X-API-Key': apiKey };
  }

  /**
   * Submit a file for scanning
   * @param {string} filePath - Path to the file to scan
   * @returns {Promise<Object>} Upload response with scanId
   */
  async uploadFile(filePath) {
    const stats = fs.statSync(filePath);
    
    if (stats.size > 32 * 1024 * 1024) {
      throw new Error('File size exceeds 32MB limit');
    }

    const form = new FormData();
    form.append('file', fs.createReadStream(filePath), path.basename(filePath));

    const response = await axios.post(
      `${this.baseUrl}/api/upload`,
      form,
      {
        headers: {
          ...this.headers,
          ...form.getHeaders(),
        },
      }
    );

    return response.data;
  }

  /**
   * Get the status and results of a scan
   * @param {string} scanId - The scan ID
   * @returns {Promise<Object>} Scan status and results
   */
  async getScanStatus(scanId) {
    const response = await axios.get(
      `${this.baseUrl}/api/scans/${scanId}`,
      { headers: this.headers }
    );
    return response.data;
  }

  /**
   * Wait for a scan to complete
   * @param {string} scanId - The scan ID to monitor
   * @param {number} timeout - Maximum time to wait in seconds (default: 300)
   * @param {number} pollInterval - Time between checks in seconds (default: 2)
   * @returns {Promise<Object>} Final scan result
   */
  async waitForScan(scanId, timeout = 300, pollInterval = 2) {
    const startTime = Date.now();

    while (true) {
      const elapsed = (Date.now() - startTime) / 1000;

      if (elapsed > timeout) {
        throw new Error(`Scan ${scanId} did not complete within ${timeout} seconds`);
      }

      const result = await this.getScanStatus(scanId);
      const status = result.status;

      console.log(`Status: ${status} (elapsed: ${elapsed.toFixed(1)}s)`);

      if (status === 'completed') {
        return result;
      } else if (status === 'failed' || status === 'error') {
        const error = result.error || 'Unknown error';
        throw new Error(`Scan failed: ${error}`);
      } else if (status === 'unsupported') {
        throw new Error('File type is not supported for analysis');
      } else if (['queued', 'pending', 'extracting', 'processing'].includes(status)) {
        await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
      } else {
        throw new Error(`Unknown status: ${status}`);
      }
    }
  }

  /**
   * Submit a file for scanning, optionally waiting for results
   * @param {string} filePath - Path to the file to scan
   * @param {boolean} wait - Whether to wait for scan completion (default: true)
   * @returns {Promise<Object>} Scan result
   */
  async scanFile(filePath, wait = true) {
    console.log(`Submitting file: ${filePath}`);
    const uploadResult = await this.uploadFile(filePath);
    const scanId = uploadResult.scanId;
    console.log(`Scan submitted. Scan ID: ${scanId}`);

    if (wait) {
      console.log('Waiting for scan to complete...');
      return await this.waitForScan(scanId);
    } else {
      return uploadResult;
    }
  }
}

/**
 * Main function
 */
async function main() {
  // Configuration
  const API_KEY = process.env.GODELS_SIEVE_API_KEY || 'gds_your_api_key_here';
  const API_HOST = process.env.GODELS_SIEVE_HOST || 'https://api.sieve.godel-labs.ai';
  const FILE_PATH = process.argv[2] || './suspicious.pdf';

  // Create client
  const client = new GodelsSieveClient(API_KEY, API_HOST);

  try {
    // Scan file and wait for results
    const result = await client.scanFile(FILE_PATH, true);

    // Display results
    console.log('\n' + '='.repeat(50));
    console.log('SCAN RESULTS');
    console.log('='.repeat(50));
    console.log(`File Name:      ${result.fileName}`);
    console.log(`File Hash:      ${result.fileHash}`);
    console.log(`Status:         ${result.status}`);
    console.log(`Threat Score:   ${result.threatScore}`);
    console.log(`Is Threat:      ${result.isThreat}`);
    console.log(`Severity:       ${result.severity}`);
    console.log(`Risk Category:  ${result.riskCategory}`);
    console.log(`Processing:     ${result.processingTimeMs}ms`);

    if (result.results) {
      console.log('\nDetailed Results:');
      for (const [key, value] of Object.entries(result.results)) {
        console.log(`  ${key}: ${value}`);
      }
    }

    // Exit with appropriate code
    process.exit(result.isThreat ? 1 : 0);

  } catch (error) {
    if (error.response) {
      console.error(`API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
    } else {
      console.error(`Error: ${error.message}`);
    }
    process.exit(1);
  }
}

// Run if executed directly
if (require.main === module) {
  main();
}

module.exports = { GodelsSieveClient };

Usage

# Set your API key
export GODELS_SIEVE_API_KEY="gds_your_api_key_here"

# Run the scanner
node scan_file.js ./suspicious.pdf

TypeScript Version

For TypeScript projects, add type definitions:

type ScanStatus =
  | 'queued'
  | 'pending'
  | 'extracting'
  | 'processing'
  | 'completed'
  | 'failed'
  | 'error'
  | 'unsupported';

interface ScanResult {
  scanId: string;
  fileName: string;
  fileHash: string;
  fileSize: number;
  mimeType: string;
  status: ScanStatus;
  results: Record<string, unknown> | null;
  threatScore: number | null;
  isThreat: boolean | null;
  severity: string | null;
  riskCategory: string | null;
  analysisMode: string | null;
  processingTimeMs: number | null;
  confidence: number | null;
  securityIssuesCount: number | null;
  sensitiveDataCount: number | null;
  detectionClass: string[];
  owaspLlmRisks: string[];
  owaspAgenticThreats: string[];
  compliance: Record<string, unknown> | null;
  error: string | null;
  createdAt: string;
  updatedAt: string;
}

interface UploadResult {
  scanId: string;
  fileName: string;
  fileSize: number;
  fileHash: string;
  status: ScanStatus;
  cached?: boolean;
  isArchive?: boolean;
  message: string;
}

Advanced Usage

Batch Scanning (Python)

def scan_multiple_files(client: GodelsSieveClient, file_paths: list[str]) -> list[dict]:
    """Scan multiple files concurrently."""
    import concurrent.futures
    
    results = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        future_to_file = {
            executor.submit(client.scan_file, fp, wait=True): fp 
            for fp in file_paths
        }
        
        for future in concurrent.futures.as_completed(future_to_file):
            file_path = future_to_file[future]
            try:
                result = future.result()
                results.append({'file': file_path, 'result': result})
            except Exception as e:
                results.append({'file': file_path, 'error': str(e)})
    
    return results

Batch Scanning (Node.js)

/**
 * Scan multiple files concurrently
 * @param {GodelsSieveClient} client - The API client
 * @param {string[]} filePaths - Array of file paths to scan
 * @param {number} concurrency - Maximum concurrent scans (default: 5)
 * @returns {Promise<Array>} Array of scan results
 */
async function scanMultipleFiles(client, filePaths, concurrency = 5) {
  const results = [];
  
  // Process files in batches
  for (let i = 0; i < filePaths.length; i += concurrency) {
    const batch = filePaths.slice(i, i + concurrency);
    
    const batchResults = await Promise.allSettled(
      batch.map(fp => 
        client.scanFile(fp, true)
          .then(result => ({ file: fp, result }))
          .catch(error => ({ file: fp, error: error.message }))
      )
    );
    
    results.push(...batchResults.map(r => r.value || r.reason));
  }
  
  return results;
}

Error Handling Best Practices

# Python: Exponential backoff for rate limits
from time import sleep
from requests.exceptions import HTTPError

def upload_with_retry(client, file_path, max_retries=3):
    """Upload with exponential backoff on rate limits."""
    for attempt in range(max_retries):
        try:
            return client.upload_file(file_path)
        except HTTPError as e:
            if e.response.status_code == 429:  # Rate limit
                wait_time = 2 ** attempt  # Exponential backoff
                print(f"Rate limited. Waiting {wait_time}s...")
                sleep(wait_time)
            else:
                raise
    raise Exception(f"Failed after {max_retries} retries")
// Node.js: Retry logic with exponential backoff
async function uploadWithRetry(client, filePath, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.uploadFile(filePath);
    } catch (error) {
      if (error.response?.status === 429) {  // Rate limit
        const waitTime = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Waiting ${waitTime/1000}s...`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
      } else {
        throw error;
      }
    }
  }
  throw new Error(`Failed after ${maxRetries} retries`);
}

Integration Patterns

CI/CD Pipeline Integration

#!/bin/bash
# scan-artifacts.sh - Scan build artifacts in CI/CD

API_KEY="${GODELS_SIEVE_API_KEY}"
API_HOST="https://api.sieve.godel-labs.ai"

for file in dist/*.zip; do
  echo "Scanning $file..."
  
  python scan_file.py "$file"
  
  if [ $? -ne 0 ]; then
    echo "❌ Threat detected in $file - failing build"
    exit 1
  fi
  
  echo "✅ $file is safe"
done

echo "All artifacts scanned successfully"

Webhook Notifications (Optional)

If your workflow requires immediate notifications instead of polling, consider implementing a webhook endpoint to receive scan completion events.


Performance Tips

  1. Use compression for large files:

    import gzip
    
    with open('file.zip', 'rb') as f_in:
        with gzip.open('file.zip.gz', 'wb') as f_out:
            f_out.writelines(f_in)
    
    # Upload with compression flag
    files = {'file': open('file.zip.gz', 'rb')}
    data = {'isCompressed': 'true', 'originalSize': original_size}
  2. Adjust poll intervals based on file size:

    • Small files (< 1MB): 1-2 seconds
    • Medium files (1-10MB): 3-5 seconds
    • Large files (> 10MB): 5-10 seconds
  3. Use async/await for concurrent operations in both Python (asyncio) and Node.js

  4. Cache results for previously scanned file hashes to avoid duplicate scans