API-Referenz

Alle Anfragen gehen an https://hullwise.co/api/v1. Antworten kommen als JSON, Fehler in der Form, die unter Fehler beschrieben ist. Den Check gibt es auch als MCP-Tool, er prüft dort genauso. Die maschinenlesbare Fassung liegt unter openapi.json.

Basis-URL
https://hullwise.co/api/v1

Scan

Eine öffentliche Website oder App von außen prüfen.

Website prüfen

GETPOST/api/v1/scan

Der Check ruft die Seite einmal per GET ab, folgt bis zu fünf Weiterleitungen und liest höchstens 800 KB HTML. Danach lädt er die JavaScript-Dateien, die die Seite selbst über <script src> oder modulepreload einbindet: nur von derselben Domain, höchstens 10 Dateien, je Datei 2 MB, zusammen 6 MB und 6 Sekunden. Darin sucht er nach Mustern geheimer Schlüssel. Dazu kommen ein TLS-Handschlag und DNS-Abfragen für SPF und DMARC. Bei einer Subdomain sucht er die Einträge bis hinauf zur Hauptdomain. Bei GET steht die Adresse im Query-String, bei POST im JSON-Body. Beides liefert dieselbe Antwort.

Interne und private Adressen lehnen wir ab, auch wenn eine Weiterleitung dorthin zeigt. Pfade, die die Seite nicht selbst einbindet, probieren wir nicht aus. Gefundene Schlüssel benutzen wir nie. Ihren Wert geben wir nur gekürzt zurück, etwa sk_live_…9f2A. Den vollständigen Wert geben wir nie zurück und speichern ihn nicht. Die geprüfte Seite hat 8 Sekunden Zeit zu antworten. Es gibt keinen Zwischenspeicher, jede Anfrage prüft neu.

MCP-Tool scan_websiteSchlüssel nötigNur lesenWiederholbarGreift nach außen
ParameterBeschreibung
url Pflicht
query (GET) · body (POST) · string
Domain oder volle Adresse, etwa example.com oder https://example.com/shop. Ohne http:// oder https:// nehmen wir https:// an. Andere Schemata ergeben bad_scheme.
Anfrage · GET
curl "https://hullwise.co/api/v1/scan?url=example.com" \
  -H "Authorization: Bearer $SEALCHECK_API_KEY"
Anfrage · POST
curl -X POST "https://hullwise.co/api/v1/scan" \
  -H "Authorization: Bearer $SEALCHECK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"example.com"}'
200 OK · Beispiel-Antwort mit Business, Texte verkürzt
{
  "data": {
    "url": "kunden.example.com",
    "scannedAt": "2026-10-25T09:14:03.512Z",
    "https": true,
    "score": 5,
    "grade": "F",
    "tech": [],
    "platforms": ["Lovable", "Vite", "Supabase"],
    "scripts": 3,
    "tls": {
      "issuer": "Let's Encrypt",
      "validTo": "2026-12-30T08:11:52.000Z",
      "daysLeft": 66,
      "protocol": "TLSv1.3",
      "valid": true
    },
    "email": { "domain": "example.com", "spf": true, "dmarc": null },
    "findings": {
      "https": [],
      "tls": [],
      "exposure": [
        {
          "id": "code.secret.openai",
          "sev": "warn",
          "critical": true,
          "label": "OpenAI-API-Schlüssel in den JavaScript-Dateien der App (1×)",
          "why": "Jeder Besucher kann diesen Schlüssel aus index-B7xQ2.js auslesen und auf Ihre Kosten nutzen. … Den vollen Wert zeigen wir nicht an, nur Anfang und Ende zum Zuordnen.",
          "found": [{ "where": "index-B7xQ2.js", "value": "sk-proj-…x9Qa" }]
        }
      ],
      "versions": [],
      "headers": [
        {
          "id": "header.csp.missing",
          "sev": "warn",
          "label": "Kein Schutz gegen eingeschleuste Skripte (CSP)",
          "why": "Gelangt fremder Code in Ihre Seite, führt der Browser ihn aus. …"
        },
        { "id": "header.frame.missing", "sev": "info", "label": "Fremde Seiten dürfen Ihre Seite einbetten", "why": "…" },
        { "id": "header.permissions.missing", "sev": "info", "label": "Kamera, Mikrofon und Standort nicht gesperrt", "why": "…" }
      ],
      "email": [
        { "id": "email.dmarc.missing", "sev": "warn", "label": "Keine Regel für gefälschte Mails (DMARC)", "why": "…" }
      ],
      "cookies": []
    },
    "disclaimer": "Check von außen, ohne Besitznachweis. …"
  }
}

Felder der Antwort

Der Bericht steht immer im Feld data. Die Befundlisten sind oft leer. Die Header X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset kommen bei jeder Antwort mit einem gültigen Schlüssel mit, siehe Authentifizierung.

