Set false_positive_budget to match your risk tolerance, then use the measured fires_at level for any additional policy.
Find 0-day malware in the software supply chain.
Beamline is an API for finding 0-day malware in the software supply chain. Calls wait for an assessment and may be retried.
Analyze packages, exact downloads, and uploaded artifacts—not just files that already appear on a threat list.
Beamline is based on the open-source Atomdrift project.
| Method | Route | Use it for |
|---|---|---|
| GET | /v1/lookup | Ask whether an artifact is already known. |
| POST | /v1/analyze | Analyze a PURL, exact URL, or uploaded bytes. |
| POST | /v1/flush | Forget every cached answer for an artifact. |
Lookup
GET /v1/lookup returns what we already know. It never starts an analysis. Ask with a PURL, an exact URL, or a SHA-256.
PURL
GET /v1/lookup?purl=…curl -sS \
"https://api.isotope13.ai/v1/lookup?purl=pkg%3Anpm%2Faxios%401.19.0"URL-encode PURLs. Repeat purl for up to 50 packages. One PURL returns an object; several return a list.
Exact URL New
GET /v1/lookup?url=…curl -sS \
"https://api.isotope13.ai/v1/lookup?url=https%3A%2F%2Fregistry.npmjs.org%2Faxios%2F-%2Faxios-1.19.0.tgz"URL-encode the url query parameter value.
SHA-256
GET /v1/lookup?sha256=…curl -sS \
"https://api.isotope13.ai/v1/lookup?sha256=dd46efaa38534ef67370ac3ebc6151e4cf475fdb4bb68f8e2003b432d11d92c2"Analyze
POST /v1/analyze streams newline-delimited JSON until an assessment arrives. Complex samples may take minutes. Retrying the same PURL or URL reuses an analysis already in progress.
PURL
POST /v1/analyze?purl=…curl -sN -X POST \
"https://api.isotope13.ai/v1/analyze?purl=pkg%3Acargo%2Ftokio%401.40.0"Exact URL New
POST /v1/analyze?url=…curl -sN -X POST \
"https://api.isotope13.ai/v1/analyze?url=https%3A%2F%2Fregistry.npmjs.org%2Faxios%2F-%2Faxios-1.19.0.tgz"Beamline fetches the exact URL and returns its SHA-256. The response includes a PURL when one is known.
Refresh a stored sample
POST /v1/analyze?sha256=…&refresh=1For a sample already held by Hopper, refresh=1 skips Beamline's edge and KV cache reads. A current Scan-local verdict may answer immediately; otherwise Scan accepts Hopper's verdict only when it matches Scan's current traits version. If it does not, Scan fetches the sample and runs the normal analysis path. The result refills the ordinary caches, and refresh is not part of their key.
Upload bytes
POST /v1/analyzecurl -sN -X POST \
--data-binary @sample.tgz \
-H "Content-Type: application/octet-stream" \
"https://api.isotope13.ai/v1/analyze"
Archives, binaries, and source are accepted. The default upload limit is 16 MiB.
Add ?full=1 to return {status: "analyzed", ml, raw, llm?} as the terminal line. The complete Scan envelope remains at the top level, with the same terminal status marker as the compact response. Full envelopes and compact decisions use separate cache entries.
Flush New
POST /v1/flush drops every cached answer for one artifact. Name it with ?purl=, ?url=, or ?sha256= — whichever you have. Nothing is re-analyzed; the next lookup or analyze starts from scratch.
POST /v1/flush?purl=…curl -X POST \\
"https://api.isotope13.ai/v1/flush?purl=pkg%3Anpm%2Faxios%401.19.0"An artifact is filed under more than one key: one per follow policy, one per response shape, and one per name that reaches it. A flush clears all of them, then follows the answers it found to the artifact's other names — its digest, its download URL, the PURL Scan normalized it to — and clears those too. The response reports what it reached.
Flushing something that was never cached is not an error. It returns 200 with keys_dropped: 0.
False-positive budget
false_positive_budget sets the false-positive rate you accept, measured per 100 million benign files. It defaults to 25 and controls the returned severity. We recommend choosing 1–250; values through 3000 are accepted because 3000 is the suspicious ceiling.
GET /v1/lookup?purl=…&false_positive_budget=25
POST /v1/analyze?url=…&false_positive_budget=250
| Value | Behavior |
|---|---|
| 0 | Block only when fires_at is 0. |
| 25 | Block when fires_at is 0–25. Default. |
| 250 | Recommended upper end; block when fires_at is 0–250. |
Use a whole number from 0 to 3000. We recommend 1 to 250. The response field fires_at is the tightest budget at which the artifact is hostile. Severity is hostile at or below the requested budget, suspicious above it through 3000, and benign above 3000 or at -1.
Following references New
?follow= controls what Beamline retrieves from inside the requested artifact. The artifact itself is always analyzed.
| Value | Retrieves | Default for |
|---|---|---|
| none | Nothing else. | URL |
| dependencies | Dependencies in manifests and lockfiles. | — |
| references | Packages and URLs in install or download commands. | PURL, SHA-256 |
| ci-actions | Third-party CI actions and their dependencies. | — |
| all | Everything above. | Upload |
curl -sN -X POST \
"https://api.isotope13.ai/v1/analyze?purl=pkg%3Anpm%2Faxios%401.19.0&follow=all"
Omit ?follow= to use the default above.
Response
{
"status": "analyzed",
"purl": "pkg:npm/axios@1.19.0",
"sha256": "a511049fdaec40a320368b3ee965079b3e14481f82d052584f746bbdc3f01ede",
"severity": "benign",
"fires_at": -1,
"findings": [],
"engine_version": "2.8.0",
"analyzed_at": "2026-08-23T11:48:00Z"
}
status describes whether Beamline has an assessment; severity describes that assessment. A response is factual and does not choose whether your system should proceed. Fields with no value are omitted to save bandwidth:
status: "analyzed"meansseveritywas derived fromfires_atand the requested budget.status: "unanalyzed"means nobody has analyzed the artifact; severity isunknown.status: "unavailable"means Beamline could not answer; severity isunknown.causesays which outage it was:saturatedandmixedare worth retrying with backoff,unreachableandno_workersare not.
For streaming analysis, the terminal line contains status, or ml and raw when full=1. Other lines report progress. Retry if the stream ends without a terminal line.
A {"state":"resumed"} line means the scan worker was lost mid-run and Beamline moved the run to another one. It is progress, not an answer: keep reading. Phases restart after it; elapsed times do not go backwards.
Use cases
CI systems
Use POST /v1/analyze when a job needs a fresh assessment. Name registry dependencies with purl. Upload local build outputs.
Wait for the terminal line containing status, then apply your policy to severity and fires_at:
| Status | CI action |
|---|---|
| analyzed | Compare fires_at with your budget. |
| unanalyzed | Apply your unknown-artifact policy. |
| unavailable | Retry with backoff when cause is saturated or mixed; otherwise apply your outage policy. |
A stream that ends before a status is not an answer. Retry it. Keep the returned sha256 with the build record.
Transparent proxy integration
Keep proxy integrations narrow:
- Use
?url=. Send the exact URL already resolved by the proxy. - Choose
?follow=. Use?follow=nonewhen the proxy sees every download. If it sees only a subset of commands, such as curl or npm, consider?follow=references. Beamline will also analyze packages and URLs named by install or download commands, which can catch a package that downloads malware later. - Whitelist domains. For privacy reasons, never forward arbitrary user-supplied URLs or URLs containing credentials or private query data.
curl -sN -X POST --get \
--data-urlencode "url=${EXACT_URL}" \
--data "follow=none" \
"https://api.isotope13.ai/v1/analyze"
Keep the returned sha256 and cache the result too.
Errors
Errors are JSON with a stable error.code and a human-readable message.
{
"error": {
"code": "invalid_url",
"message": "url must be an absolute http or https URL."
}
}
400 means the request is invalid. 401 means a bearer token is required or invalid. 413 means too many packages or an oversized upload. 429 means capacity is temporarily full; retry with backoff. If Beamline cannot answer about an artifact, it returns 200 with status: "unavailable".