Private Scanner API
The Netlas Private Scanner API provides functionalities for managing and analyzing network scans efficiently.
With this API, it is possible to:
- Initiate new scans
- Retrieve a list of previously executed scans
- Check the status of ongoing or completed scans
All scan results are securely stored in a private index, which can be accessed through the Responses search tool.
Scanner API Endpoints
HTTP Method | Endpoint | Description |
---|---|---|
GET |
/api/scanner/ | List scans |
POST |
/api/scanner/ | Create scan |
GET |
/api/scanner/{id} / |
Get scan details |
PATCH |
/api/scanner/{id} / |
Update scan details |
POST |
/api/scanner/change_priority/ | Change scan priority |
DELETE |
/api/scanner/{id} / |
Delete scan |
Create Scan
POST
/scanner/
To begin a new scan, an API request must include a set of targets to be analyzed. The targets define the scope of the scan and determine which network assets will be examined. A valid list of targets may contain:
- IP addresses – Individual IP addresses (e.g.,
192.168.1.1
) can be scanned for open ports, services, and vulnerabilities. - Domains – Fully qualified domain names (e.g.,
example.com
) allow for DNS-based reconnaissance and analysis. - CIDR ranges – Subnet ranges (e.g.,
192.168.1.0/24
) enable scanning of multiple IP addresses within a defined network segment.
Additionally, an optional label may be specified to help identify the scan. It can be useful for categorization, filtering, or distinguishing scans conducted for different purposes.
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))
If any errors occur while creating a scan, refer to the Error Handling section for troubleshooting.
List Scans and Scan Details
GET
/scanner/{id}
/
The /scanner/
endpoint provides access to scans associated with the authenticated account. This includes:
- Fetching a list of all available scans
- Retrieving detailed information about a specific scan – By specifying a unique scan ID
To obtain details of an individual scan, use the /scanner/{id}/
endpoint. Both endpoints return comprehensive information, including scan statuses, labels, and other relevant metadata.
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.
curl -X 'GET' "https://app.netlas.io/api/scanner/SCAN_ID/" \
-H "X-API-Key: API_KEY" \
-H "accept: application/json" | jq "."
{
"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"
}
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))
{
"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
PATCH
/scanner/{id}
/
The PATCH
method on the /scanner/{id}/
endpoint allows you to update scan labels of an existing scans.
import requests
import json
api_key = "your_api_key"
base_url = "https://app.netlas.io/api/scanner/"
scan_id = "your_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 PATCH request to update the scan
response = requests.patch(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/
Allows users to adjust the priority of a scan within the processing queue. This feature is useful for prioritizing urgent scans or delaying less critical ones, ensuring better queue management.
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.
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: 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
Certain tools or features may not be accessible depending on your subscription tier. Receiving this error message means that the Scanner is not available on your plan.
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.
- Alternatively, consider upgrading your subscription plan if additional storage capacity is required.
Error 400: Max number of targets per scan exceeded
This error occurs when the number of targets specified for a scan surpasses the maximum limit allowed by your subscription plan. Each plan imposes a cap on the number of targets that can be scanned simultaneously:
- Reduce the number of targets in your scan request and try again.
- Upgrade to a higher-tier plan if you require a larger scan limit.
Error 400: Not Enough Scan Coins
This error indicates insufficient scan coins to perform the requested action. The number of scan-coins is fixed according to the monthly subscription level:
- Check your available scan coins on the profile page.
- Re-purchase a subscription to access the Scanner tool availability or wait until the next allocation.
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 scans.
To retrieve the complete list of scan results, use the Responses Download Endpoint of the Search Tools API.
#!/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
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, "indices": "your_scan_index_id"})
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,
"indices": "your_scan_index_id"
}
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)
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.
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.
#!/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" == "done" ]; 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
#!/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 == "done":
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
<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)