Skip to main content
These examples use Python and the requests library to cover the most common workflows: managing targets, running scans, retrieving findings, and generating reports. Each section builds on the setup below, so start there before copying individual snippets.

Setup

import json
import time
import requests
from enum import IntEnum

API_KEY = "YOUR_API_KEY"  # Get from My account > API
API_URL = "https://app.pentest-tools.com/api/v2"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}


class Tool(IntEnum):
    """Available scanning tools and their IDs"""
    SUBDOMAIN_FINDER = 20
    WHOIS_LOOKUP = 30
    PORT_SCANNER = 70
    URL_FUZZER = 90
    VHOSTS_FINDER = 160
    WEBSITE_SCANNER = 170
    ICMP_PING = 240
    SHAREPOINT_SCANNER = 260
    WORDPRESS_SCANNER = 270
    DRUPAL_SCANNER = 280
    JOOMLA_SCANNER = 290
    WEBSITE_RECON = 310
    SUBDOMAIN_TAKEOVER = 320
    NETWORK_SCANNER = 350
    SQLI_EXPLOITER = 380
    DOMAIN_FINDER = 390
    PASSWORD_AUDITOR = 400
    SSL_SCANNER = 450
    SNIPER = 490
    WAF_DETECTOR = 500
    API_SCANNER = 510
    CLOUD_SCANNER = 520
    PEOPLE_HUNTER = 530
    KUBERNETES_SCANNER = 540

Targets

List all targets

