---
title: "Behaviorally scan every pinned dependency in a lockfile, billed per package."
method: POST
path: "/v1/scan/batch"
---

# Behaviorally scan every pinned dependency in a lockfile, billed per package.

`POST /v1/scan/batch`

## Request

- Base URL: `https://lazaretto.dev`
- URL: `https://lazaretto.dev/v1/scan/batch`
- Auth: API key in header `X-API-Key`

The paid counterpart to /v1/lockfile, which only matches identities. Reads the code of each package and screens for credential theft, exfiltration, obfuscation, prompt injection and install-time droppers. Each result carries a verdict, a risk level, the ids of the rules that fired and a risk summary. It does not carry file-and-line evidence: for that, scan the package on its own with POST /v1/scan (scan_artifact over MCP). Requires an X-API-Key holding credits: one credit per package that returns a verdict, NOTHING for a package that errors (its reservation is refunded). Capped at 25 packages per call, in lockfile order, deduplicated, worst verdict first. Sending the same lockfile again rescans the same first 25. To continue, send the rest as {"packages":[...]}: the `not_scanned.packages` list from the result (name@version strings) is accepted as it is. Read `complete_coverage` before trusting the result: false means something was capped, errored, only partly readable, or skipped for having no registry identity, so it is not a clean bill of health for the whole tree.

## Request body

- object
  - `lockfile` string
  - `packages` union[]: Exactly pinned packages, as {name, version} objects or name@version strings.
    - union
      - object
        - `name` string
        - `version` string
      - string

## Response `200`

Per-package verdicts plus coverage and billing detail. `not_scanned` is null or {count, reasons, by_reason: {credits, time, cap}, packages}: by_reason splits count by cause, and packages lists the name@version of every package not scanned, in lockfile order. `refund_failed` is true only when unbilled credits could not be returned automatically.

## Other responses

- `400`: No exactly-pinned versions found.
- `401`: Missing or invalid API key.
- `402`: No credits available. Body quotes the packs and their prices.
- `429`: Rate limited (per key).
- `503`: The batch could not be completed. No scan credits are billed; `refund_failed` says whether the reserved credits came back. `auto_reload_possible` is present and true when the key has auto-reload switched on: reserving the credits may have started a reload, and if it did, that pack was charged to the saved card and its credits are on the key.

---

[API](https://skmtc.dev/lazaretto/apis/lazaretto-skill-tool-verification-api.md) · [All operations](https://skmtc.dev/lazaretto/apis/lazaretto-skill-tool-verification-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lazaretto/apis/lazaretto-skill-tool-verification-api/revisions/2bd4e34d0067?raw)