FeldTypBedeutung
urlstringDer Hostname, der nach allen Weiterleitungen geprüft wurde. Nur der Name, ohne Schema und Pfad.
scannedAtstringZeitpunkt des Checks, ISO 8601 in UTC.
httpsbooleanOb die Seite am Ende über HTTPS ausgeliefert wurde.
scorenumberPunkte von 0 bis 100. Vorhandene Schutz-Header bringen Punkte, Befunde ziehen welche ab, ein offen liegender geheimer Schlüssel 40 Punkte je Art. Die ganze Regel steht unter Methode (/docs/method).
gradestringNote A bis F. A ab 90, B ab 75, C ab 60, D ab 40, E ab 20, sonst F. Ohne HTTPS immer F.
tech[]object[]Erkannte Technik: name, version (oder null) und source, also wo sie erkannt wurde. Im gekürzten Bericht ist version immer null.
platformsstring[]Erkannte Plattformen und Hoster, etwa Lovable, Bolt, v0, Replit, Base44, Vercel, Netlify, Next.js, Vite, Supabase oder Firebase. Leer, wenn nichts eindeutig erkennbar ist.
scriptsnumberWie viele JavaScript-Dateien der Seite geladen und durchsucht wurden, höchstens 10.
tlsobject | nullZertifikat: issuer, validTo, daysLeft, protocol und valid. null, wenn die Seite ohne HTTPS läuft.
emailobjectE-Mail-Schutz der Domain: domain, spf (true oder false) und dmarc (die Richtlinie wie reject, quarantine, none, oder null).
findingsobjectBefunde, sortiert in die Felder https, tls, headers, versions, email, exposure, cookies, privacy (Datenschutz) und dns. Jedes Feld ist eine Liste, oft leer.
findings.*[].idstringFeste ID des Prüfpunkts. Alle IDs stehen unter Prüfpunkte.
findings.*[].sevstringwarn oder info.
findings.*[].labelstringKurzer Titel für Menschen. Leer bei gesperrten Befunden.
findings.*[].whystringEin bis zwei Sätze dazu, was der Befund bedeutet.
findings.*[].refsstring[]Nur bei Versionen: CVE- oder OSV-Kennungen.
findings.*[].sourcestringNur bei Versionen: Quelle des Treffers.
findings.*[].criticalbooleanNur bei offen liegenden geheimen Schlüsseln in den JavaScript-Dateien: true.
findings.*[].lockedbooleanNur im gekürzten Bericht: true bei gesperrten Befunden.
lockedobjectNur im gekürzten Bericht: hidden (Zahl der gesperrten Befunde) und total (Zahl aller Befunde).
disclaimerstringWas der Check gelesen hat und was ein Befund bedeutet. Geben Sie ihn mit aus, wenn Sie das Ergebnis Menschen zeigen.

Gekürzte Berichte ohne Business

Ohne bezahlten Plan kürzt der Server den Bericht, bevor er ihn sendet. Das gilt für den kostenlosen Schlüssel und für den Test-Schlüssel sc_test_demo, beim MCP-Server auch ohne Schlüssel. Vollständig bleiben Note, Punkte, Zertifikat, E-Mail-Schutz, Plattformen und die Zahl aller Befunde.

  • Vollständig sind die ersten 2 Befunde. Offen liegende Schlüssel (critical: true) kommen zuerst, dann Warnungen, dann Hinweise, zuletzt DNS-Hygiene (CAA, DNSSEC, security.txt, MTA-STS). Bei gleicher Stufe gilt die Reihenfolge exposure, versions, https, tls, headers, privacy, email, cookies, dns.
  • Alle übrigen Befunde bleiben an ihrer Stelle, enthalten aber nur sev, ein leeres label und locked: true. id, why, refs und source fehlen.
  • In tech[] steht bei jeder Technik version: null.
  • Das Feld locked nennt mit hidden die gesperrten und mit total alle Befunde.

Mit einem Schlüssel aus einem Konto mit Business fehlt das Feld locked, und jeder Befund ist vollständig. Was nicht gesendet wird, lässt sich auch im Client nicht freilegen.

200 OK · gekürzter Bericht, Ausschnitt
{
  "data": {
    "url": "kunden.example.com",
    "score": 5,
    "grade": "F",
    "platforms": ["Lovable", "Vite", "Supabase"],
    "scripts": 3,
    "findings": {
      "exposure": [
        { "id": "code.secret.openai", "sev": "warn", "critical": true, "label": "OpenAI-API-Schlüssel …", "why": "…" }
      ],
      "versions": [],
      "https": [],
      "tls": [],
      "headers": [
        { "id": "header.csp.missing", "sev": "warn", "label": "Kein Schutz gegen eingeschleuste Skripte (CSP)", "why": "…" },
        { "sev": "info", "label": "", "locked": true },
        { "sev": "info", "label": "", "locked": true }
      ],
      "email": [
        { "sev": "warn", "label": "", "locked": true }
      ],
      "cookies": []
    },
    "locked": { "hidden": 3, "total": 5 }
  }
}

Mögliche Fehler

Ohne oder mit falschem Schlüssel kommt 401 unauthorized, über dem Limit 429 rate_limited. Dazu kommen Eingabe- und Abruffehler wie blocked_host, timeout oder tls_invalid. Die ganze Liste steht unter Fehler.

Startseite

Der Endpunkt, den das Formular auf der Startseite benutzt.

Website prüfen ohne Schlüssel

GET/api/scan

Dieser Endpunkt ist für den Browser gedacht. Er braucht keinen Schlüssel und zählt je IP-Adresse (20 pro Stunde) oder, wenn Sie angemeldet sind, je Konto. Der Parameter url ist derselbe wie oben. Den vollständigen Bericht erhalten nur angemeldete Konten mit Business, sonst ist er gekürzt wie oben.

Die Antwort ist der Bericht selbst, ohne die Hülle data. Fehler kommen als zwei einfache Felder, etwa { "error": "rate_limited", "message": "…" }. Rate-Limit-Header schickt dieser Endpunkt nicht. Für Skripte, CI und Agenten nehmen Sie bitte /api/v1/scan oder den MCP-Server.

Kein SchlüsselNur lesenGreift nach außen
Anfrage
curl "https://hullwise.co/api/scan?url=example.com"

Ist Ihre App
wasserdicht?

Hullwise findet die Lecks, bevor es andere tun: offene Schlüssel, Datenbank-Zugänge und veraltete Software. Zu jedem Befund gibt es eine Anleitung zur Behebung.

https://

Klicken Sie auf einen Befund, um ihn als behoben zu markieren.

Preise ansehenSo läuft ein Scan ab

Ship fast. Stay hullwise.