API-Referenz

Lesen Sie Punktzahlen, Befunde und Änderungen Ihrer Domains und starten Sie Audits, aus Ihren Skripten, Ihrer CI, Ihren Automatisierungswerkzeugen und Ihren KI-Assistenten. Ab dem Solo-Tarif.

Version 1.0.0 · OpenAPI-3.1-Spezifikation (JSON)

Erste Schritte

Die API antwortet unter https://app.perimetry.cloud. Erstellen Sie einen Schlüssel unter Einstellungen › API-Schlüssel (Inhaber und Administratoren) und senden Sie ihn bei jeder Anfrage im Header Authorization, nie in der Adresse:

Authorization: Bearer pc_…

Ein Schlüssel, der nur liest, genügt für alles außer dem Starten von Audits, das einen Schlüssel mit der Berechtigung audit braucht. Schlüssel erstellen und widerrufen: API-Schlüssel.

Einen Schlüssel prüfen:

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" https://app.perimetry.cloud/api/v1/me

Domains per ID oder per Name

Wo ein Pfad {id} nimmt, geben Sie die ID der Domain oder ihren Namen an (example.com). Eine Domain eines anderen Arbeitsbereichs antwortet 404, wie eine fehlende.

Versionen

Jede Antwort trägt Perimetry-Api-Version: 1. Version 1 gewinnt nur Felder hinzu: Nichts wird umbenannt oder entfernt. Ignorieren Sie Felder, die Sie nicht kennen.

Die Spezifikation

Dieselbe Beschreibung in OpenAPI 3.1, für Postman, Insomnia, Code-Generatoren und Automatisierungswerkzeuge. Öffentlich, ohne Schlüssel: https://app.perimetry.cloud/api/v1/openapi.json. Diese Seite wird daraus erzeugt.

Ohne Code

Konnektoren für n8n, Zapier und Make werden auf Grundlage dieser API vorbereitet. Bis zu ihrer Veröffentlichung funktioniert das HTTP-Request-Modul jedes Werkzeugs mit den Beispielen unten; um benachrichtigt zu werden, sobald ein Audit fertig ist, fügen Sie die Webhook-Adresse Ihres Szenarios auf der Seite Benachrichtigungen als Webhook-Kanal hinzu: Sie erhält die Zusammenfassung jedes Audits als JSON.

Limits

Aufrufe werden für den ganzen Arbeitsbereich gezählt, pro Uhrstunde:

TarifAufrufe pro Stunde
Solo300
Agency1.200
Agency Plus3.600

Jede authentifizierte Antwort trägt X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset (Unix-Zeit der nächsten Stunde). Über dem Limit antwortet die API 429 mit Retry-After. Nach sechzig Anfragen mit falschem Schlüssel in fünfzehn Minuten aus einem Netz werden die folgenden abgewiesen: Warten Sie fünfzehn Minuten.

Audits auf Anfrage folgen der Regel der Schaltfläche Jetzt auditieren (eines pro Stunde und Domain mit Solo) und über die API einem alle 15 Minuten pro Domain.

Fehler

Fehler antworten in JSON mit einem Satz (auf Englisch) und einem stabilen Code:

{"error": "invalid API key", "code": "unauthorized"}
StatusCodeBedeutung
400invalid_parameterEin Parameter hat einen Wert, den die API nicht annimmt; die Meldung nennt die zulässigen Werte.
401unauthorizedDer Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt.
403plan_excludes_apiDer Tarif des Arbeitsbereichs umfasst die API nicht.
403insufficient_scopeDer Schlüssel kann keine Audits starten: Erstellen Sie einen mit der Berechtigung audit.
404not_foundNicht in diesem Arbeitsbereich (eine Domain eines anderen Arbeitsbereichs antwortet genauso).
404no_auditDie Domain hat noch kein Audit.
409not_verifiedEin Audit wurde für eine nicht nachgewiesene Domain angefordert.
429rate_limitedDas Stundenlimit ist erreicht: Warten Sie Retry-After Sekunden.
429too_many_failuresZu viele Anfragen mit falschem Schlüssel aus Ihrem Netz: fünfzehn Minuten Wartezeit.
429audit_too_soonEin Audit auf Anfrage dieser Domain lief vor zu kurzer Zeit: Warten Sie Retry-After Sekunden.
500internalEin Problem auf unserer Seite.
503unavailableDas Audit konnte nicht eingereiht werden: erneut versuchen.
507storage_quotaDer Arbeitsbereich hat den Speicherplatz seines Tarifs aufgebraucht: Löschen Sie alte Daten oder wechseln Sie zu einem größeren Tarif.
507storage_fullUnser Speicher ist derzeit voll: Bitte versuchen Sie es später erneut.

