API Reference · Your Data

Scan Delta

What changed between this scan and the previous scan of the same target — new and removed hosts, ports, services, certificates, findings — as a precomputed summary plus an ordered event list. The diff is computed by the platform when a scan completes; this endpoint returns it, it never computes on the fly.

Combined with GET /my/scans, this is the cheapest way to stay current: notice a new scan (1 credit), read its delta (1 credit), and only fetch the full document when the delta says something you care about moved.

GET
/v1/my/scans/{scan_id}/delta

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

Path parameters

scan_idstring (uuid)required

The newer scan of the pair. The delta compares it against the previous scan of the same target, named in from_scan_id.

Request

curl "https://api.precursorintelligence.com/functions/v1/my/scans/f63285e2-9c78-4020-b2bb-5c8bd2ca51ed/delta" \
  -H "Authorization: Bearer $PRECURSOR_API_KEY"

Response

200 OK
{
  "data": {
    "scan_id": "f63285e2-9c78-4020-b2bb-5c8bd2ca51ed",
    "from_scan_id": "226f9d98-9974-2db7-c307-28f3c5203d10",
    "has_delta": true,
    "computed_at": "2026-08-06T06:45:12+00:00",
    "summary": {
      "added": 5, "removed": 0, "changed": 0, "total_changes": 5,
      "by_category": {
        "ports":                { "added": 2, "removed": 0, "changed": 0, "total_before": 34, "total_after": 36 },
        "hosts":                { "added": 1, "removed": 0, "changed": 0, "total_before": 15, "total_after": 16 },
        "vulnerabilities":      { "added": 2, "removed": 0, "changed": 0, "total_before": 13, "total_after": 15 }
      }
    },
    "events": [
      {
        "category": "hosts",
        "categoryLabel": "Hosts",
        "icon": "fas fa-server",
        "type": "added",
        "key": "staging.acmecorp.com",
        "label": "staging.acmecorp.com",
        "message": "New host discovered: staging.acmecorp.com",
        "weight": 60,
        "changes": null
      }
    ]
  },
  "meta": {
    "request_id": "8b16efb8-9a6c-4ace-b817-11cbee626d08",
    "api_version": "1.3.0",
    "credits": { "charged": 1, "remaining": 9996 }
  }
}

Response fields

data.has_deltabooleanoptional

false is a real answer, not an error: it means this is the first scan of its target, or the platform has not computed the diff. When false, summary, events, from_scan_id and computed_at are all null.

data.from_scan_idstring | nulloptional

The older scan the comparison ran against.

data.summaryobject | nulloptional

Overall added / removed / changed / total_changes counts, plus a by_category breakdown (categories: ports, hosts, ip_addresses, services, ssl_certificates, domains, subdomains, dns_records, network_ranges, vulnerabilities, vulnerability_instances, exposed_credentials) with before/after totals per category.

data.eventsarray | nulloptional

The individual changes. Each event has category, type (added / removed / changed), key (stable identity of the thing that changed), label, a human-readable message, a significance weight (higher = more attention-worthy — an exposed credential outranks a TTL change), and changes (the changed fields, on type: "changed" events).

A scan that isn't yours looks exactly like one that doesn't exist

An unknown scan_id — or another organisation's — returns 200 OK with data set to null, and the credit is still charged. Take scan ids from GET /my/scans.

Errors

StatuserrorWhen
400invalid_scan_idThe path segment is not a UUID. 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.