Skip to content

Private Scanner API

The Netlas Private Scanner API allows users to manage and analyze network scans efficiently. With this API, you can:

  • Initiate scans
  • Retrieve a list of scans
  • Check scan statuses

Scan results are securely stored in a private index, which can be accessed using the Responses search tool.

This guide includes examples in Python (using the requests library) and Shell (using curl). Replace placeholders like your_api_key_here and scan_id with your actual values.

Scanner API Endpoints

HTTP Method Endpoint Description
GET /api/scanner/ List scans
POST /api/scanner/ Create scan
GET /api/scanner/{id}/ Get scan details
PUT /api/scanner/{id}/ Update scan details
POST /api/scanner/change_priority/ Change scan priority
DELETE /api/scanner/{id}/ Delete scan

Create Scan

POST /scanner/

Use this endpoint to initiate a new scan by specifying a set of targets. A valid list of targets for scanning consists of IP addresses, domains, or CIDRs. Additionally, you can provide an optional label to identify the scan.

Scan a list of IP addresses and domains
curl -X 'POST' "https://app.netlas.io/api/scanner/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{"targets": ["your.target.com", "1.1.1.1", "1.1.1.0/24"], "label": "New Scan"}' | jq "."
Scan a list of IP addresses and domains
import requests
import json

# Set your API key and base URL
api_key = "your_api_key_here"
base_url = "https://app.netlas.io/api/scanner/"

# Define headers for API requests
headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
# Payload with the targets and label for the scan
payload = {
    "targets": ["dns.google", "8.8.8.8", "microsoft.com"],
    "label": "New Scan"
}
# Send the POST request to create a scan
response = requests.post(base_url, headers=headers, json=payload)
data = response.json()  # Parse the JSON response
print("Create Scan Response:", json.dumps(data, indent=4))

Refer to the Error Handling section if you encounter errors while creating a scan.

List Scans and Scan Details

GET /scanner/{id}/

Call /scanner/ to fetch a list of available to your private scans. This includes retrieving a list of all scans available to the user and getting detailed information about a specific scan using its ID.

Get list of scans
curl -X 'GET' "https://app.netlas.io/api/scanner/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" | jq "."
Get list of scans
import requests
import json

api_key = "your_api_key_here"
base_url = "https://app.netlas.io/api/scanner/"

headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
response = requests.get(base_url, headers=headers)
data = response.json()  # Parse the JSON response
print("Scans List:", json.dumps(data, indent=4))  # Pretty print the result

Call /scanner/{id}/ to fetch the same data on particular scan. Both endpoinds provides detailed information, such as scan statuses, labels, and other relevant data.

Get status of particular scan
curl -X 'GET' "https://app.netlas.io/api/scanner/SCAN_ID/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" | jq "."
Abbreviated example of command output
{
"id": 349,
"label": "Scan 2024/11/10 21:54:15",
"targets": [
    "example.com",
    "subdomain.example.com",
    "1.1.1.1",
    "1.1.1.0/24"
],
"scan_started_dt": "2024-11-12T12:31:38Z",
"scan_ended_dt": "2024-11-12T12:36:40Z",
"scan_progress": [
    {
    "id": 4284,
    "state": "pending",
    "description": null,
    "timestamp": "2024-11-12T12:31:24.753541Z"
    },
    {
    "id": 4285,
    "state": "scheduled",
    "description": "Scan queued",
    "timestamp": "2024-11-12T12:31:36Z"
    },
    {
    "id": 4286,
    "state": "scanning",
    "description": "1/10 - Scan started",
    "timestamp": "2024-11-12T12:31:38Z"
    },
        . . .
    {
    "id": 4296,
    "state": "done",
    "description": "Scan finished",
    "timestamp": "2024-11-12T12:36:40Z"
    }
],
"created_dt": "2024-11-12T12:31:24.748832Z",
"index_id": 512,
"saved_graph": null,
"estimated_time_to_complete": null,
"queue_position": null,
"count": 1826,
"owner": {},
"is_scan_from_team": false,
"status": "done"
}
Get status of particular scan
import requests
import json

api_key = "your_api_key_here"
base_url = "https://app.netlas.io/api/scanner/"
scan_id = "required_scan_id"

headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
# Construct the URL for the specific scan ID
scan_url = f"{base_url}{scan_id}/"
# Send the GET request to retrieve the scan details
response = requests.get(scan_url, headers=headers)
data = response.json()  # Parse the JSON response
print("Scan Details:", json.dumps(data, indent=4))
Abbreviated example of command output
{
"id": 349,
"label": "Scan 2024/11/10 21:54:15",
"targets": [
    "example.com",
    "subdomain.example.com",
    "1.1.1.1",
    "1.1.1.0/24"
],
"scan_started_dt": "2024-11-12T12:31:38Z",
"scan_ended_dt": "2024-11-12T12:36:40Z",
"scan_progress": [
    {
    "id": 4284,
    "state": "pending",
    "description": null,
    "timestamp": "2024-11-12T12:31:24.753541Z"
    },
    {
    "id": 4285,
    "state": "scheduled",
    "description": "Scan queued",
    "timestamp": "2024-11-12T12:31:36Z"
    },
    {
    "id": 4286,
    "state": "scanning",
    "description": "1/10 - Scan started",
    "timestamp": "2024-11-12T12:31:38Z"
    },
        . . .
    {
    "id": 4296,
    "state": "done",
    "description": "Scan finished",
    "timestamp": "2024-11-12T12:36:40Z"
    }
],
"created_dt": "2024-11-12T12:31:24.748832Z",
"index_id": 512,
"saved_graph": null,
"estimated_time_to_complete": null,
"queue_position": null,
"count": 1826,
"owner": {},
"is_scan_from_team": false,
"status": "done"
}

Key fields in response:

  • id is unique identifier of scan.
  • label – scan label assigned by the user (by default it contains creation date and time).
  • targets – list of targets to scan (IP addresses, domains, subnets).
  • scan_started_dt (scan_ended_dt) is date and time the scan started (completed).
  • scan_progress is an array with the progress of scan:
    • Another id field is unique identifier of progress step.
    • state – current status of step (pending, scheduled, scanning, done).
  • status – total scan status (done, failed, in_progress, etc.).
  • count – total number of results found.

Update Scan Label

PUT /scanner/{id}/

The PUT method on the endpoint /scanner/{id}/ allows you to modify the attributes of an existing scan. For now, only a scan label can be updated.

Modify scan attributes
curl -X 'PUT' "https://app.netlas.io/api/scanner/SCAN_ID/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{"label": "Updated Scan Label"}' | jq "."
Modify scan attributes
import requests
import json

api_key = "your_api_key"
base_url = "https://app.netlas.io/api/scanner/"
scan_id = "required_scan_id"
new_label = "New Scan Updated"

headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
# URL to update scan details
update_url = f"{base_url}{scan_id}/"
payload = {"label": new_label}
# Send the PUT request to update the scan
response = requests.put(update_url, headers=headers, json=payload)
data = response.json()  # Parse the JSON response
print("Updated Scan Response:", json.dumps(data, indent=4))

Change Scan Priority

POST /scanner/change_priority/

Adjust the priority of the scan to ensure that it is processed as soon as possible if needed to manage the scan queue.

Scan priority alter
curl -X 'POST' "https://app.netlas.io/api/scanner/change_priority/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{"id": "SCAN_ID", "shift": SHIFT_POS"}' | jq "."
Scan priority alter
import requests
import json

api_key = "your_api_key"
base_url = "https://app.netlas.io/api/scanner/"
scan_id = "required_scan_id"
shift_pos = "shift_position"

headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
# URL to change priority
priority_url = f"{base_url}change_priority/"

payload = {"id": scan_id, "shift": shift_pos}

# Send the POST request to change the scan's priority
response = requests.post(priority_url, headers=headers, json=payload)

# Check response status
if response.status_code == 200:
    try:
        data = response.json()  # Parse the JSON response
        print("Change Scan Priority Response:", json.dumps(data, indent=4))
    except json.JSONDecodeError:
        print("Error: Response is not valid JSON.")
        print("Response Text:", response.text)
else:
    print(f"Failed request: {response.status_code} - {response.text}")

The shift argument in the change_priority request allows you to adjust the relative position of a scan in the queue:

  • A negative value, e.g., -2, moves the scan 2 positions closer to the front of the queue, prioritizing it for earlier execution.
  • A positive value, e.g., +3, moves the scan 3 positions closer to the back of the queue, delaying its execution.

