API Reference · Your Data

List Your Scans

Every EdgeProtect attack-surface scan run for your organisation, newest first. Each row embeds the target it ran against and rollup counts for everything the scan found — hosts, ports, services, domains, IP addresses, DNS records, SSL certificates, network ranges, vulnerabilities (and instances), and exposed credentials.

This is the entry point to the whole attack-surface dataset, and it is designed to be cheap: one call answers "what scans do I have, how big is each one, and did anything new appear?" There is deliberately no /my/targets endpoint — the target is on every scan row.

GET
/v1/my/scans

Cost: 1 credit per successful call · Scope: api:org

Query parameters

targetstring (uuid)optional

Only scans of this target. The target id comes from the target.id field of any scan row.

statusstringoptional

Only scans with this status (e.g. completed).

limitintegeroptional

Page size, 1–200. Default 50. Out-of-range is a 400, not a silent clamp.

cursorstringoptional

Opaque pagination cursor from a previous page's next_cursor. Replay the same filters with it.

Request

curl https://api.precursorintelligence.com/functions/v1/my/scans \
  -H "Authorization: Bearer $PRECURSOR_API_KEY"

Response

200 OK
{
  "data": {
    "count": 2,
    "items": [
      {
        "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-7cd7-6421-4d79-bc5bbe00bf86",
          "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
        }
      }
    ],
    "next_cursor": "eyJpIjogImVjM2Q4...",
    "has_more": true,
    "generated_at": "2026-08-18T09:30:00+00:00"
  },
  "meta": {
    "request_id": "8b16efb8-9a6c-4ace-b817-11cbee626d08",
    "api_version": "1.3.0",
    "credits": { "charged": 1, "remaining": 9998 }
  }
}

Response fields

data.items[].idstring (uuid)optional

The scan id. Use it in /my/scans/{scan_id}, …/delta and …/credentials.

data.items[].targetobjectoptional

The target the scan ran against: id, name, value (e.g. the domain) and type.

data.items[].countsobjectoptional

Rollup counts for every collection the scan produced. Authoritative — use them to decide whether a scan is worth fetching before spending a credit on the full document.

data.next_cursorstring | nulloptional

Follow until null, replaying the same filters. Opaque — do not parse it.

The intended polling loop

Scans are immutable once complete, so the cheapest way to stay current is:

  1. GET /my/scans?limit=1 — has a new scan appeared? (1 credit)
  2. If yes: GET /my/scans/{id}/delta for what changed, or GET /my/scans/{id} for the full snapshot. (1 credit)

That is 1–2 credits per scan cycle. There is no need to re-fetch a scan you have already downloaded — it will never change.

Errors

StatuserrorWhen
400invalid_target, invalid_status, invalid_limit, invalid_cursorA parameter failed validation. Free — rejected before billing.
401invalid_authorizationMissing or malformed Authorization header.
401unauthorizedInvalid/revoked key, IP not allowlisted, or the key lacks the api:org scope.
402insufficient_creditsOrganisation credit balance is below 1.
405method_not_allowedOnly GET and OPTIONS are accepted.
500internalUnexpected server error (credit auto-refunded).

See Errors for the full error reference.