Konto

Der Schlüssel und sein Arbeitsbereich.

GET /api/v1/me

Der Schlüssel, sein Arbeitsbereich und sein Stundenlimit

Arbeitsbereich und Tarif des Schlüssels, was er darf (seine Berechtigungen) und was vom Stundenlimit übrig ist. Ein guter erster Aufruf, um einen Schlüssel zu prüfen.

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/me"

Antwort 200

{
  "apiVersion": 1,
  "workspace": {
    "name": "Acme",
    "plan": "agency"
  },
  "key": {
    "name": "GitHub Actions",
    "prefix": "pc_3kQx",
    "scopes": [
      "read",
      "audit"
    ]
  },
  "rateLimit": {
    "limit": 1200,
    "remaining": 1187,
    "resetAt": "2026-11-12T10:00:00.000Z"
  }
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
workspaceobject
workspace.namestring | nullName of the workspace.
workspace.planstringPlan of the workspace: solo, agency, agency-plus.
keyobject
key.namestringName given to the key.
key.prefixstringFirst characters of the key.
key.scopesread | audit[]read: read results; audit: also start audits.
rateLimitobject
rateLimit.limitintegerCalls allowed per clock hour, for the whole workspace.
rateLimit.remainingintegerCalls left this hour (this one counted).
rateLimit.resetAtstring (date-time)When the next hour starts.

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

Domains

Die Domains des Arbeitsbereichs.

GET /api/v1/domains

Domains auflisten

Jede Domain des Arbeitsbereichs mit Status, Kunde, Punktzahl, Note, offenen Befunden nach Schweregrad, den Daten des letzten und des nächsten Audits und einem Link zu ihrer Seite.

Parameter

NameOrtTypPflichtBeschreibung
nameQuerystringneinNur die Domain mit diesem Namen behalten (example.com).

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains"

Antwort 200

{
  "apiVersion": 1,
  "domains": [
    {
      "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
      "name": "example.com",
      "status": "verified",
      "verified": true,
      "client": null,
      "score": 72,
      "grade": "C",
      "counts": {
        "critical": 0,
        "high": 2,
        "medium": 3,
        "low": 4,
        "info": 6
      },
      "lastAuditAt": "2026-11-12T03:14:07.000Z",
      "nextAuditAt": "2026-11-19T03:14:07.000Z",
      "attention": true,
      "links": {
        "app": "https://app.perimetry.cloud/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
        "api": "/api/v1/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10"
      }
    }
  ]
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainsobject[]
domains[].idstring (uuid)Id of the domain.
domains[].namestringName of the domain, such as example.com.
domains[].statuspending | verified | lost | pausedpending: ownership not proved yet; verified: proved and audited; lost: the proof disappeared; paused: audits stopped.
domains[].verifiedbooleanTrue when the status is verified.
domains[].clientstring | nullClient the domain belongs to (agency plans), or null.
domains[].scoreinteger | nullScore of the last audit, out of 100; null before the first audit.
domains[].gradestring | nullGrade of the last audit, A to F.
domains[].counts{ severity: integer } | nullOpen findings of the last audit by severity.
domains[].lastAuditAtstring (date-time) | nullWhen the last audit started.
domains[].nextAuditAtstring (date-time) | nullWhen the next scheduled audit is due.
domains[].attentionboolean | nullTrue when the last audit found something new and serious.
domains[].linksobject
domains[].links.appstring (uri)The domain’s page in the application.
domains[].links.apistringThis domain in the API.

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

GET /api/v1/domains/{id}

Eine Domain und ihr letztes Audit

Die Domain, ihr letztes Audit mit der Veränderung der Punktzahl seit dem vorigen, die Zahl neuer und behobener Befunde und die drei Befunde, die zuerst zu beheben sind.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).
langQueryen | fr | deneinSprache der Titel, Erklärungen und Behebungen der Befunde (Groß- oder Kleinschreibung egal). Jeder andere Wert ergibt Englisch. (Standard en)

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com"

Antwort 200

{
  "apiVersion": 1,
  "domain": {
    "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
    "name": "example.com",
    "status": "verified",
    "verified": true,
    "client": null,
    "score": 72,
    "grade": "C",
    "counts": {
      "critical": 0,
      "high": 2,
      "medium": 3,
      "low": 4,
      "info": 6
    },
    "lastAuditAt": "2026-11-12T03:14:07.000Z",
    "nextAuditAt": "2026-11-19T03:14:07.000Z",
    "attention": true,
    "links": {
      "app": "https://app.perimetry.cloud/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
      "api": "/api/v1/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10"
    }
  },
  "lastAudit": {
    "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
    "startedAt": "2026-11-12T03:14:07.000Z",
    "finishedAt": "2026-11-12T03:16:52.000Z",
    "trigger": "schedule",
    "scannerVersion": "0.13.1",
    "score": 72,
    "grade": "C",
    "counts": {
      "critical": 0,
      "high": 2,
      "medium": 3,
      "low": 4,
      "info": 6
    },
    "attention": true,
    "previous": {
      "id": "c4e7a1b9-2d5f-4a8c-9e3b-6f1a2d4c8e57",
      "startedAt": "2026-11-05T03:14:07.000Z",
      "score": 80,
      "grade": "B"
    },
    "scoreDelta": -8,
    "newFindings": 2,
    "fixedFindings": 1
  },
  "doFirst": [
    {
      "fp": "5d0e3b7c1a9f4e26",
      "id": "email.dmarc.p_none",
      "severity": "high",
      "title": "DMARC policy only monitors (p=none)",
      "target": "example.com"
    }
  ]
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainobject
domain.idstring (uuid)Id of the domain.
domain.namestringName of the domain, such as example.com.
domain.statuspending | verified | lost | pausedpending: ownership not proved yet; verified: proved and audited; lost: the proof disappeared; paused: audits stopped.
domain.verifiedbooleanTrue when the status is verified.
domain.clientstring | nullClient the domain belongs to (agency plans), or null.
domain.scoreinteger | nullScore of the last audit, out of 100; null before the first audit.
domain.gradestring | nullGrade of the last audit, A to F.
domain.counts{ severity: integer } | nullOpen findings of the last audit by severity.
domain.lastAuditAtstring (date-time) | nullWhen the last audit started.
domain.nextAuditAtstring (date-time) | nullWhen the next scheduled audit is due.
domain.attentionboolean | nullTrue when the last audit found something new and serious.
domain.linksobject
domain.links.appstring (uri)The domain’s page in the application.
domain.links.apistringThis domain in the API.
lastAuditobject | nullNull before the first audit.
lastAudit.idstring (uuid)
lastAudit.startedAtstring (date-time)
lastAudit.finishedAtstring (date-time) | null
lastAudit.triggerstringschedule or manual.
lastAudit.scannerVersionstringVersion of the scanner.
lastAudit.scoreinteger
lastAudit.gradestring
lastAudit.counts{ severity: integer }Open findings by severity (a severity with none may be left out).
lastAudit.attentionbooleanSomething new and serious.
lastAudit.previousobject | null
lastAudit.previous.idstring (uuid)Id of the audit.
lastAudit.previous.startedAtstring (date-time) | nullWhen the audit started.
lastAudit.previous.scoreintegerScore out of 100.
lastAudit.previous.gradestringGrade, A to F.
lastAudit.scoreDeltainteger | nullChange of score since the previous audit.
lastAudit.newFindingsinteger | nullFindings that were not in the previous audit.
lastAudit.fixedFindingsinteger | nullFindings of the previous audit that are gone.
doFirstobject[]The (at most) three findings to fix first.
doFirst[].fpstring | nullFingerprint: the same check on the same target keeps the same fingerprint from one audit to the next.
doFirst[].idstringId of the check, such as email.dmarc.missing.
doFirst[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
doFirst[].titlestringTitle, in the language asked for.
doFirst[].targetstringHost, address or record the finding is about.

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (Code not_found).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

Befunde

Was das neueste Audit gefunden hat.

GET /api/v1/domains/{id}/findings

Offene Befunde des neuesten Audits

Die offenen Befunde des neuesten Audits, die schwersten zuerst, jeweils mit Behebung, kopierfertigen DNS-Einträgen und den betroffenen Compliance-Anforderungen. Akzeptierte Befunde auf Wunsch.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).
min_severityQuerycritical | high | medium | low | infoneinBefunde dieses Schweregrads und schwerere behalten (Groß- oder Kleinschreibung egal). Jeder andere Wert ergibt 400 invalid_parameter. Ohne ihn: alle. (Standard info)
include_acceptedQuery1 | truenein1 oder true: auch die akzeptierten Befunde zurückgeben, in accepted.
limitQueryintegerneinHöchstens so viele Befunde (und ebenso viele akzeptierte). (Standard 500)
langQueryen | fr | deneinSprache der Titel, Erklärungen und Behebungen der Befunde (Groß- oder Kleinschreibung egal). Jeder andere Wert ergibt Englisch. (Standard en)

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com/findings?min_severity=high&lang=fr"

Antwort 200

{
  "apiVersion": 1,
  "domain": {
    "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
    "name": "example.com"
  },
  "audit": {
    "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
    "startedAt": "2026-11-12T03:14:07.000Z",
    "score": 72,
    "grade": "C",
    "counts": {
      "critical": 0,
      "high": 2,
      "medium": 3,
      "low": 4,
      "info": 6
    }
  },
  "total": 1,
  "findings": [
    {
      "fp": "5d0e3b7c1a9f4e26",
      "id": "email.dmarc.p_none",
      "severity": "high",
      "category": "email",
      "target": "example.com",
      "title": "DMARC policy only monitors (p=none)",
      "detail": "Receivers are told to deliver mail that fails DMARC anyway: anyone can still send mail in the domain’s name.",
      "evidence": "v=DMARC1; p=none; rua=mailto:dmarc@example.com",
      "fix": "Move the policy to quarantine, then reject, once the reports show every sender is aligned.",
      "references": [
        "https://www.rfc-editor.org/rfc/rfc7489"
      ],
      "records": [
        {
          "type": "TXT",
          "name": "_dmarc.example.com",
          "value": "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com",
          "replaces": "v=DMARC1; p=none; rua=mailto:dmarc@example.com",
          "note": "One step stricter: mail that fails DMARC goes to spam."
        }
      ],
      "compliance": [
        "NIS2 Art. 21(2)(j)",
        "ISO 27001 A.8.21"
      ]
    }
  ]
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainobject
domain.idstring (uuid)Id of the domain.
domain.namestringName of the domain.
auditobject
audit.idstring (uuid)
audit.startedAtstring (date-time)
audit.scoreinteger
audit.gradestring
audit.counts{ severity: integer }Open findings by severity (a severity with none may be left out).
totalintegerOpen findings at or above min_severity, before limit.
findingsobject[]Open findings, worst first.
findings[].fpstring | nullFingerprint, stable from one audit to the next.
findings[].idstringId of the check.
findings[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
findings[].categorystringemail, web, dns, exposure…
findings[].targetstringHost, address or record the finding is about.
findings[].titlestringTitle, in the language asked for.
findings[].detailstring | nullWhy it matters.
findings[].evidencestring | nullWhat the audit saw.
findings[].fixstring | nullHow to fix it.
findings[].referencesstring (uri)[]Standards and documentation.
findings[].recordsobject[]DNS records ready to copy that fix it, when the audit allows (always empty for an accepted finding).
findings[].records[].typestringTXT, CAA, MX… or "file" for a file to serve (MTA-STS policy).
findings[].records[].namestringName of the record (or address of the file).
findings[].records[].valuestringValue to publish.
findings[].records[].replaces?string | nullThe current value this one replaces, when there is one.
findings[].records[].note?stringWhat to check before publishing.
findings[].compliancestring[]NIS2, Cyber Essentials and ISO 27001 requirements the finding relates to.
findings[].acceptedReason?string | nullAccepted findings only: why the risk was accepted.
findings[].acceptedUntil?string | nullAccepted findings only: until when, or null for no end date.
accepted?object[]Accepted findings (with include_accepted only), with the reason and end date.
accepted[].fpstring | nullFingerprint, stable from one audit to the next.
accepted[].idstringId of the check.
accepted[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
accepted[].categorystringemail, web, dns, exposure…
accepted[].targetstringHost, address or record the finding is about.
accepted[].titlestringTitle, in the language asked for.
accepted[].detailstring | nullWhy it matters.
accepted[].evidencestring | nullWhat the audit saw.
accepted[].fixstring | nullHow to fix it.
accepted[].referencesstring (uri)[]Standards and documentation.
accepted[].recordsobject[]DNS records ready to copy that fix it, when the audit allows (always empty for an accepted finding).
accepted[].records[].typestringTXT, CAA, MX… or "file" for a file to serve (MTA-STS policy).
accepted[].records[].namestringName of the record (or address of the file).
accepted[].records[].valuestringValue to publish.
accepted[].records[].replaces?string | nullThe current value this one replaces, when there is one.
accepted[].records[].note?stringWhat to check before publishing.
accepted[].compliancestring[]NIS2, Cyber Essentials and ISO 27001 requirements the finding relates to.
accepted[].acceptedReason?string | nullAccepted findings only: why the risk was accepted.
accepted[].acceptedUntil?string | nullAccepted findings only: until when, or null for no end date.

Fehler

  • 400 Ein Parameter hat einen Wert, den die API nicht annimmt (Code invalid_parameter); die Meldung nennt den Parameter und die zulässigen Werte.
  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (not_found), oder die Domain hat noch kein Audit (no_audit).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

Audits

Verlauf, Vergleich, neuestes Audit und Audits auf Anfrage.

GET /api/v1/domains/{id}/compare

Was sich seit dem vorigen Audit geändert hat

Punktzahl vorher und nachher, hinzugekommene, behobene oder im Schweregrad geänderte Befunde und die Änderungen der Infrastruktur: Hosts, Ports, Zertifikate, Lookalike-Domains, Cloud-Dienste.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).
langQueryen | fr | deneinSprache der Titel, Erklärungen und Behebungen der Befunde (Groß- oder Kleinschreibung egal). Jeder andere Wert ergibt Englisch. (Standard en)

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com/compare"

Antwort 200

{
  "apiVersion": 1,
  "domain": {
    "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
    "name": "example.com"
  },
  "audit": {
    "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
    "startedAt": "2026-11-12T03:14:07.000Z",
    "score": 72,
    "grade": "C"
  },
  "previous": {
    "id": "c4e7a1b9-2d5f-4a8c-9e3b-6f1a2d4c8e57",
    "startedAt": "2026-11-05T03:14:07.000Z",
    "score": 80,
    "grade": "B"
  },
  "comparison": {
    "score": {
      "from": 80,
      "to": 72,
      "delta": -8
    },
    "attention": true,
    "worstAdded": "high",
    "added": [
      {
        "fp": "5d0e3b7c1a9f4e26",
        "id": "email.dmarc.p_none",
        "severity": "high",
        "title": "DMARC policy only monitors (p=none)",
        "target": "example.com"
      }
    ],
    "resolved": [],
    "severityChanged": [],
    "infra": {
      "newHosts": [
        "staging.example.com"
      ],
      "goneHosts": [],
      "newLookalikes": [],
      "newCloud": [],
      "ports": [
        {
          "ip": "192.0.2.10",
          "opened": [
            8080
          ],
          "closed": []
        }
      ],
      "certificates": [],
      "newCertificates": [
        {
          "issuer": "Let's Encrypt",
          "names": [
            "staging.example.com"
          ],
          "notBefore": "2026-11-10T00:00:00.000Z"
        }
      ]
    }
  }
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainobject
domain.idstring (uuid)Id of the domain.
domain.namestringName of the domain.
auditobject
audit.idstring (uuid)
audit.startedAtstring (date-time)
audit.scoreinteger
audit.gradestring
previousobject | nullNull at the first audit.
previous.idstring (uuid) | null
previous.startedAtstring (date-time) | null
previous.scoreinteger
previous.gradestring | null
comparisonobject | nullNull at the first audit.
comparison.scoreobject
comparison.score.frominteger
comparison.score.tointeger
comparison.score.deltainteger
comparison.attentionbooleanSomething new and serious.
comparison.worstAddedstring | nullWorst severity among the new findings.
comparison.addedobject[]
comparison.added[].fpstring | nullFingerprint: the same check on the same target keeps the same fingerprint from one audit to the next.
comparison.added[].idstringId of the check, such as email.dmarc.missing.
comparison.added[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
comparison.added[].titlestringTitle, in the language asked for.
comparison.added[].targetstringHost, address or record the finding is about.
comparison.resolvedobject[]
comparison.resolved[].fpstring | nullFingerprint: the same check on the same target keeps the same fingerprint from one audit to the next.
comparison.resolved[].idstringId of the check, such as email.dmarc.missing.
comparison.resolved[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
comparison.resolved[].titlestringTitle, in the language asked for.
comparison.resolved[].targetstringHost, address or record the finding is about.
comparison.severityChangedobject[]
comparison.severityChanged[].fpstring | nullFingerprint: the same check on the same target keeps the same fingerprint from one audit to the next.
comparison.severityChanged[].idstringId of the check, such as email.dmarc.missing.
comparison.severityChanged[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
comparison.severityChanged[].titlestringTitle, in the language asked for.
comparison.severityChanged[].targetstringHost, address or record the finding is about.
comparison.severityChanged[].fromstringSeverity before.
comparison.severityChanged[].tostringSeverity now.
comparison.infraobject
comparison.infra.newHostsstring[]
comparison.infra.goneHostsstring[]
comparison.infra.newLookalikesstring[]
comparison.infra.newCloudstring[]
comparison.infra.portsobject[]
comparison.infra.ports[].ipstring
comparison.infra.ports[].openedinteger[]
comparison.infra.ports[].closedinteger[]
comparison.infra.certificatesobject[]Certificates that changed.
comparison.infra.certificates[].hoststring
comparison.infra.certificates[].fromstring
comparison.infra.certificates[].tostring
comparison.infra.newCertificatesobject[]New certificates seen in the transparency logs.
comparison.infra.newCertificates[].issuerstring
comparison.infra.newCertificates[].namesstring[]
comparison.infra.newCertificates[].notBeforestring

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (not_found), oder die Domain hat noch kein Audit (no_audit).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

GET /api/v1/domains/{id}/audits

Verlauf der Audits

Die Audits der Domain, das neueste zuerst: Datum, Auslöser, Punktzahl, Note, Befunde nach Schweregrad.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).
limitQueryintegerneinHöchstens so viele Audits. (Standard 30)

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com/audits?limit=10"

Antwort 200

{
  "apiVersion": 1,
  "domain": {
    "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
    "name": "example.com"
  },
  "audits": [
    {
      "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
      "startedAt": "2026-11-12T03:14:07.000Z",
      "trigger": "schedule",
      "score": 72,
      "grade": "C",
      "counts": {
        "high": 2,
        "medium": 3,
        "low": 4,
        "info": 6
      },
      "attention": true
    }
  ]
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainobject
domain.idstring (uuid)Id of the domain.
domain.namestringName of the domain.
auditsobject[]Newest first.
audits[].idstring (uuid)
audits[].startedAtstring (date-time)
audits[].triggerstring
audits[].scoreinteger
audits[].gradestring
audits[].counts{ severity: integer }Open findings by severity (a severity with none may be left out).
audits[].attentionboolean

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (Code not_found).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

GET /api/v1/domains/{id}/latest

Das neueste Audit vollständig oder als SARIF

Das neueste Audit, wie es gespeichert ist, mit seinen Befunden und den akzeptierten, auf Englisch; oder mit format=sarif ein SARIF-2.1.0-Dokument für Code-Scanning-Tools (als application/json ausgeliefert).

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).
formatQuerysarifneinsarif: als SARIF 2.1.0 antworten.

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com/latest?format=sarif"

Antwort 200

Das Audit (JSON) oder SARIF (application/sarif+json) mit format=sarif.

{
  "apiVersion": 1,
  "domain": "example.com",
  "audit": {
    "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
    "domain_id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
    "started_at": "2026-11-12T03:14:07.000Z",
    "finished_at": "2026-11-12T03:16:52.000Z",
    "duration_ms": 165000,
    "scanner_version": "0.13.1",
    "score": 72,
    "grade": "C",
    "counts": {
      "high": 2,
      "medium": 3,
      "low": 4,
      "info": 6
    },
    "snapshot": {},
    "errors": [],
    "diff": null,
    "attention": true,
    "trigger": "schedule",
    "created_at": "2026-11-12T03:16:52.000Z"
  },
  "findings": [
    {
      "fp": "5d0e3b7c1a9f4e26",
      "id": "email.dmarc.p_none",
      "severity": "high",
      "category": "email",
      "target": "example.com",
      "title": "DMARC policy only monitors (p=none)",
      "fix": "Move the policy to quarantine, then reject."
    }
  ],
  "accepted": []
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
domainstringName of the domain.
auditobjectThe audit as stored (its raw facts left out). Kept as it always was for existing scripts; further fields may appear.
audit.idstring (uuid)
audit.domain_idstring (uuid)
audit.started_atstring (date-time)
audit.finished_at?string (date-time) | null
audit.duration_ms?integer | null
audit.scanner_versionstring
audit.scoreinteger
audit.gradestring
audit.counts{ severity: integer }Open findings by severity (a severity with none may be left out).
audit.errors?object[]Checks that could not run.
audit.diff?object | nullChanges since the previous audit.
audit.attentionboolean
audit.triggerstring
audit.snapshot?objectSummary kept for comparisons.
audit.created_at?string (date-time)
findingsobject[]Open findings, worst first, in English.
findings[].fp?stringFingerprint.
findings[].idstringId of the check.
findings[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
findings[].categorystring
findings[].targetstring
findings[].titlestring
findings[].detail?string
findings[].evidence?string
findings[].fix?string
findings[].references?string[]
findings[].acceptedReason?string
findings[].acceptedUntil?string | null
acceptedobject[]Accepted findings.
accepted[].fp?stringFingerprint.
accepted[].idstringId of the check.
accepted[].severitycritical | high | medium | low | infoFrom the worst to the mildest.
accepted[].categorystring
accepted[].targetstring
accepted[].titlestring
accepted[].detail?string
accepted[].evidence?string
accepted[].fix?string
accepted[].references?string[]
accepted[].acceptedReason?string
accepted[].acceptedUntil?string | null

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (not_found), oder die Domain hat noch kein Audit (no_audit).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

POST /api/v1/domains/{id}/audit

Ein Audit starten

Startet ein Audit einer nachgewiesenen Domain; der Schlüssel braucht die Berechtigung audit. Antwortet 202 mit einem Lauf, der unter /api/v1/runs/{id} verfolgt wird. Wartet oder läuft bereits ein Audit der Domain, folgt der neue Lauf diesem (alreadyQueued). Dieselbe Grenze wie die Schaltfläche Jetzt auditieren (eines pro Stunde und Domain mit Solo) und über die API eines alle 15 Minuten pro Domain. Kein Anfragetext.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstringjaID der Domain oder ihr Name (example.com).

Beispiel

curl -X POST -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/domains/example.com/audit"

Antwort 202

Angenommen: Das Audit ist in der Warteschlange (oder war es schon).

{
  "apiVersion": 1,
  "queued": true,
  "alreadyQueued": false,
  "run": {
    "id": "6f1c9d2e-4b7a-4c3e-8f5d-1a2b3c4d5e6f",
    "status": "queued",
    "domain": {
      "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
      "name": "example.com"
    },
    "requestedAt": "2026-11-12T09:00:02.000Z",
    "progress": null,
    "audit": null,
    "previous": {
      "id": "c4e7a1b9-2d5f-4a8c-9e3b-6f1a2d4c8e57",
      "startedAt": "2026-11-05T03:14:07.000Z",
      "score": 72,
      "grade": "C"
    },
    "scoreDelta": null,
    "links": {
      "self": "/api/v1/runs/6f1c9d2e-4b7a-4c3e-8f5d-1a2b3c4d5e6f",
      "app": "https://app.perimetry.cloud/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10"
    }
  }
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
queuedtrue
alreadyQueuedbooleanTrue when an audit of the domain was already waiting or running: this run follows it.
runobject
run.idstring (uuid)Id of the run, to follow at /api/v1/runs/{id}.
run.statusqueued | running | done | failedqueued: waiting for a scanner; running: under way (see progress); done: the audit is in; failed: the audit did not finish.
run.domainobject
run.domain.idstring (uuid)Id of the domain.
run.domain.namestringName of the domain.
run.requestedAtstring (date-time)When the audit was asked for.
run.progressobject | nullWhile running only.
run.progress.percentnumber | nullPercentage done.
run.progress.stepstring | nullCurrent step.
run.error?stringFailed runs only: what happened.
run.auditobject | nullThe audit, once done.
run.audit.idstring (uuid)
run.audit.startedAtstring (date-time)
run.audit.finishedAtstring (date-time) | null
run.audit.triggerstringmanual for an audit started on demand.
run.audit.scoreinteger
run.audit.gradestring
run.audit.counts{ severity: integer }Open findings by severity (a severity with none may be left out).
run.audit.newFindingsinteger | nullFindings that were not in the previous audit.
run.audit.fixedFindingsinteger | nullFindings of the previous audit that are gone.
run.audit.worstAddedstring | nullWorst severity among the new findings.
run.previousobject | nullThe audit before this one.
run.previous.idstring (uuid)Id of the audit.
run.previous.startedAtstring (date-time) | nullWhen the audit started.
run.previous.scoreintegerScore out of 100.
run.previous.gradestringGrade, A to F.
run.scoreDeltainteger | nullChange of score since the previous audit.
run.linksobject
run.links.selfstringThis run in the API.
run.links.appstring (uri)The domain’s page in the application.

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif umfasst die API nicht (plan_excludes_api), oder der Schlüssel kann keine Audits starten (insufficient_scope).
  • 404 Nicht in diesem Arbeitsbereich (Code not_found).
  • 409 Die Domain ist nicht nachgewiesen (Code not_verified).
  • 429 Das Stundenlimit ist erreicht (rate_limited), zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures), oder ein Audit dieser Domain auf Anfrage lief vor zu kurzer Zeit (audit_too_soon). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).
  • 503 Das Audit konnte nicht eingereiht werden; erneut versuchen (Code unavailable).
  • 507 Der Arbeitsbereich hat den Speicherplatz seines Tarifs aufgebraucht (storage_quota), oder unser Speicher ist derzeit voll (storage_full): kein Audit auf Abruf, bis wieder Platz ist. Löschen Sie alte Daten oder wechseln Sie zu einem größeren Tarif; alles Gespeicherte bleibt lesbar.

GET /api/v1/runs/{id}

Stand eines über die API gestarteten Audits

queued (wartet), running (läuft, mit Fortschritt), done (fertig: neue Punktzahl, vorige Punktzahl, Veränderung) oder failed (fehlgeschlagen). Fragen Sie alle 10 bis 30 Sekunden ab; ein Audit dauert meist einige Minuten.

Parameter

NameOrtTypPflichtBeschreibung
idPfadstring (uuid)jaID des Laufs, aus POST /api/v1/domains/{id}/audit.

Beispiel

curl -H "Authorization: Bearer $PERIMETRY_API_KEY" \
  "https://app.perimetry.cloud/api/v1/runs/6f1c9d2e-4b7a-4c3e-8f5d-1a2b3c4d5e6f"

Antwort 200

{
  "apiVersion": 1,
  "run": {
    "id": "6f1c9d2e-4b7a-4c3e-8f5d-1a2b3c4d5e6f",
    "status": "done",
    "domain": {
      "id": "3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10",
      "name": "example.com"
    },
    "requestedAt": "2026-11-12T09:00:02.000Z",
    "progress": null,
    "audit": {
      "id": "8b1d5e2f-7c3a-4f6e-b2d9-0a4c6e8f1a23",
      "startedAt": "2026-11-12T09:00:05.000Z",
      "finishedAt": "2026-11-12T09:02:41.000Z",
      "trigger": "manual",
      "score": 78,
      "grade": "C",
      "counts": {
        "critical": 0,
        "high": 1,
        "medium": 3,
        "low": 4,
        "info": 6
      },
      "newFindings": 0,
      "fixedFindings": 1,
      "worstAdded": null
    },
    "previous": {
      "id": "c4e7a1b9-2d5f-4a8c-9e3b-6f1a2d4c8e57",
      "startedAt": "2026-11-05T03:14:07.000Z",
      "score": 72,
      "grade": "C"
    },
    "scoreDelta": 6,
    "links": {
      "self": "/api/v1/runs/6f1c9d2e-4b7a-4c3e-8f5d-1a2b3c4d5e6f",
      "app": "https://app.perimetry.cloud/domains/3f9c2a64-5b1e-4c0a-9a57-2f6d8e1b4c10"
    }
  }
}
Felder der Antwort

Beschreibungen der Felder auf Englisch, wie in der Spezifikation.

NameTypBeschreibung
apiVersion1
runobject
run.idstring (uuid)Id of the run, to follow at /api/v1/runs/{id}.
run.statusqueued | running | done | failedqueued: waiting for a scanner; running: under way (see progress); done: the audit is in; failed: the audit did not finish.
run.domainobject
run.domain.idstring (uuid)Id of the domain.
run.domain.namestringName of the domain.
run.requestedAtstring (date-time)When the audit was asked for.
run.progressobject | nullWhile running only.
run.progress.percentnumber | nullPercentage done.
run.progress.stepstring | nullCurrent step.
run.error?stringFailed runs only: what happened.
run.auditobject | nullThe audit, once done.
run.audit.idstring (uuid)
run.audit.startedAtstring (date-time)
run.audit.finishedAtstring (date-time) | null
run.audit.triggerstringmanual for an audit started on demand.
run.audit.scoreinteger
run.audit.gradestring
run.audit.counts{ severity: integer }Open findings by severity (a severity with none may be left out).
run.audit.newFindingsinteger | nullFindings that were not in the previous audit.
run.audit.fixedFindingsinteger | nullFindings of the previous audit that are gone.
run.audit.worstAddedstring | nullWorst severity among the new findings.
run.previousobject | nullThe audit before this one.
run.previous.idstring (uuid)Id of the audit.
run.previous.startedAtstring (date-time) | nullWhen the audit started.
run.previous.scoreintegerScore out of 100.
run.previous.gradestringGrade, A to F.
run.scoreDeltainteger | nullChange of score since the previous audit.
run.linksobject
run.links.selfstringThis run in the API.
run.links.appstring (uri)The domain’s page in the application.

Fehler

  • 401 Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt (Code unauthorized).
  • 403 Der Tarif des Arbeitsbereichs umfasst die API nicht (Code plan_excludes_api). X-RateLimit-Limit und X-RateLimit-Remaining sind 0.
  • 404 Nicht in diesem Arbeitsbereich (Code not_found).
  • 429 Das Stundenlimit ist erreicht (rate_limited), oder zu viele Anfragen kamen mit einem falschen Schlüssel (too_many_failures). Warten Sie Retry-After Sekunden.
  • 500 Ein Problem auf unserer Seite (Code internal).

Spezifikation

Dieses Dokument.

GET /api/v1/openapi.json

Diese Spezifikation

Die OpenAPI-3.1-Beschreibung der API. Öffentlich: ohne Schlüssel, zählt nicht gegen das Stundenlimit.

Kein Schlüssel nötig.

Beispiel

curl \
  "https://app.perimetry.cloud/api/v1/openapi.json"

Antwort 200

Das OpenAPI-Dokument.