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 requestsComplete 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.pyExample 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: lowNode.js Example
Prerequisites
npm install axios form-dataComplete 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.pdfTypeScript 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 resultsBatch 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
-
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} -
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
-
Use async/await for concurrent operations in both Python (asyncio) and Node.js
-
Cache results for previously scanned file hashes to avoid duplicate scans