vPenTest API

NOTE  This feature is available to all vPenTest customers including MSP partners and internal IT teams.

The vPenTest API enables direct integration with your existing systems or internal tools, eliminating the need for manual platform interaction. Developers can easily connect vPenTest to internal tools and workflows by pulling data such as organizations, schedules, download URLs for all report types, and more.

Step 1: Generate an API key in vPenTest

  1. From your profile drop-down menu in the upper-right corner of the header, click Generate API Key.

  2. Decide if you want the API to expire by a certain date or not expire at all.
  3. Click Generate API Key.
  4. Be sure to copy your generated API key, which is displayed here only once. You can generate an API key any time to safely replace the existing one.

Step 2: Authorize the API key in the API documentation

  1. Once your API key is generated, visit the vPenTest API documentation.
  2. Click Authorize in the upper-right corner of the page.
  3. Enter the API key generated from vPenTest.

Example usage

To authenticate API requests, include your API key in the Authorization header using the Bearer token format. Below are some examples demonstrating how to use your API key with different programming languages.

List All Assessments (cURL)

curl -X GET "https://app.vpentest.io/api/v3/assessments" \
	-H "Authorization: Bearer YOUR_API_KEY_HERE" \
	-H "Content-Type: application/json"

Response

{
 "data": [
  {
   "id": "24b38497-01cd-4539-a7d5-4f4b0ced2228",
   "company_id": "789e0123-e89b-12d3-a456-426614174005",
   "name": "External Network Security Assessment",
   "severity": "informational",
   "created_at": "2025-02-24T16:21:22.093Z",
   "updated_at": "2025-02-24T16:44:38.689Z"
  }
 ],
 "total_count": 25,
 "page": 1,
 "per_page": 25,
 "total_pages": 1
}

Get Assessment Findings (Python)

import requests

API_KEY = "YOUR_API_KEY_HERE"
ASSESSMENT_ID = "24b38497-01cd-4539-a7d5-4f4b0ced2228"

headers = {
	"Authorization": f"Bearer {API_KEY}",
	"Content-Type": "application/json"
}

response = requests.get(
	f"https://app.vpentest.io/api/v3/assessments/{ASSESSMENT_ID}/findings",
	headers=headers
)

findings = response.json()
	print(f"Total findings: {findings['total_count']}")

for finding in findings['data']:
print(f"- {finding['name']} (Severity: {finding['severity']})")

Get Report Download URLs (Ruby)

require "net/http"
require "json"
require "uri"

API_KEY  = "YOUR_API_KEY_HERE"
REPORT_ID = "123e4567-e89b-12d3-a456-426614174000"
BASE_URL = "https://app.vpentest.io"

# --- 1) Fetch report metadata (requires API key) ---
api_uri = URI("#{BASE_URL}/api/v3/reports/#{REPORT_ID}")
report = Net::HTTP.start(api_uri.host, api_uri.port, use_ssl: api_uri.scheme == "https") do |http|
	req = Net::HTTP::Get.new(api_uri)
	req["Authorization"] = "Bearer #{API_KEY}"
	res = http.request(req)
	abort "API error #{res.code}: #{res.body[0, 200]}" unless res.is_a?(Net::HTTPSuccess)
	JSON.parse(res.body)
end

file_url = report.dig("data", "executive_summary_url")
abort "No executive_summary_url (report may not include that file yet)." if file_url.nil? || file_url.empty?

# --- 2) Request signed download on app host (same API key) ---
attach_uri = URI(file_url)
attach_res = Net::HTTP.start(attach_uri.host, attach_uri.port, use_ssl: attach_uri.scheme == "https") do |http|
	req = Net::HTTP::Get.new(attach_uri)
	req["Authorization"] = "Bearer #{API_KEY}"
	http.request(req)
end
abort "get_attachment error #{attach_res.code}" unless attach_res.is_a?(Net::HTTPRedirection)
location = attach_res["location"]
abort "Missing Location header after redirect." if location.nil? || location.empty?

# --- 3) Follow redirect to S3 (presigned URL — no Bearer token) ---
# IMPORTANT! This will be an AWS S3 host URL (not the vpentest host), make sure your firewall does not block it
s3_uri = URI(location)
file = Net::HTTP.start(s3_uri.host, s3_uri.port, use_ssl: s3_uri.scheme == "https") do |http|
	req = Net::HTTP::Get.new(s3_uri)
	http.request(req)
end
abort "Download failed #{file.code}: #{file.body[0, 500]}" unless file.is_a?(Net::HTTPSuccess)

File.binwrite("report.bin", file.body) # rename by Content-Type or URL; may be .pdf, .zip, etc.