def get_targets(workspace_id: int = None) -> list:
    """Get all targets, optionally filtered by workspace"""
    params = {}
    if workspace_id:
        params["workspace_id"] = workspace_id

    response = requests.get(
        f"{API_URL}/targets",
        headers=HEADERS,
        params=params
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
targets = get_targets()
for target in targets:
    print(f"{target['id']}: {target['name']}")

Create a target

def create_target(name: str, description: str = "", workspace_id: int = None) -> int:
    """Create a new target and return its ID"""
    data = {"name": name}
    if description:
        data["description"] = description
    if workspace_id:
        data["workspace_id"] = workspace_id

    response = requests.post(
        f"{API_URL}/targets",
        headers=HEADERS,
        json=data
    )
    response.raise_for_status()
    return response.json()["data"]["created_id"]


# Usage
target_id = create_target(
    name="https://example.com",
    description="Main production site"
)
print(f"Created target: {target_id}")

Get target by ID

def get_target(target_id: int) -> dict:
    """Get a specific target by ID"""
    response = requests.get(
        f"{API_URL}/targets/{target_id}",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
target = get_target(12345)
print(f"Target: {target['name']}")

Delete a target

def delete_target(target_id: int) -> bool:
    """Delete a target"""
    response = requests.delete(
        f"{API_URL}/targets/{target_id}",
        headers=HEADERS
    )
    return response.status_code == 204


# Usage
if delete_target(12345):
    print("Target deleted")

Scans

Start a scan

def start_scan(
    tool_id: int,
    target_name: str = None,
    target_id: int = None,
    tool_params: dict = None,
    workspace_id: int = None,
    vpn_profile_uuid: str = None
) -> dict:
    """
    Start a scan using either target_name or target_id.
    Returns dict with target_id and created_id (scan_id).
    """
    if not (target_name or target_id):
        raise ValueError("Provide either target_name or target_id")

    data = {"tool_id": tool_id}

    if target_name:
        data["target_name"] = target_name
    else:
        data["target_id"] = target_id

    if tool_params:
        data["tool_params"] = tool_params
    if workspace_id:
        data["workspace_id"] = workspace_id
    if vpn_profile_uuid:
        data["vpn_profile_uuid"] = vpn_profile_uuid

    response = requests.post(
        f"{API_URL}/scans",
        headers=HEADERS,
        json=data
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage: Start a Website Scanner with light scan
result = start_scan(
    tool_id=Tool.WEBSITE_SCANNER,
    target_name="https://example.com",
    tool_params={"scan_type": "light"}
)
scan_id = result["created_id"]
print(f"Started scan {scan_id}")

Get scan status

def get_scan(scan_id: int) -> dict:
    """Get scan details and status"""
    response = requests.get(
        f"{API_URL}/scans/{scan_id}",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
scan = get_scan(12345)
print(f"Status: {scan['status_name']}, Progress: {scan['progress']}%")

List scans

def get_scans(
    workspace_id: int = None,
    target_id: int = None,
    tool_id: int = None,
    status: str = None,
    started_after: str = None,   # ISO date, e.g. "2025-01-01"
    started_before: str = None,  # ISO date, e.g. "2025-01-31"
    limit: int = 100,
    page: int = 1
) -> list:
    """
    List scans with optional filters.

    Valid status values: running, finished, stopped, timed out, waiting,
    aborted, failed to start, VPN connection error, auth failed, connection error
    """
    params = {"limit": limit, "page": page}
    if workspace_id:
        params["workspace_id"] = workspace_id
    if target_id:
        params["target_id"] = target_id
    if tool_id:
        params["tool_id"] = tool_id
    if status:
        params["status"] = status
    if started_after:
        params["start_time[start]"] = started_after
    if started_before:
        params["start_time[end]"] = started_before

    response = requests.get(
        f"{API_URL}/scans",
        headers=HEADERS,
        params=params
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage: Get all running scans
running_scans = get_scans(status="running")
for scan in running_scans:
    print(f"Scan {scan['id']}: {scan['tool_name']} - {scan['progress']}%")

# Usage: Get all Website Scanner scans started in January 2025
website_scans = get_scans(
    tool_id=Tool.WEBSITE_SCANNER,
    started_after="2025-01-01",
    started_before="2025-01-31"
)

Get scan output

def get_scan_output(scan_id: int) -> dict:
    """Get the JSON output of a completed scan"""
    response = requests.get(
        f"{API_URL}/scans/{scan_id}/output",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
output = get_scan_output(12345)
print(json.dumps(output, indent=2))

Stop a scan

def stop_scan(scan_id: int) -> bool:
    """Stop a running scan"""
    response = requests.post(
        f"{API_URL}/scans/{scan_id}/stop",
        headers=HEADERS
    )
    return response.status_code == 204


# Usage
if stop_scan(12345):
    print("Scan stopped")

Delete a scan

def delete_scan(scan_id: int) -> bool:
    """Delete a completed scan"""
    response = requests.delete(
        f"{API_URL}/scans/{scan_id}",
        headers=HEADERS
    )
    return response.status_code == 204

Workspaces

List workspaces

def get_workspaces() -> list:
    """Get all workspaces"""
    response = requests.get(
        f"{API_URL}/workspaces",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
workspaces = get_workspaces()
for ws in workspaces:
    print(f"{ws['id']}: {ws['name']} ({ws['scan_count']} scans, {ws['target_count']} targets)")

Create a workspace

def create_workspace(name: str, description: str = "") -> int:
    """Create a new workspace"""
    data = {"name": name}
    if description:
        data["description"] = description

    response = requests.post(
        f"{API_URL}/workspaces",
        headers=HEADERS,
        json=data
    )
    response.raise_for_status()
    return response.json()["data"]["created_id"]


# Usage
workspace_id = create_workspace("Q4 2024 Audit", "External penetration test")

Update a workspace

def update_workspace(workspace_id: int, name: str = None, description: str = None) -> bool:
    """Update workspace name and/or description"""
    data = {}
    if name:
        data["name"] = name
    if description is not None:
        data["description"] = description

    response = requests.put(
        f"{API_URL}/workspaces/{workspace_id}",
        headers=HEADERS,
        json=data
    )
    return response.status_code == 204

Delete a workspace

def delete_workspace(workspace_id: int) -> bool:
    """Delete a workspace (cannot delete current active workspace)"""
    response = requests.delete(
        f"{API_URL}/workspaces/{workspace_id}",
        headers=HEADERS
    )
    return response.status_code == 204

Findings

List findings

def get_findings(
    workspace_id: int = None,
    target_id: int = None,
    task_id: int = None,
    group_duplicates: bool = False,
    limit: int = 100,
    page: int = 1
) -> list:
    """Get findings with optional filters"""
    params = {"limit": limit, "page": page}
    if workspace_id:
        params["workspace_id"] = workspace_id
    if target_id:
        params["target_id"] = target_id
    if task_id:
        params["task_id"] = task_id
    if group_duplicates:
        params["group_duplicates"] = "true"

    response = requests.get(
        f"{API_URL}/findings",
        headers=HEADERS,
        params=params
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage: Get findings from a specific scan
findings = get_findings(task_id=12345)
for finding in findings:
    print(f"[{finding['cvss']}] {finding['name']}")

Get finding details

def get_finding(finding_id: int) -> dict:
    """Get detailed information about a finding"""
    response = requests.get(
        f"{API_URL}/findings/{finding_id}",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
finding = get_finding(12345)
print(f"Name: {finding['name']}")
print(f"Severity: {finding['risk_level']}")
print(f"Description: {finding['vuln_description']}")

Reports

Create a report

def create_report(
    source: str,
    resource_ids: list,
    format: str = "pdf",
    group_by: str = "target",
    webhook_url: str = None
) -> int:
    """
    Create a report from scans or findings.

    Args:
        source: "scans" or "findings"
        resource_ids: List of scan IDs or finding IDs
        format: "pdf", "docx", "html", "json", "csv", or "xlsx"
        group_by: "target" or "vulnerability"
        webhook_url: Optional URL to receive notification when ready

    Returns:
        Report ID
    """
    data = {
        "source": source,
        "resources": resource_ids,
        "format": format,
        "group_by": group_by
    }
    if webhook_url:
        data["webhook_url"] = webhook_url

    response = requests.post(
        f"{API_URL}/reports",
        headers=HEADERS,
        json=data
    )
    response.raise_for_status()
    return response.json()["data"]["report_id"]


# Usage: Generate PDF report from scans
report_id = create_report(
    source="scans",
    resource_ids=[123, 124, 125],
    format="pdf",
    group_by="vulnerability"
)
print(f"Report {report_id} is being generated")

Get report status

def get_report(report_id: int) -> dict:
    """Get report details and status"""
    response = requests.get(
        f"{API_URL}/reports/{report_id}",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
report = get_report(12345)
print(f"Status: {report['status']}")

Download a report

def download_report(report_id: int, output_path: str) -> bool:
    """Download a completed report to a file"""
    response = requests.get(
        f"{API_URL}/reports/{report_id}/download",
        headers=HEADERS
    )

    if response.status_code == 202:
        print("Report not ready yet")
        return False

    response.raise_for_status()

    with open(output_path, "wb") as f:
        f.write(response.content)
    return True


# Usage
if download_report(12345, "pentest_report.pdf"):
    print("Report downloaded successfully")

HTTP Loggers

Create an HTTP logger

def create_http_logger(label: str) -> int:
    """Create a new HTTP logger for out-of-band testing"""
    response = requests.post(
        f"{API_URL}/http_loggers",
        headers=HEADERS,
        json={"label": label}
    )
    response.raise_for_status()
    return response.json()["data"]["created_id"]


# Usage
logger_id = create_http_logger("SSRF Test - Production")

List HTTP loggers

def get_http_loggers() -> list:
    """Get all HTTP loggers"""
    response = requests.get(
        f"{API_URL}/http_loggers",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
loggers = get_http_loggers()
for logger in loggers:
    print(f"{logger['label']}: {logger['num_requests']} requests captured")

Get logger data

def get_http_logger_data(logger_id: int) -> list:
    """Get all captured requests for a logger"""
    response = requests.get(
        f"{API_URL}/http_loggers/{logger_id}/data",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]["requests"]


# Usage: Check for SSRF callbacks
data = get_http_logger_data(12345)
for request in data:
    print(f"{request['request_method']} from {request['ip_address']}")

Clear logger data

def clear_http_logger_data(logger_id: int) -> bool:
    """Clear all captured data for a logger"""
    response = requests.delete(
        f"{API_URL}/http_loggers/{logger_id}/data",
        headers=HEADERS
    )
    return response.status_code == 204

VPN Profiles

List VPN profiles

def get_vpn_profiles() -> list:
    """Get all VPN profiles for internal network scanning"""
    response = requests.get(
        f"{API_URL}/vpn_profiles",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()["data"]


# Usage
profiles = get_vpn_profiles()
for profile in profiles:
    print(f"{profile['name']}: {profile['id']}")

Complete workflows

Full scan workflow with polling

def run_scan_and_wait(
    tool_id: int,
    target: str,
    tool_params: dict = None,
    poll_interval: int = 10,
    timeout: int = 3600
) -> dict:
    """
    Start a scan, wait for completion, and return results.

    Args:
        tool_id: Tool to use
        target: Target URL or hostname
        tool_params: Tool-specific parameters
        poll_interval: Seconds between status checks
        timeout: Maximum wait time in seconds

    Returns:
        Scan output data
    """
    # Start the scan
    result = start_scan(
        tool_id=tool_id,
        target_name=target,
        tool_params=tool_params or {}
    )
    scan_id = result["created_id"]
    print(f"Started scan {scan_id}")

    # Poll for completion
    start_time = time.time()
    while True:
        if time.time() - start_time > timeout:
            raise TimeoutError(f"Scan {scan_id} timed out")

        scan = get_scan(scan_id)
        status = scan["status_name"]
        progress = scan.get("progress", 0)

        print(f"Status: {status}, Progress: {progress}%")

        if status == "finished":
            return get_scan_output(scan_id)
        elif status in ("stopped", "failed to start", "timed out", "aborted",
                        "VPN connection error", "auth failed", "connection error"):
            raise RuntimeError(f"Scan {status}")

        time.sleep(poll_interval)


# Usage
output = run_scan_and_wait(
    tool_id=Tool.WEBSITE_SCANNER,
    target="https://example.com",
    tool_params={"scan_type": "light"}
)

# Save results
with open("scan_results.json", "w") as f:
    json.dump(output, f, indent=2)

Batch scanning multiple targets

def batch_scan(
    targets: list,
    tool_id: int,
    tool_params: dict = None,
    workspace_id: int = None
) -> list:
    """
    Start scans on multiple targets and return scan IDs.
    Respects rate limits with automatic retry.
    """
    scan_ids = []

    for target in targets:
        while True:
            try:
                result = start_scan(
                    tool_id=tool_id,
                    target_name=target,
                    tool_params=tool_params,
                    workspace_id=workspace_id
                )
                scan_ids.append(result["created_id"])
                print(f"Started scan for {target}: {result['created_id']}")
                break
            except requests.HTTPError as e:
                if e.response.status_code == 429:
                    retry_after = int(e.response.headers.get("Retry-After", 60))
                    print(f"Rate limited. Waiting {retry_after}s...")
                    time.sleep(retry_after)
                else:
                    raise

    return scan_ids


# Usage
targets = [
    "https://app.example.com",
    "https://api.example.com",
    "https://admin.example.com"
]

scan_ids = batch_scan(
    targets=targets,
    tool_id=Tool.WEBSITE_SCANNER,
    tool_params={"scan_type": "deep"}
)

Wait for multiple scans

def wait_for_scans(scan_ids: list, poll_interval: int = 30) -> dict:
    """
    Wait for multiple scans to complete.
    Returns dict mapping scan_id to final status.
    """
    pending = set(scan_ids)
    results = {}

    while pending:
        for scan_id in list(pending):
            scan = get_scan(scan_id)
            status = scan["status_name"]

            if status in ("finished", "stopped", "failed to start", "timed out",
                          "aborted", "VPN connection error", "auth failed", "connection error"):
                results[scan_id] = status
                pending.remove(scan_id)
                print(f"Scan {scan_id}: {status}")

        if pending:
            print(f"Waiting for {len(pending)} scans...")
            time.sleep(poll_interval)

    return results


# Usage
statuses = wait_for_scans(scan_ids)
finished = [sid for sid, status in statuses.items() if status == "finished"]
print(f"{len(finished)}/{len(scan_ids)} scans completed successfully")

Generate report after scans

def scan_and_report(
    targets: list,
    tool_id: int,
    tool_params: dict = None,
    report_format: str = "pdf"
) -> str:
    """
    Scan multiple targets and generate a consolidated report.
    Returns path to downloaded report.
    """
    # Start all scans
    scan_ids = batch_scan(targets, tool_id, tool_params)

    # Wait for completion
    statuses = wait_for_scans(scan_ids)

    # Get completed scans
    completed = [sid for sid, s in statuses.items() if s == "finished"]
    if not completed:
        raise RuntimeError("No scans completed successfully")

    # Generate report
    report_id = create_report(
        source="scans",
        resource_ids=completed,
        format=report_format,
        group_by="vulnerability"
    )

    # Wait for report
    while True:
        report = get_report(report_id)
        if report["status"] == "finished":
            break
        elif report["status"] in ("failed", "unauthorized"):
            raise RuntimeError(f"Report generation failed: {report['status']}")
        time.sleep(5)

    # Download
    output_path = f"report_{report_id}.{report_format}"
    download_report(report_id, output_path)

    return output_path


# Usage
report_path = scan_and_report(
    targets=["https://example.com", "https://api.example.com"],
    tool_id=Tool.WEBSITE_SCANNER,
    tool_params={"scan_type": "deep"},
    report_format="pdf"
)
print(f"Report saved to: {report_path}")
Always store your API key in an environment variable:
import os
API_KEY = os.environ.get("PENTEST_TOOLS_API_KEY")