Documentation
Guides
Code Examples

Code Examples

Ready-to-run examples for the complete NAT scan lifecycle in Python, JavaScript/TypeScript, and Bash/curl. All examples use the SaaS API at https://api.nat-testing.io.

Replace nat_pk_your_api_key_here with your actual API key from the dashboard (opens in a new tab). See the API Quickstart if you do not have one yet.


Full scan lifecycle

import time
import requests
 
NAT_API_KEY = "nat_pk_your_api_key_here"
BASE_URL = "https://api.nat-testing.io/api/v1"
HEADERS = {"X-API-Key": NAT_API_KEY, "Content-Type": "application/json"}
 
 
def start_scan(url: str, spec_url: str | None = None) -> str:
    """Start a scan and return the scan ID."""
    payload = {"url": url}
    if spec_url:
        payload["spec_url"] = spec_url
    r = requests.post(f"{BASE_URL}/scan", headers=HEADERS, json=payload)
    r.raise_for_status()
    return r.json()["scan_id"]
 
 
def poll_scan_status(scan_id: str, interval: int = 5) -> dict:
    """Poll until the scan reaches a terminal state and return the status object."""
    while True:
        r = requests.get(f"{BASE_URL}/scan/{scan_id}", headers=HEADERS)
        r.raise_for_status()
        data = r.json()
        status = data.get("status")
        print(f"  [{scan_id}] status: {status}")
        if status in ("completed", "failed", "cancelled"):
            return data
        time.sleep(interval)
 
 
def get_results(scan_id: str) -> dict:
    """Fetch the full results for a completed scan."""
    r = requests.get(f"{BASE_URL}/scan/{scan_id}/results", headers=HEADERS)
    r.raise_for_status()
    return r.json()
 
 
def export_report(scan_id: str, fmt: str = "html", output_path: str | None = None) -> str:
    """Export a report and save it to a file. Returns the output file path."""
    r = requests.get(
        f"{BASE_URL}/scan/{scan_id}/export",
        headers=HEADERS,
        params={"format": fmt},
    )
    r.raise_for_status()
    path = output_path or f"report_{scan_id}.{fmt}"
    with open(path, "wb") as f:
        f.write(r.content)
    print(f"  Report saved to {path}")
    return path
 
 
def check_usage() -> dict:
    """Return current quota usage for the tenant."""
    r = requests.get(f"{BASE_URL}/usage", headers=HEADERS)
    r.raise_for_status()
    return r.json()
 
 
if __name__ == "__main__":
    # 1. Start scan
    scan_id = start_scan(
        url="https://api.example.com",
        spec_url="https://api.example.com/openapi.json",
    )
    print(f"Scan started: {scan_id}")
 
    # 2. Poll status
    status = poll_scan_status(scan_id)
    print(f"Scan finished with status: {status['status']}")
 
    # 3. Fetch results
    results = get_results(scan_id)
    print(f"Passed: {results['passed']}  Failed: {results['failed']}")
 
    # 4. Export HTML report
    export_report(scan_id, fmt="html")
 
    # 5. Check usage
    usage = check_usage()
    print(f"Usage: {usage['scans_used']} / {usage['scans_limit']} scans")

Individual endpoint examples

Start a scan

curl -X POST https://api.nat-testing.io/api/v1/scan \
  -H "X-API-Key: $NAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com",
    "spec_url": "https://api.example.com/openapi.json",
    "options": { "concurrency": 5, "timeout": 30 }
  }'

Check scan status

curl https://api.nat-testing.io/api/v1/scan/$SCAN_ID \
  -H "X-API-Key: $NAT_API_KEY"

Get scan results

curl https://api.nat-testing.io/api/v1/scan/$SCAN_ID/results \
  -H "X-API-Key: $NAT_API_KEY"

Export results

# HTML report
curl "https://api.nat-testing.io/api/v1/scan/$SCAN_ID/export?format=html" \
  -H "X-API-Key: $NAT_API_KEY" -o report.html
 
# JSON export
curl "https://api.nat-testing.io/api/v1/scan/$SCAN_ID/export?format=json" \
  -H "X-API-Key: $NAT_API_KEY" -o results.json
 
# CSV export
curl "https://api.nat-testing.io/api/v1/scan/$SCAN_ID/export?format=csv" \
  -H "X-API-Key: $NAT_API_KEY" -o results.csv

Check usage

curl https://api.nat-testing.io/api/v1/usage \
  -H "X-API-Key: $NAT_API_KEY"

Polling helper pattern

A reusable helper that polls scan status with exponential back-off:

import time
 
def wait_for_scan(scan_id: str, max_wait: int = 600) -> dict:
    """
    Poll GET /api/v1/scan/{id} until the scan reaches a terminal state.
    Uses exponential back-off (5 → 10 → 20 → 40 → 60 seconds, capped at 60).
    Raises TimeoutError if the scan does not complete within max_wait seconds.
    """
    interval = 5
    elapsed = 0
    while elapsed < max_wait:
        r = requests.get(f"{BASE_URL}/scan/{scan_id}", headers=HEADERS)
        r.raise_for_status()
        data = r.json()
        if data["status"] in ("completed", "failed", "cancelled"):
            return data
        time.sleep(interval)
        elapsed += interval
        interval = min(interval * 2, 60)
    raise TimeoutError(f"Scan {scan_id} did not complete within {max_wait}s")

Next steps

Was this helpful?