Skip to content

Developers

The Hardn REST API. Scan from CI.

Register sites, prove ownership, start scans and read findings as JSON. The same ownership rules and credits as the app.

Your first scan is free, no card needed. You prove you own the site before Hardn touches it.

Authentication

Create a key in the app under API keys (owners and admins). Send it as a bearer token. Keys start with hk_live_, belong to one organisation, and are shown only once.

Header
Authorization: Bearer hk_live_…
Base URLhttps://api.hardn.si/v1
Site idstgt_…
Scan idsasm_…
TimesISO-8601, UTC

Quick start

Four calls from a new site to its report.

  1. 1. Register your site

    bash
    curl -s https://api.hardn.si/v1/targets \  -H "Authorization: Bearer $HARDN_KEY" -H "Content-Type: application/json" \  -d '{"url": "https://app.example.com", "kind": "website"}'
  2. 2. Add the DNS record from instructions.dns_txt, then verify

    bash
    curl -s -X POST https://api.hardn.si/v1/targets/tgt_…/verify -H "Authorization: Bearer $HARDN_KEY"
  3. 3. Start a scan and poll it

    bash
    curl -s https://api.hardn.si/v1/scans -H "Authorization: Bearer $HARDN_KEY" -H "Content-Type: application/json" \  -d '{"target_id": "tgt_…", "scan_type": "public"}'curl -s https://api.hardn.si/v1/scans/asm_… -H "Authorization: Bearer $HARDN_KEY"
  4. 4. Read the report

    bash
    curl -s "https://api.hardn.si/v1/scans/asm_…/report?format=json" -H "Authorization: Bearer $HARDN_KEY"

Routes

Every route is scoped to the key's organisation.

REST API routes
RouteBody → result
POST/targets{"url": "https://app.example.com", "kind": "website"}201 — the site, with verification_token and instructions (DNS and file).
GET/targetsYour organisation's sites.
POST/targets/{id}/verify{"status", "verified", "detail"} — runs the DNS or file check now.
POST/targets/{id}/accounts{"label", "level", "tenant", "auth_type", "secret", "header_name"}201 — a deep-scan test account. Secrets are write-only.
POST/scans{"target_id": "tgt_…", "scan_type": "public"}201 — the scan, queued. 409 target_unverified, 402 insufficient_credits.
GET/scans/{id}Status, progress, and when completed: score, grade and counts.
GET/scans/{id}/findingsEvery finding with severity, evidence and the fix.
GET/scans/{id}/report?format=json|htmlThe full report and fix report.

Errors

Errors are JSON: {"detail": {"code": "…", "message": "…"}}.

Status and codeWhat it means
401 unauthenticatedMissing, wrong or revoked API key.
402 insufficient_creditsThe run isn't covered by the plan (Pro includes 500 API runs a month, Agency 3,000) and the organisation has no credits left.
402 plan_limitThe plan doesn't include this, for example another site. Upgrade in Billing.
404 not_foundThe id doesn't exist or belongs to another organisation.
409 target_unverifiedOwnership hasn't been proved, or the proof has gone.
422 validation_errorThe body is invalid; errors lists each field.
429 rate_limitedSlow down and retry after a moment.

Use it in CI

Store the key as a secret, start a scan after each deploy, and fail the job if the grade drops below what you accept.

Watch the walkthrough: add Hardn to your CI pipeline (1:14)

.github/workflows/hardn.yml (excerpt)
- name: Hardn scan  env: { HARDN_KEY: ${{ secrets.HARDN_KEY }} }  run: |    ID=$(curl -s https://api.hardn.si/v1/scans -H "Authorization: Bearer $HARDN_KEY" \      -H "Content-Type: application/json" \      -d '{"target_id": "tgt_…", "scan_type": "public"}' | jq -r .id)    while :; do      SCAN=$(curl -s https://api.hardn.si/v1/scans/$ID -H "Authorization: Bearer $HARDN_KEY")      case "$(echo "$SCAN" | jq -r .status)" in queued|running) sleep 15;; *) break;; esac    done    GRADE=$(echo "$SCAN" | jq -r .grade)    case "$GRADE" in A|B) ;; *) echo "Hardn grade $GRADE"; exit 1;; esac

Create a free account to get a key.

Command line (CLI)

The hardn command starts a scan of a verified site, waits for it, and exits 1 when it finds issues at or above the severity you choose. Python 3.9 or newer, no dependencies.

Watch the walkthrough: install the CLI and run a scan (1:02)

Install and run
pip install hardn-cli          # or: pipx install hardn-cliexport HARDN_API_KEY=hk_live_…hardn targets                              # your sites and their idshardn scan --target tgt_… --fail-on high   # exit 0 pass, 1 issues found, 2 errorhardn scan --target tgt_… --sarif hardn.sarif
.github/workflows/hardn.yml (excerpt)
- name: Hardn scan  env:    HARDN_API_KEY: ${{ secrets.HARDN_API_KEY }}  run: |    pip install hardn-cli    hardn scan --target tgt_… --fail-on high --sarif hardn.sarif

On PyPI. Without PyPI access, install the same wheel from hardn.si (checksums).

Runtime SDK

Middleware for your own app: it labels automated-agent traffic, serves honeytokens and screens prompt injection, then reports signals to the Runtime page. Fail-open, and client IPs are hashed before they leave your process.

Watch the walkthrough: add runtime protection to your app (1:15)

Install
# Python 3.9+pip install hardn-runtime# Node 18+npm install @hardn/runtime
Flask
import osfrom hardn_runtime import Telemetry, Honeytokens, score_agent, scan_prompttel = Telemetry(api_key=os.environ["HARDN_API_KEY"], source="web-1")honey = Honeytokens()@app.before_requestdef guard():    sig = score_agent(user_agent=request.headers.get("User-Agent"), path=request.path,                      accept=request.headers.get("Accept"))    if sig.is_agent:        tel.record("agent_detected", ip=request.remote_addr, path=request.path, score=sig.score)    if honey.is_decoy_path(request.path):        tel.record("honeytoken_hit", ip=request.remote_addr, path=request.path, score=100)        abort(404)
Express
const { scoreAgent, Honeytokens, scanPrompt, Telemetry } = require("@hardn/runtime");const tel = new Telemetry(process.env.HARDN_API_KEY, { source: "web-1" });app.use((req, res, next) => {  const sig = scoreAgent({ userAgent: req.get("user-agent"), path: req.path, accept: req.get("accept") });  if (sig.isAgent) tel.record("agent_detected", { ip: req.ip, path: req.path, score: sig.score });  if (Honeytokens.isDecoyPath(req.path)) {    tel.record("honeytoken_hit", { ip: req.ip, path: req.path, score: 100 });    return res.status(404).end();  }  next();});

Scan from your pipeline this week

Add your site, prove you own it, and Hardn runs 27 read-only checks. You get a grade, every issue it found and the steps to fix each one. Your first scan is free.