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.
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/meDomains 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:
| Tarif | Aufrufe pro Stunde |
|---|---|
| Solo | 300 |
| Agency | 1.200 |
| Agency Plus | 3.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"}| Status | Code | Bedeutung |
|---|---|---|
| 400 | invalid_parameter | Ein Parameter hat einen Wert, den die API nicht annimmt; die Meldung nennt die zulässigen Werte. |
| 401 | unauthorized | Der Schlüssel fehlt, ist fehlerhaft, widerrufen oder unbekannt. |
| 403 | plan_excludes_api | Der Tarif des Arbeitsbereichs umfasst die API nicht. |
| 403 | insufficient_scope | Der Schlüssel kann keine Audits starten: Erstellen Sie einen mit der Berechtigung audit. |
| 404 | not_found | Nicht in diesem Arbeitsbereich (eine Domain eines anderen Arbeitsbereichs antwortet genauso). |
| 404 | no_audit | Die Domain hat noch kein Audit. |
| 409 | not_verified | Ein Audit wurde für eine nicht nachgewiesene Domain angefordert. |
| 429 | rate_limited | Das Stundenlimit ist erreicht: Warten Sie Retry-After Sekunden. |
| 429 | too_many_failures | Zu viele Anfragen mit falschem Schlüssel aus Ihrem Netz: fünfzehn Minuten Wartezeit. |
| 429 | audit_too_soon | Ein Audit auf Anfrage dieser Domain lief vor zu kurzer Zeit: Warten Sie Retry-After Sekunden. |
| 500 | internal | Ein Problem auf unserer Seite. |
| 503 | unavailable | Das Audit konnte nicht eingereiht werden: erneut versuchen. |
| 507 | storage_quota | Der Arbeitsbereich hat den Speicherplatz seines Tarifs aufgebraucht: Löschen Sie alte Daten oder wechseln Sie zu einem größeren Tarif. |
| 507 | storage_full | Unser 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
workspace | object | |
workspace.name | string | null | Name of the workspace. |
workspace.plan | string | Plan of the workspace: solo, agency, agency-plus. |
key | object | |
key.name | string | Name given to the key. |
key.prefix | string | First characters of the key. |
key.scopes | read | audit[] | read: read results; audit: also start audits. |
rateLimit | object | |
rateLimit.limit | integer | Calls allowed per clock hour, for the whole workspace. |
rateLimit.remaining | integer | Calls left this hour (this one counted). |
rateLimit.resetAt | string (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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
name | Query | string | nein | Nur 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domains | object[] | |
domains[].id | string (uuid) | Id of the domain. |
domains[].name | string | Name of the domain, such as example.com. |
domains[].status | pending | verified | lost | paused | pending: ownership not proved yet; verified: proved and audited; lost: the proof disappeared; paused: audits stopped. |
domains[].verified | boolean | True when the status is verified. |
domains[].client | string | null | Client the domain belongs to (agency plans), or null. |
domains[].score | integer | null | Score of the last audit, out of 100; null before the first audit. |
domains[].grade | string | null | Grade of the last audit, A to F. |
domains[].counts | { severity: integer } | null | Open findings of the last audit by severity. |
domains[].lastAuditAt | string (date-time) | null | When the last audit started. |
domains[].nextAuditAt | string (date-time) | null | When the next scheduled audit is due. |
domains[].attention | boolean | null | True when the last audit found something new and serious. |
domains[].links | object | |
domains[].links.app | string (uri) | The domain’s page in the application. |
domains[].links.api | string | This 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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID der Domain oder ihr Name (example.com). |
lang | Query | en | fr | de | nein | Sprache 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domain | object | |
domain.id | string (uuid) | Id of the domain. |
domain.name | string | Name of the domain, such as example.com. |
domain.status | pending | verified | lost | paused | pending: ownership not proved yet; verified: proved and audited; lost: the proof disappeared; paused: audits stopped. |
domain.verified | boolean | True when the status is verified. |
domain.client | string | null | Client the domain belongs to (agency plans), or null. |
domain.score | integer | null | Score of the last audit, out of 100; null before the first audit. |
domain.grade | string | null | Grade of the last audit, A to F. |
domain.counts | { severity: integer } | null | Open findings of the last audit by severity. |
domain.lastAuditAt | string (date-time) | null | When the last audit started. |
domain.nextAuditAt | string (date-time) | null | When the next scheduled audit is due. |
domain.attention | boolean | null | True when the last audit found something new and serious. |
domain.links | object | |
domain.links.app | string (uri) | The domain’s page in the application. |
domain.links.api | string | This domain in the API. |
lastAudit | object | null | Null before the first audit. |
lastAudit.id | string (uuid) | |
lastAudit.startedAt | string (date-time) | |
lastAudit.finishedAt | string (date-time) | null | |
lastAudit.trigger | string | schedule or manual. |
lastAudit.scannerVersion | string | Version of the scanner. |
lastAudit.score | integer | |
lastAudit.grade | string | |
lastAudit.counts | { severity: integer } | Open findings by severity (a severity with none may be left out). |
lastAudit.attention | boolean | Something new and serious. |
lastAudit.previous | object | null | |
lastAudit.previous.id | string (uuid) | Id of the audit. |
lastAudit.previous.startedAt | string (date-time) | null | When the audit started. |
lastAudit.previous.score | integer | Score out of 100. |
lastAudit.previous.grade | string | Grade, A to F. |
lastAudit.scoreDelta | integer | null | Change of score since the previous audit. |
lastAudit.newFindings | integer | null | Findings that were not in the previous audit. |
lastAudit.fixedFindings | integer | null | Findings of the previous audit that are gone. |
doFirst | object[] | The (at most) three findings to fix first. |
doFirst[].fp | string | null | Fingerprint: the same check on the same target keeps the same fingerprint from one audit to the next. |
doFirst[].id | string | Id of the check, such as email.dmarc.missing. |
doFirst[].severity | critical | high | medium | low | info | From the worst to the mildest. |
doFirst[].title | string | Title, in the language asked for. |
doFirst[].target | string | Host, 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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID der Domain oder ihr Name (example.com). |
min_severity | Query | critical | high | medium | low | info | nein | Befunde dieses Schweregrads und schwerere behalten (Groß- oder Kleinschreibung egal). Jeder andere Wert ergibt 400 invalid_parameter. Ohne ihn: alle. (Standard info) |
include_accepted | Query | 1 | true | nein | 1 oder true: auch die akzeptierten Befunde zurückgeben, in accepted. |
limit | Query | integer | nein | Höchstens so viele Befunde (und ebenso viele akzeptierte). (Standard 500) |
lang | Query | en | fr | de | nein | Sprache 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domain | object | |
domain.id | string (uuid) | Id of the domain. |
domain.name | string | Name of the domain. |
audit | object | |
audit.id | string (uuid) | |
audit.startedAt | string (date-time) | |
audit.score | integer | |
audit.grade | string | |
audit.counts | { severity: integer } | Open findings by severity (a severity with none may be left out). |
total | integer | Open findings at or above min_severity, before limit. |
findings | object[] | Open findings, worst first. |
findings[].fp | string | null | Fingerprint, stable from one audit to the next. |
findings[].id | string | Id of the check. |
findings[].severity | critical | high | medium | low | info | From the worst to the mildest. |
findings[].category | string | email, web, dns, exposure… |
findings[].target | string | Host, address or record the finding is about. |
findings[].title | string | Title, in the language asked for. |
findings[].detail | string | null | Why it matters. |
findings[].evidence | string | null | What the audit saw. |
findings[].fix | string | null | How to fix it. |
findings[].references | string (uri)[] | Standards and documentation. |
findings[].records | object[] | DNS records ready to copy that fix it, when the audit allows (always empty for an accepted finding). |
findings[].records[].type | string | TXT, CAA, MX… or "file" for a file to serve (MTA-STS policy). |
findings[].records[].name | string | Name of the record (or address of the file). |
findings[].records[].value | string | Value to publish. |
findings[].records[].replaces? | string | null | The current value this one replaces, when there is one. |
findings[].records[].note? | string | What to check before publishing. |
findings[].compliance | string[] | NIS2, Cyber Essentials and ISO 27001 requirements the finding relates to. |
findings[].acceptedReason? | string | null | Accepted findings only: why the risk was accepted. |
findings[].acceptedUntil? | string | null | Accepted 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[].fp | string | null | Fingerprint, stable from one audit to the next. |
accepted[].id | string | Id of the check. |
accepted[].severity | critical | high | medium | low | info | From the worst to the mildest. |
accepted[].category | string | email, web, dns, exposure… |
accepted[].target | string | Host, address or record the finding is about. |
accepted[].title | string | Title, in the language asked for. |
accepted[].detail | string | null | Why it matters. |
accepted[].evidence | string | null | What the audit saw. |
accepted[].fix | string | null | How to fix it. |
accepted[].references | string (uri)[] | Standards and documentation. |
accepted[].records | object[] | DNS records ready to copy that fix it, when the audit allows (always empty for an accepted finding). |
accepted[].records[].type | string | TXT, CAA, MX… or "file" for a file to serve (MTA-STS policy). |
accepted[].records[].name | string | Name of the record (or address of the file). |
accepted[].records[].value | string | Value to publish. |
accepted[].records[].replaces? | string | null | The current value this one replaces, when there is one. |
accepted[].records[].note? | string | What to check before publishing. |
accepted[].compliance | string[] | NIS2, Cyber Essentials and ISO 27001 requirements the finding relates to. |
accepted[].acceptedReason? | string | null | Accepted findings only: why the risk was accepted. |
accepted[].acceptedUntil? | string | null | Accepted 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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID der Domain oder ihr Name (example.com). |
lang | Query | en | fr | de | nein | Sprache 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domain | object | |
domain.id | string (uuid) | Id of the domain. |
domain.name | string | Name of the domain. |
audit | object | |
audit.id | string (uuid) | |
audit.startedAt | string (date-time) | |
audit.score | integer | |
audit.grade | string | |
previous | object | null | Null at the first audit. |
previous.id | string (uuid) | null | |
previous.startedAt | string (date-time) | null | |
previous.score | integer | |
previous.grade | string | null | |
comparison | object | null | Null at the first audit. |
comparison.score | object | |
comparison.score.from | integer | |
comparison.score.to | integer | |
comparison.score.delta | integer | |
comparison.attention | boolean | Something new and serious. |
comparison.worstAdded | string | null | Worst severity among the new findings. |
comparison.added | object[] | |
comparison.added[].fp | string | null | Fingerprint: the same check on the same target keeps the same fingerprint from one audit to the next. |
comparison.added[].id | string | Id of the check, such as email.dmarc.missing. |
comparison.added[].severity | critical | high | medium | low | info | From the worst to the mildest. |
comparison.added[].title | string | Title, in the language asked for. |
comparison.added[].target | string | Host, address or record the finding is about. |
comparison.resolved | object[] | |
comparison.resolved[].fp | string | null | Fingerprint: the same check on the same target keeps the same fingerprint from one audit to the next. |
comparison.resolved[].id | string | Id of the check, such as email.dmarc.missing. |
comparison.resolved[].severity | critical | high | medium | low | info | From the worst to the mildest. |
comparison.resolved[].title | string | Title, in the language asked for. |
comparison.resolved[].target | string | Host, address or record the finding is about. |
comparison.severityChanged | object[] | |
comparison.severityChanged[].fp | string | null | Fingerprint: the same check on the same target keeps the same fingerprint from one audit to the next. |
comparison.severityChanged[].id | string | Id of the check, such as email.dmarc.missing. |
comparison.severityChanged[].severity | critical | high | medium | low | info | From the worst to the mildest. |
comparison.severityChanged[].title | string | Title, in the language asked for. |
comparison.severityChanged[].target | string | Host, address or record the finding is about. |
comparison.severityChanged[].from | string | Severity before. |
comparison.severityChanged[].to | string | Severity now. |
comparison.infra | object | |
comparison.infra.newHosts | string[] | |
comparison.infra.goneHosts | string[] | |
comparison.infra.newLookalikes | string[] | |
comparison.infra.newCloud | string[] | |
comparison.infra.ports | object[] | |
comparison.infra.ports[].ip | string | |
comparison.infra.ports[].opened | integer[] | |
comparison.infra.ports[].closed | integer[] | |
comparison.infra.certificates | object[] | Certificates that changed. |
comparison.infra.certificates[].host | string | |
comparison.infra.certificates[].from | string | |
comparison.infra.certificates[].to | string | |
comparison.infra.newCertificates | object[] | New certificates seen in the transparency logs. |
comparison.infra.newCertificates[].issuer | string | |
comparison.infra.newCertificates[].names | string[] | |
comparison.infra.newCertificates[].notBefore | string |
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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID der Domain oder ihr Name (example.com). |
limit | Query | integer | nein | Hö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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domain | object | |
domain.id | string (uuid) | Id of the domain. |
domain.name | string | Name of the domain. |
audits | object[] | Newest first. |
audits[].id | string (uuid) | |
audits[].startedAt | string (date-time) | |
audits[].trigger | string | |
audits[].score | integer | |
audits[].grade | string | |
audits[].counts | { severity: integer } | Open findings by severity (a severity with none may be left out). |
audits[].attention | boolean |
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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID der Domain oder ihr Name (example.com). |
format | Query | sarif | nein | sarif: 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
domain | string | Name of the domain. |
audit | object | The audit as stored (its raw facts left out). Kept as it always was for existing scripts; further fields may appear. |
audit.id | string (uuid) | |
audit.domain_id | string (uuid) | |
audit.started_at | string (date-time) | |
audit.finished_at? | string (date-time) | null | |
audit.duration_ms? | integer | null | |
audit.scanner_version | string | |
audit.score | integer | |
audit.grade | string | |
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 | null | Changes since the previous audit. |
audit.attention | boolean | |
audit.trigger | string | |
audit.snapshot? | object | Summary kept for comparisons. |
audit.created_at? | string (date-time) | |
findings | object[] | Open findings, worst first, in English. |
findings[].fp? | string | Fingerprint. |
findings[].id | string | Id of the check. |
findings[].severity | critical | high | medium | low | info | From the worst to the mildest. |
findings[].category | string | |
findings[].target | string | |
findings[].title | string | |
findings[].detail? | string | |
findings[].evidence? | string | |
findings[].fix? | string | |
findings[].references? | string[] | |
findings[].acceptedReason? | string | |
findings[].acceptedUntil? | string | null | |
accepted | object[] | Accepted findings. |
accepted[].fp? | string | Fingerprint. |
accepted[].id | string | Id of the check. |
accepted[].severity | critical | high | medium | low | info | From the worst to the mildest. |
accepted[].category | string | |
accepted[].target | string | |
accepted[].title | string | |
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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string | ja | ID 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
queued | true | |
alreadyQueued | boolean | True when an audit of the domain was already waiting or running: this run follows it. |
run | object | |
run.id | string (uuid) | Id of the run, to follow at /api/v1/runs/{id}. |
run.status | queued | running | done | failed | queued: waiting for a scanner; running: under way (see progress); done: the audit is in; failed: the audit did not finish. |
run.domain | object | |
run.domain.id | string (uuid) | Id of the domain. |
run.domain.name | string | Name of the domain. |
run.requestedAt | string (date-time) | When the audit was asked for. |
run.progress | object | null | While running only. |
run.progress.percent | number | null | Percentage done. |
run.progress.step | string | null | Current step. |
run.error? | string | Failed runs only: what happened. |
run.audit | object | null | The audit, once done. |
run.audit.id | string (uuid) | |
run.audit.startedAt | string (date-time) | |
run.audit.finishedAt | string (date-time) | null | |
run.audit.trigger | string | manual for an audit started on demand. |
run.audit.score | integer | |
run.audit.grade | string | |
run.audit.counts | { severity: integer } | Open findings by severity (a severity with none may be left out). |
run.audit.newFindings | integer | null | Findings that were not in the previous audit. |
run.audit.fixedFindings | integer | null | Findings of the previous audit that are gone. |
run.audit.worstAdded | string | null | Worst severity among the new findings. |
run.previous | object | null | The audit before this one. |
run.previous.id | string (uuid) | Id of the audit. |
run.previous.startedAt | string (date-time) | null | When the audit started. |
run.previous.score | integer | Score out of 100. |
run.previous.grade | string | Grade, A to F. |
run.scoreDelta | integer | null | Change of score since the previous audit. |
run.links | object | |
run.links.self | string | This run in the API. |
run.links.app | string (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
| Name | Ort | Typ | Pflicht | Beschreibung |
|---|---|---|---|---|
id | Pfad | string (uuid) | ja | ID 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.
| Name | Typ | Beschreibung |
|---|---|---|
apiVersion | 1 | |
run | object | |
run.id | string (uuid) | Id of the run, to follow at /api/v1/runs/{id}. |
run.status | queued | running | done | failed | queued: waiting for a scanner; running: under way (see progress); done: the audit is in; failed: the audit did not finish. |
run.domain | object | |
run.domain.id | string (uuid) | Id of the domain. |
run.domain.name | string | Name of the domain. |
run.requestedAt | string (date-time) | When the audit was asked for. |
run.progress | object | null | While running only. |
run.progress.percent | number | null | Percentage done. |
run.progress.step | string | null | Current step. |
run.error? | string | Failed runs only: what happened. |
run.audit | object | null | The audit, once done. |
run.audit.id | string (uuid) | |
run.audit.startedAt | string (date-time) | |
run.audit.finishedAt | string (date-time) | null | |
run.audit.trigger | string | manual for an audit started on demand. |
run.audit.score | integer | |
run.audit.grade | string | |
run.audit.counts | { severity: integer } | Open findings by severity (a severity with none may be left out). |
run.audit.newFindings | integer | null | Findings that were not in the previous audit. |
run.audit.fixedFindings | integer | null | Findings of the previous audit that are gone. |
run.audit.worstAdded | string | null | Worst severity among the new findings. |
run.previous | object | null | The audit before this one. |
run.previous.id | string (uuid) | Id of the audit. |
run.previous.startedAt | string (date-time) | null | When the audit started. |
run.previous.score | integer | Score out of 100. |
run.previous.grade | string | Grade, A to F. |
run.scoreDelta | integer | null | Change of score since the previous audit. |
run.links | object | |
run.links.self | string | This run in the API. |
run.links.app | string (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.