Use negative shift values to speed up the execution of tasks with high priority.

This adjustment applies only to scans that are not yet scheduled (status Pending).

Delete Scan

DELETE /scanner/{id}/

This endpoint deletes a specific scan by its ID and erases the associated index. Once the scan has been removed, all related data, including the index, will be permanently detached.

Before deleting, use the GET /scanner/{id}/ method to check the scan data. Make sure that you delete the desired scan, as recovery is only possible for a short period of time.

Delete scan by id
curl -X 'DELETE' "https://app.netlas.io/api/scanner/SCAN_ID/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" | jq "."
Delete scan by id
import requests
import json

api_key = "your_api_key"
base_url = "https://app.netlas.io/api/scanner/"
scan_id = "required_scan_id"

headers = {
    "X-API-Key": api_key,
    "accept": "application/json",
    "Content-Type": "application/json"
}
# URL to delete the scan
delete_url = f"{base_url}{scan_id}/"
# Send the DELETE request
response = requests.delete(delete_url, headers=headers)
if response.status_code == 204:
    print("Scan successfully deleted.")
else:
    print("Delete Scan Response:", response.json())

Restore accidentally deleted scans

Accidentally deleted scans can be restored if you contact support within 1 week. After 2 weeks, deleted scans are permanently erased and cannot be recovered under any circumstances.

Error Handling

This section provides an overview of common error responses, their meanings, and recommendations for resolving them.

Error 400: Max Number of Stored Scans Exceeded

The number of scans a user can store depends on their subscription plan. When this limit is reached, any attempt to create a new scan will result in this error:

  • Delete one or more of your stored scans to free up space.
  • Alternatively, consider upgrading your subscription plan if additional storage capacity is required.

Error 400: Request Parsing Error

This error occurs when the server cannot process the request due to malformed or invalid input. Common causes include improperly formatted JSON, incorrect parameter names, or missing required fields:

  • Verify the request payload and ensure it complies with the API documentation.
  • Check for syntax errors in the JSON data or missing mandatory parameters.

Error 400: Tool Is Not Available for Current Subscription Plan

Receiving this error message means that the Scanner is not available in your data plan. Certain tools or features may not be accessible depending on your subscription tier. If you attempt to use a tool outside the scope of your plan, this error will occur:

  • Review the features included in your current subscription plan.
  • Upgrade to a higher-tier plan if the required tool is part of its feature set.

Error 400: Not Enough Scan Coins

This error indicates insufficient scan coins to perform the requested action. Scan coins are a consumable resource allocated to your account based on your plan:

  • Check your available scan coins on the account dashboard.
  • Re-purchase a subscription to access the Scanner tool availability or wait until the next allocation if your plan includes periodic replenishment.

The number of scan-coins is fixed according to the monthly subscription level.

Error 403: Current Subscription Plan Does Not Allow Making This Request

This error occurs when you try to perform an action with the Scan menu item or API Schema request, and your data plan limits it. This can happen if you exceed the maximum number of targets you can scan per day, or if you try to store too many scans:

  • Confirm the limits and permissions associated with your subscription plan.
  • Upgrade to a plan that provides access to the desired functionality or contact support for assistance.

Access Control

  • Only the user who initiated the scan has access to their private indexes.
  • If the user is part of a team, all team members can access the private indexes of other participants.
  • Only the owner of a scan can delete a scan and its associated index.

Access Scan Results

Scan data can be searched and downloaded using the Responses Search Tool. To access the data, you need to specify the index_id, which can be obtained by listing your scans.

To retrieve the complete list of scan results, use the Responses Download Endpoint of the Search Tools API.

Downloading Scan Results
#!/bin/bash

# Define the API key and the base URL for the Netlas API
API_KEY="YOUR_API_KEY"
BASE_URL="https://app.netlas.io/api"
QUERY="host:*.netlas.io"  # Define your search query

# Get the count of responses for the query
COUNT=$(curl -s -H "X-API-Key: $API_KEY" \
    "$BASE_URL/responses_count/?q=$QUERY" \
    | jq ".count")

