Get a Scan
One whole EdgeProtect scan as a single document, for a single credit: the scan summary with its rollup counts, plus hosts (each with its IP addresses and open ports, and what is listening on them), domains, DNS records, SSL certificates, network ranges, and vulnerabilities (with their instances inline).
This is deliberately the opposite of a one-endpoint-per-table API. Walking the same data as separate hosts/ports/services/DNS/SSL collections would cost 10+ credits per scan; this returns the entire snapshot for 1. The projection is lean — no scanner metadata, raw banners, certificate chains or proof-of-concept text — a typical full document is on the order of 100 kB.
Scans are immutable once complete: fetch a scan once and cache it forever.
/v1/my/scans/{scan_id}Cost: 1 credit per successful call · Scope: api:org
Path parameters
scan_idstring (uuid)requiredA scan id from GET /my/scans.
Query parameters
sectionsstringoptionalComma-separated projection of which sections to include, from hosts,
domains, dns_records, ssl_certificates, network_ranges,
vulnerabilities. Omit for everything. The scan summary is always
present. Same price either way — this is a bandwidth knob, not a
billing one. Unknown or duplicate names are a free 400.
Request
curl "https://api.precursorintelligence.com/functions/v1/my/scans/f63285e2-9c78-4020-b2bb-5c8bd2ca51ed" \
-H "Authorization: Bearer $PRECURSOR_API_KEY"Response
Trimmed for length — each array shows one representative entry.
{
"data": {
"scan": {
"id": "f63285e2-9c78-4020-b2bb-5c8bd2ca51ed",
"name": "Scan 06/08/2026 - acmecorp.com",
"status": "completed",
"created_at": "2026-08-06T06:12:44+00:00",
"target": { "id": "73d787fb-…", "name": "Acme Corporation", "value": "acmecorp.com", "type": "domain" },
"counts": { "hosts": 16, "ports": 36, "services": 36, "domains": 1, "ip_addresses": 17,
"dns_records": 34, "ssl_certificates": 13, "network_ranges": 6,
"vulnerabilities": 15, "vulnerability_instances": 42, "exposed_credentials": 40 }
},
"hosts": [
{
"id": "8138cd83-46e2-42c3-bd94-97a2c727ba22",
"hostname": "vpn",
"fqdn": "vpn.acmecorp.com",
"os": { "family": "Linux" },
"status": "active",
"last_seen": "2026-08-06T02:40:33+00:00",
"ip_addresses": [
{ "ip": "203.0.113.65", "version": 4, "city": "London", "country_code": "GB",
"asn": "AS16509", "isp": "Amazon.com, Inc." }
],
"open_ports": [
{ "ip": "203.0.113.65", "port": 443, "protocol": "tcp",
"service": "https", "product": "nginx", "version": "1.24.0",
"ssl": true, "http_status": 200, "http_title": "Acme VPN portal",
"cpe": ["cpe:2.3:a:f5:nginx:1.24.0:*:*:*:*:*:*:*"] }
]
}
],
"domains": [
{ "id": "…", "domain_name": "acmecorp.com", "domain_type": "primary", "status": "active",
"registrar": "Example Registrar Ltd", "registration_date": "2003-04-11",
"expiration_date": "2027-04-11", "nameservers": ["ns1.example-dns.com"] }
],
"dns_records": [
{ "type": "MX", "name": "acmecorp.com", "value": "aspmx.l.google.com", "ttl": 3600, "priority": 1 }
],
"ssl_certificates": [
{ "id": "…", "subject_common_name": "*.acmecorp.com",
"subject_alternative_names": ["*.acmecorp.com", "acmecorp.com"],
"issuer": { "common_name": "R11", "organization": "Let's Encrypt" },
"valid_from": "2026-06-20T00:00:00+00:00", "valid_to": "2026-09-18T00:00:00+00:00",
"is_expired": false, "is_wildcard": true,
"key_algorithm": "RSA", "key_size": 2048, "signature_algorithm": "SHA256withRSA",
"fingerprint_sha256": "9f86d081884c7d65…" }
],
"network_ranges": [
{ "cidr": "203.0.113.0/24", "netname": "ACME-NET", "organization": "Acme Corporation",
"country_code": "GB", "asn": "AS64500" }
],
"vulnerabilities": [
{
"id": "…",
"title": "TLS 1.0 enabled",
"severity": "medium",
"cve_id": null,
"cwe_id": "CWE-326",
"cvss_score": null,
"epss_score": null,
"kev_status": false,
"public_exploit_available": false,
"status": "open",
"affected_component": "vpn.acmecorp.com:443",
"description": "The service accepts TLS 1.0 connections…",
"remediation": "Disable TLS 1.0 and 1.1; require TLS 1.2 or later.",
"affects": ["vpn.acmecorp.com:443"],
"instances": [
{ "id": "…", "title": "vpn.acmecorp.com:443", "entity_type": "service",
"entity_id": "…", "status": "open" }
]
}
]
},
"meta": {
"request_id": "8b16efb8-9a6c-4ace-b817-11cbee626d08",
"api_version": "1.3.0",
"credits": { "charged": 1, "remaining": 9997 }
}
}Response fields
data.scanobjectoptionalAlways present, whatever sections you asked for: id, name, status,
created_at, the embedded target, and the rollup counts.
data.hostsarrayoptionalOne entry per discovered host: hostname, fqdn, os (name/family/version
where known), ip_addresses (with geo + ASN/ISP), and open_ports. Each
open port carries the service observed on it — service, product,
version, ssl, http_status, http_title and cpe strings. There is no
separate ports or services collection; this is it.
data.vulnerabilitiesarrayoptionalOrdered worst-first by severity. Each finding carries scoring context
(cvss_score, epss_score, kev_status, public_exploit_available),
description and remediation text, the affects list, and its concrete
instances (what exactly it was observed on).
data.ssl_certificatesarrayoptionalOrdered by expiry (valid_to ascending) — the certificates about to bite
come first.
An unknown scan_id — or another organisation's — returns 200 OK with
data set to null, and the credit is still charged. The API never
distinguishes "absent" from "not yours"; that distinction would itself leak
which ids exist. Take scan ids from GET /my/scans rather than guessing.
Breach-exposed credentials have their own endpoint,
GET /my/scans/{scan_id}/credentials,
which returns them redacted to existence flags. The bundle's
scan.counts.exposed_credentials tells you how many exist.
Errors
| Status | error | When |
|---|---|---|
| 400 | invalid_scan_id, invalid_sections | The path or sections failed validation. Free — rejected before billing. |
| 401 | invalid_authorization | Missing or malformed Authorization header. |
| 401 | unauthorized | Invalid/revoked key, IP not allowlisted, or the key lacks the api:org scope. |
| 402 | insufficient_credits | Organisation credit balance is below 1. |
| 405 | method_not_allowed | Only GET and OPTIONS are accepted. |
| 500 | internal | Unexpected server error (credit auto-refunded). |
See Errors for the full error reference.