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.
https://hullwise.co/api/v1Scan
Eine öffentliche Website oder App von außen prüfen.
Website prüfen
/api/v1/scanDer 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.
| Parameter | Beschreibung |
|---|---|
url Pflichtquery (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. |
curl "https://hullwise.co/api/v1/scan?url=example.com" \
-H "Authorization: Bearer $SEALCHECK_API_KEY"curl -X POST "https://hullwise.co/api/v1/scan" \
-H "Authorization: Bearer $SEALCHECK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"example.com"}'{
"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.
| Feld | Typ | Bedeutung |
|---|---|---|
url | string | Der Hostname, der nach allen Weiterleitungen geprüft wurde. Nur der Name, ohne Schema und Pfad. |
scannedAt | string | Zeitpunkt des Checks, ISO 8601 in UTC. |
https | boolean | Ob die Seite am Ende über HTTPS ausgeliefert wurde. |
score | number | Punkte 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). |
grade | string | Note 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. |
platforms | string[] | Erkannte Plattformen und Hoster, etwa Lovable, Bolt, v0, Replit, Base44, Vercel, Netlify, Next.js, Vite, Supabase oder Firebase. Leer, wenn nichts eindeutig erkennbar ist. |
scripts | number | Wie viele JavaScript-Dateien der Seite geladen und durchsucht wurden, höchstens 10. |
tls | object | null | Zertifikat: issuer, validTo, daysLeft, protocol und valid. null, wenn die Seite ohne HTTPS läuft. |
email | object | E-Mail-Schutz der Domain: domain, spf (true oder false) und dmarc (die Richtlinie wie reject, quarantine, none, oder null). |
findings | object | Befunde, sortiert in die Felder https, tls, headers, versions, email, exposure, cookies, privacy (Datenschutz) und dns. Jedes Feld ist eine Liste, oft leer. |
findings.*[].id | string | Feste ID des Prüfpunkts. Alle IDs stehen unter Prüfpunkte. |
findings.*[].sev | string | warn oder info. |
findings.*[].label | string | Kurzer Titel für Menschen. Leer bei gesperrten Befunden. |
findings.*[].why | string | Ein bis zwei Sätze dazu, was der Befund bedeutet. |
findings.*[].refs | string[] | Nur bei Versionen: CVE- oder OSV-Kennungen. |
findings.*[].source | string | Nur bei Versionen: Quelle des Treffers. |
findings.*[].critical | boolean | Nur bei offen liegenden geheimen Schlüsseln in den JavaScript-Dateien: true. |
findings.*[].locked | boolean | Nur im gekürzten Bericht: true bei gesperrten Befunden. |
locked | object | Nur im gekürzten Bericht: hidden (Zahl der gesperrten Befunde) und total (Zahl aller Befunde). |
disclaimer | string | Was 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 Reihenfolgeexposure,versions,https,tls,headers,privacy,email,cookies,dns. - Alle übrigen Befunde bleiben an ihrer Stelle, enthalten aber nur
sev, ein leereslabelundlocked: true.id,why,refsundsourcefehlen. - In
tech[]steht bei jeder Technikversion: null. - Das Feld
lockednennt mithiddendie gesperrten und mittotalalle 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.
{
"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
/api/scanDieser 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.
curl "https://hullwise.co/api/scan?url=example.com"