# Check if responses exist and download them
if [ "$COUNT" -gt 0 ]; then
    echo "Downloading $COUNT results for query: $QUERY"
    curl -s -X POST "$BASE_URL/responses/download/" \
    -H "Content-Type: application/json" \
    -H "X-API-Key: $API_KEY" \
    -d '{
        "q": "'"${QUERY}"'",
        "fields": ["uri"],
        "source_type": "include",
        "size": '"${COUNT}"'
        }' -o "scan_results.json"
    echo "Results saved to scan_results.json"
else
    echo "No responses found for the query: $QUERY"
fi
Downloading Scan Results
import requests
import json

def download_scan_results(api_key, query):
    """
    Downloads scan results from Netlas API using the Download method.

    Parameters:
    - api_key (str): API key for authentication.
    - query (str): Search query to identify results to download.

    Returns:
    - None. Saves results to a JSON file.
    """
    base_url = "https://app.netlas.io/api"
    headers = {
        "X-API-Key": api_key,
        "accept": "application/json",
        "Content-Type": "application/json"
    }

    # Step 1: Get the response count
    count_url = f"{base_url}/responses_count/"
    count_response = requests.get(count_url, headers=headers, params={"q": query})

    if count_response.status_code != 200:
        print(f"Error fetching response count: {count_response.status_code}")
        print("Details:", count_response.json())
        return

    count = count_response.json().get("count", 0)
    if count == 0:
        print(f"No responses found for query: {query}")
        return

    print(f"Found {count} responses for query: {query}")

    # Step 2: Download the responses
    download_url = f"{base_url}/responses/download/"
    payload = {
        "q": query,
        "fields": ["uri"],  # Adjust fields as needed
        "source_type": "include",
        "size": count
    }

    download_response = requests.post(download_url, headers=headers, json=payload)
    if download_response.status_code != 200:
        print(f"Error downloading responses: {download_response.status_code}")
        print("Details:", download_response.json())
        return

    # Save results to file
    file_name = "scan_results.json"
    with open(file_name, "w") as file:
        json.dump(download_response.json(), file, indent=4)

    print(f"Scan results saved to {file_name}")

# Example usage
API_KEY = "YOUR_API_KEY"
QUERY = "host:*.netlas.io"

download_scan_results(API_KEY, QUERY)

Example of Usage

Below is an example of a script that:

  • Creates a scan using the Private Scanner API.
  • Fetches the list of scans to verify the creation.
  • Downloads the results of the scan in JSON format.
netlas_private_scanner.sh
#!/bin/bash

# Usage: ./netlas_private_scanner.sh <api_key> <targets> <output_file>

API_KEY="$1"
TARGETS="$2"
OUTPUT_FILE="${3:-results.json}"

BASE_URL="https://app.netlas.io/api/scanner/"

if [ -z "$API_KEY" ] || [ -z "$TARGETS" ]; then
    echo "Usage: $0 <api_key> <targets> [output_file]"
    echo "Example: $0 your_api_key_here \"example.com,8.8.8.8\" results.json"
    exit 1
fi

# Step 1: Create a scan
echo "Creating a new scan..."
TARGETS_JSON=$(echo "$TARGETS" | jq -R 'split(",")')
SCAN_RESPONSE=$(curl -s -X POST "$BASE_URL" \
    -H "X-API-Key: $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"targets\": $TARGETS_JSON, \"label\": \"Test Scan\"}")

# Check if the response is an array
if echo "$SCAN_RESPONSE" | jq -e '.[0]' > /dev/null 2>&1; then
    SCAN_ID=$(echo "$SCAN_RESPONSE" | jq -r '.[0].id')
else
    SCAN_ID=$(echo "$SCAN_RESPONSE" | jq -r ".id")
fi

if [ -z "$SCAN_ID" ] || [ "$SCAN_ID" == "null" ]; then
    echo "Error creating scan: $SCAN_RESPONSE"
    exit 1
fi
echo "Scan created with ID: $SCAN_ID"

# Step 2: Poll for scan status
while true; do
    STATUS_RESPONSE=$(curl -s -X GET "${BASE_URL}${SCAN_ID}/" \
        -H "X-API-Key: $API_KEY")
    STATUS=$(echo "$STATUS_RESPONSE" | jq -r ".status")

    echo "Current scan status: $STATUS"
    if [ "$STATUS" == "completed" ]; then
        echo "Scan completed!"
        break
    elif [ "$STATUS" == "failed" ]; then
        echo "Scan failed: $STATUS_RESPONSE"
        exit 1
    fi
    sleep 30
