REST API Reference
Trigger scans, poll for status, fetch reports, and query findings programmatically. Base URL: https://nixer.polsia.app/api
Authentication
All API requests require an API key passed as a Bearer token in the Authorization header:
Authorization: Bearer YOUR_NIXER_API_KEY
Get your API key from the Nixer dashboard. The free tier allows 3 scans/hour per IP. Authenticated requests have higher limits depending on your plan.
Endpoints
Trigger a new scan. Returns immediately with a scan ID — poll GET /api/scanner/scan/:id for status.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
input_type | string | Yes | "github_repo" or "agent_config" |
input_value | string | Yes | GitHub repo URL or agent config JSON string |
severity_threshold | string | No | "low", "medium", "high", "critical". Default: "high" |
detectors | array | No | List of detector names to enable. Defaults to all. |
Response
{
"id": "scan_a1b2c3d4e5",
"status": "pending",
"created_at": "2026-06-05T17:00:00Z"
}Poll scan status. Returns "pending", "running", or "complete". When complete, includes the full findings payload.
{
"id": "scan_a1b2c3d4e5",
"status": "complete",
"score": 62,
"grade": "C",
"report_url": "https://nixer.polsia.app/reports/scan_a1b2c3d4e5",
"findings": [
{
"id": "f-1",
"rule_id": "nixer/cicd/unpinned-action",
"severity": "high",
"title": "Unpinned Third-Party GitHub Action",
"file_path": ".github/workflows/ci.yml",
"line_number": 18,
"description": "Action tj-actions/changed-files@v35 is pinned to a mutable tag.",
"remediation": "Pin to a full commit SHA: tj-actions/changed-files@abc123..."
}
]
}Returns the SARIF 2.1.0 document for a completed scan. Content-Type: application/json.
Code examples
curl — trigger and poll
# Trigger scan SCAN_ID=$(curl -s -X POST https://nixer.polsia.app/api/scanner/scan \ -H "Authorization: Bearer $NIXER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input_type":"github_repo","input_value":"https://github.com/your-org/your-repo"}' \ | jq -r .id) echo "Scan ID: $SCAN_ID" # Poll until complete while true; do STATUS=$(curl -s "https://nixer.polsia.app/api/scanner/scan/$SCAN_ID" \ -H "Authorization: Bearer $NIXER_API_KEY" | jq -r .status) echo "Status: $STATUS" [ "$STATUS" = "complete" ] && break sleep 5 done # Fetch SARIF curl -s "https://nixer.polsia.app/api/scanner/scan/$SCAN_ID/sarif" \ -H "Authorization: Bearer $NIXER_API_KEY" > nixer.sarif
Node.js
const BASE = 'https://nixer.polsia.app/api'; const KEY = process.env.NIXER_API_KEY; async function scan(repoUrl) { // Trigger const res = await fetch(`${BASE}/scanner/scan`, { method: 'POST', headers: { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ input_type: 'github_repo', input_value: repoUrl }), }); const { id } = await res.json(); // Poll while (true) { await new Promise(r => setTimeout(r, 3000)); const poll = await fetch(`${BASE}/scanner/scan/${id}`, { headers: { 'Authorization': `Bearer ${KEY}` }, }); const data = await poll.json(); if (data.status === 'complete') { console.log(`Score: ${data.score} (${data.grade})`); console.log(`Findings: ${data.findings.length}`); return data; } } } scan('https://github.com/your-org/your-repo').catch(console.error);
Python
import os, time, requests BASE = "https://nixer.polsia.app/api" KEY = os.environ["NIXER_API_KEY"] HEADERS = {"Authorization": f"Bearer {KEY}"} def scan(repo_url): # Trigger r = requests.post(f"{BASE}/scanner/scan", headers=HEADERS, json={ "input_type": "github_repo", "input_value": repo_url, }) scan_id = r.json()["id"] print(f"Scan ID: {scan_id}") # Poll while True: time.sleep(3) data = requests.get(f"{BASE}/scanner/scan/{scan_id}", headers=HEADERS).json() if data["status"] == "complete": print(f"Score: {data['score']} ({data['grade']})") print(f"Findings: {len(data['findings'])}") return data scan("https://github.com/your-org/your-repo")
Rate limits
| Plan | Scans per hour | Concurrent scans |
|---|---|---|
| Free (unauthenticated) | 3 / IP | 1 |
| Free (API key) | 10 | 2 |
| Pro ($49/mo) | 100 | 5 |
| Enterprise | Unlimited | Unlimited |
Rate limit headers are returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. On limit exceeded: HTTP 429 with Retry-After.