done

# Step 3: Download results
echo "Downloading scan results..."
curl -s -X GET "${BASE_URL}${SCAN_ID}/" \
    -H "X-API-Key: $API_KEY" \
    -H "accept: application/json" \
    -H "Content-Type: application/json" \
    -o "$OUTPUT_FILE"

if [ $? -eq 0 ]; then
    echo "Results saved to $OUTPUT_FILE"
else
    echo "Error downloading results"
    exit 1
fi
netlas_private_scanner.py
#!/usr/bin/env python3

import requests
import json
import argparse
import time

BASE_URL = "https://app.netlas.io/api/scanner/"


def create_scan(api_key, targets, label="New Scan"):
    headers = {
        "X-API-Key": api_key,
        "Content-Type": "application/json"
    }
    payload = {
        "targets": targets,
        "label": label
    }
    response = requests.post(BASE_URL, headers=headers, json=payload)
    if response.status_code == 201:
        scan_data = response.json()
        print("DEBUG: scan_data =", scan_data)  # Debugging output
        if isinstance(scan_data, dict) and "id" in scan_data:
            return scan_data["id"]
        elif isinstance(scan_data, list) and len(scan_data) > 0:
            return scan_data[0].get("id", None)
        else:
            print("Unexpected response format:", scan_data)
            return None
    else:
        print(
            f"Error Creating Scan: {response.status_code} - {response.text}")
        return None


def poll_scan_status(api_key, scan_id):
    headers = {"X-API-Key": api_key}
    while True:
        response = requests.get(f"{BASE_URL}{scan_id}/", headers=headers)
        if response.status_code == 200:
            status = response.json()["status"]
            print(f"Scan Status: {status}")
            if status == "completed":
                print("Scan completed!")
                break
            elif status == "failed":
                print(f"Scan failed: {response.json()}")
                exit(1)
        else:
            print(
                f"Error Checking Status: {response.status_code} - {response.text}")
            exit(1)
        time.sleep(30)


def download_results(api_key, scan_id, output_file):
    headers = {
        "X-API-Key": api_key,
        "accept": "application/json",
        "Content-Type": "application/json"
    }
    response = requests.get(f"{BASE_URL}{scan_id}/", headers=headers)
    if response.status_code == 200:
        try:
            scan_data = response.json()
            with open(output_file, "w") as file:
                json.dump(scan_data, file, indent=4)
            print(f"Results saved to {output_file}")
        except json.JSONDecodeError:
            print("Error: The response is not valid JSON.")
            print("Server response:", response.text)
    else:
        print(
            f"Error Downloading Results: {response.status_code} - {response.text}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Netlas Scanner Script")
    parser.add_argument("api_key", help="API key for authentication")
    parser.add_argument("targets",
                        help="Comma-separated list of targets (e.g., example.com,8.8.8.8)")
    parser.add_argument("-o", "--output",
                        default="results.json", help="Output file for results (default: results.json)")
    parser.add_argument(
        "-l", "--label", default="New Scan", help="Label for the scan")

    args = parser.parse_args()
    targets_list = args.targets.split(",")

    scan_id = create_scan(args.api_key, targets_list, args.label)
    if not scan_id:
        exit(1)

    print("Polling scan status...")
    poll_scan_status(args.api_key, scan_id)

    print("Downloading results...")
    download_results(args.api_key, scan_id, args.output)

Notes in creating scan

  • Replace your_api_key_here with your actual API key.
  • Ensure you have the jq utility installed for parsing JSON in the bash script. Use sudo apt install jq to install it on Debain-based systems.

Usage

python3 netlas_private_scanner.py <api_key> <targets> [-o OUTPUT] [-l LABEL]
  • <api_key>: Your Netlas API key (required)
  • <targets>: Comma-separated list of targets to scan (e.g., example.com,8.8.8.8) (required)
  • -o, --output: Specify the output file for saving scan results (optional, default: results.json)
  • -l, --label: Label for the scan (optional, default: New Scan)
Example of private_scanner.py usage
python3 netlas_private_scanner.py your_api_key_here \
   "example.com,8.8.8.8,1.1.1.0/24" -o scan_results.json -l "My Custom Scan"