Dokumentation

REST API

Token-basierter API-Zugriff für Scans, Domains und Account-Daten.

turbometrics bietet eine REST API für den programmatischen Zugriff auf Scans, Domains und Account-Daten. Authentifizierung erfolgt über Bearer Tokens, die du unter Profil → API verwaltest.

Authentifizierung

Füge deinen API-Token bei jedem Request als Authorization-Header hinzu:

Authorization: Bearer {dein-token}

Die API antwortet immer mit JSON. Ohne gültigen Token erhältst du 401 Unauthenticated.

Basis-URL

https://turbometrics.de/api/v1

Rate Limiting

Das tägliche Anfrage-Limit hängt von deinem Plan ab:

Plan Tägliches Limit
Starter 500
Pro 5.000
Agency Unbegrenzt

Bei Überschreitung erhältst du 429 Too Many Requests:

{
  "error": "Daily API limit reached",
  "limit": 500,
  "reset_at": "2026-04-01T23:59:59+02:00"
}

Der Zähler wird täglich um Mitternacht zurückgesetzt.


Endpunkte

GET /me

Gibt Account-Informationen und aktuellen API-Nutzungsstand zurück.

Beispiel:

curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/me

Antwort:

{
  "data": {
    "id": 42,
    "name": "Max Mustermann",
    "email": "[email protected]",
    "plan": {
      "key": "starter",
      "label": "Starter",
      "api_enabled": true,
      "api_daily_limit": 500
    },
    "api_usage": {
      "used_today": 12,
      "limit_today": 500,
      "reset_at": "2026-04-01T23:59:59+02:00"
    }
  }
}

GET /scans

Gibt eine paginierte Liste deiner Scans zurück.

Parameter:

Parameter Typ Beschreibung
limit int Ergebnisse pro Seite, max. 50 (Standard: 20)
domain string Filtert nach URL-Inhalt
status string queued, running, finished, failed
page int Seitennummer

Beispiel:

curl -H "Authorization: Bearer {token}" \
  "https://turbometrics.de/api/v1/scans?limit=10&status=finished"

Antwort:

{
  "data": [
    {
      "public_id": "01KN3X...",
      "status": "finished",
      "submitted_url": "https://example.com/",
      "region": "de-fsn1",
      "auth_type": null,
      "requested_at": "2026-03-31T00:29:00+02:00",
      "finished_at": "2026-03-31T00:29:45+02:00",
      "result": {
        "scores": {
          "overall": 87,
          "speed": 100,
          "images": 93,
          "caching": 89,
          "wordpress": 100,
          "technical": 70
        }
      }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 10,
    "total": 143,
    "last_page": 15
  }
}

POST /scans

Startet einen neuen Scan.

Body (JSON):

Feld Typ Pflicht Beschreibung
url string ja Die zu scannende URL
region string nein Scan-Standort (z.B. de-fsn1, de-nbg1)
public bool nein Öffentlich sichtbar (Standard: false)
force bool nein Frischen Scan erzwingen, auch wenn ein aktuelles Ergebnis vorhanden ist (Standard: false)
auth object nein Authentifizierung für geschützte Seiten (ab Pro-Plan, siehe unten)

Hinweis zum Caching: Ohne force: true wird ein vorhandenes Scan-Ergebnis der gleichen URL zurückgegeben, wenn es weniger als 24 Stunden alt ist. Die Antwort enthält dann "cached": true und HTTP 200 statt 202.

Beispiel (normaler Scan):

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}' \
  https://turbometrics.de/api/v1/scans

Beispiel (frischen Scan erzwingen):

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "force": true}' \
  https://turbometrics.de/api/v1/scans

Antwort — neuer Scan (202 Accepted):

{
  "data": {
    "id": "01KN3X...",
    "status": "queued",
    "url": "https://example.com/",
    "cached": false,
    "auth_type": null
  }
}

Antwort — Cache-Hit (200 OK):

{
  "data": {
    "id": "01KN3X...",
    "status": "finished",
    "url": "https://example.com/",
    "cached": true,
    "auth_type": null
  }
}

Der Scan wird asynchron verarbeitet. Rufe GET /scans/{id} ab, um den Status zu prüfen.

Scan mit Authentifizierung (ab Pro-Plan)

Seiten, die per HTTP Basic Auth oder eigenem HTTP-Header geschützt sind, können mit dem optionalen auth-Objekt gescannt werden. Verfügbar ab dem Pro-Plan.

Felder im auth-Objekt:

Feld Typ Beschreibung
type string "basic" oder "header"
username string Benutzername (nur bei type: "basic")
password string Passwort (nur bei type: "basic")
header_name string Name des HTTP-Headers (nur bei type: "header")
header_value string Wert des HTTP-Headers (nur bei type: "header")

Beispiel — HTTP Basic Auth:

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://staging.example.com",
    "auth": {
      "type": "basic",
      "username": "admin",
      "password": "geheim"
    }
  }' \
  https://turbometrics.de/api/v1/scans

Beispiel — HTTP-Header:

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://staging.example.com",
    "auth": {
      "type": "header",
      "header_name": "X-Preview-Key",
      "header_value": "abc123"
    }
  }' \
  https://turbometrics.de/api/v1/scans

Antwort mit Authentifizierung:

{
  "data": {
    "id": "01KN3X...",
    "status": "queued",
    "url": "https://staging.example.com/",
    "cached": false,
    "auth_type": "basic"
  }
}

Die Antwort enthält auth_type als Indikator — Zugangsdaten werden niemals zurückgegeben. Scans mit Authentifizierung werden automatisch als privat behandelt.

Ohne das scan_auth-Feature (Free / Starter) wird das auth-Objekt mit 403 Forbidden abgewiesen.


GET /scans/{id}

Gibt Details zu einem einzelnen Scan zurück.

{id} ist die public_id (z.B. 01KN3X...).

Beispiel:

curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/scans/01KN3X...

Antwort (laufender Scan):

{
  "data": {
    "public_id": "01KN3X...",
    "status": "running",
    "submitted_url": "https://example.com/",
    "region": "de-fsn1",
    "auth_type": null
  }
}

Antwort (abgeschlossener Scan):

{
  "data": {
    "public_id": "01KN3X...",
    "status": "finished",
    "is_public": false,
    "submitted_url": "https://example.com/",
    "region": "de-fsn1",
    "auth_type": null,
    "requested_at": "2026-03-31T00:29:00+02:00",
    "finished_at": "2026-03-31T00:29:45+02:00",
    "result": {
      "final_url": "https://example.com/",
      "final_host": "example.com",
      "http_status": 200,
      "scores": {
        "overall": 87,
        "speed": 100,
        "images": 93,
        "caching": 89,
        "wordpress": 100,
        "technical": 70
      },
      "summary_short": "Die Seite lädt schnell, hat aber Optimierungsbedarf bei technischen Metriken.",
      "summary_long": "...",
      "metrics": {
        "ttfb_ms": 182,
        "desktop": {
          "fcp_ms": 412,
          "lcp_ms": 830,
          "cls": 0.02,
          "tbt_ms": 0,
          "request_count": 34,
          "total_bytes": 512000
        },
        "mobile": {
          "fcp_ms": 980,
          "lcp_ms": 2100,
          "cls": 0.04,
          "tbt_ms": 120,
          "request_count": 34,
          "total_bytes": 512000
        }
      },
      "screenshots": {
        "desktop_url": "https://turbometrics.de/scan/01KN3X.../screenshot?profile=desktop",
        "mobile_url": "https://turbometrics.de/scan/01KN3X.../screenshot?profile=mobile"
      },
      "findings": [
        {
          "category": "images",
          "code": "unoptimized_images",
          "severity": "warning",
          "title": "Bilder nicht optimiert",
          "message": "3 Bilder könnten kleiner sein.",
          "recommendation": "Nutze WebP und komprimiere Bilder vor dem Upload."
        }
      ]
    }
  }
}

GET /domains

Gibt eine paginierte Liste deiner überwachten Scan-Ziele zurück.

Beispiel:

curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/domains

Antwort:

{
  "data": [
    {
      "id": 7,
      "host": "example.com",
      "url": "https://example.com/",
      "schedule": "daily",
      "is_active": true,
      "last_dispatched_at": "2026-03-31T08:00:00+02:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 5,
    "last_page": 1
  }
}

GET /domains/{id}/history

Gibt die letzten 30 abgeschlossenen Scans für ein Scan-Ziel zurück.

{id} ist die numerische ID aus GET /domains.

Beispiel:

curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/domains/7/history

Antwort:

{
  "data": [
    {
      "score": 87,
      "created_at": "2026-03-31T08:01:23+02:00",
      "region": "de-fsn1"
    },
    {
      "score": 84,
      "created_at": "2026-03-30T08:00:55+02:00",
      "region": "de-fsn1"
    }
  ]
}

GET /alerts

Gibt eine paginierte Liste deiner Alerts zurück.

Parameter:

Parameter Typ Beschreibung
status string open (offen, nicht ausgeblendet), resolved, unread
severity string critical, warning
limit int Ergebnisse pro Seite, max. 50 (Standard: 20)
page int Seitennummer

Beispiel:

curl -H "Authorization: Bearer {token}" \
  "https://turbometrics.de/api/v1/alerts?status=open&severity=critical"

Antwort:

{
  "data": [
    {
      "id": 123,
      "type": "score_below_threshold",
      "severity": "critical",
      "title": "Score unter Schwelle",
      "message": "Score 34 unter Kritisch-Schwelle 60.",
      "is_read": false,
      "is_dismissed": false,
      "url": "https://example.com/",
      "host": "example.com",
      "scan_id": "01KN3X...",
      "created_at": "2026-03-31T08:01:23+02:00",
      "resolved_at": null
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 3,
    "last_page": 1
  }
}

GET /alerts/{id}

Gibt einen einzelnen Alert zurück.

Beispiel:

curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/alerts/123

Antwort: gleiche Struktur wie ein einzelnes Element aus GET /alerts.


POST /alerts/mark-read

Markiert Alerts als gelesen. Ohne ids werden alle ungelesenen Alerts des Nutzers markiert.

Body (JSON):

Feld Typ Pflicht Beschreibung
ids array nein Array von Alert-IDs; fehlt → alle

Beispiel (bestimmte Alerts):

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"ids": [123, 124]}' \
  https://turbometrics.de/api/v1/alerts/mark-read

Beispiel (alle als gelesen markieren):

curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://turbometrics.de/api/v1/alerts/mark-read

Antwort:

{
  "data": {
    "marked_read": 5
  }
}

Fehlercodes

Code Bedeutung
401 Kein oder ungültiger Token
403 Plan unterstützt keine API (Starter oder höher nötig) — oder auth-Objekt übergeben, aber kein Pro-Plan
404 Ressource nicht gefunden
422 Validierungsfehler (z.B. ungültige URL)
429 Stündliches Scan-Limit oder tägliches API-Limit überschritten
500 Interner Fehler

Beispiel 422:

{
  "message": "The url field is required.",
  "errors": {
    "url": ["The url field is required."]
  }
}

Code-Beispiele

curl

# Scan starten
curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "region": "de-fsn1"}' \
  https://turbometrics.de/api/v1/scans

# Scan mit HTTP Basic Auth (ab Pro-Plan)
curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://staging.example.com", "auth": {"type": "basic", "username": "admin", "password": "geheim"}}' \
  https://turbometrics.de/api/v1/scans

# Scan mit HTTP-Header (ab Pro-Plan)
curl -X POST \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://staging.example.com", "auth": {"type": "header", "header_name": "X-Preview-Key", "header_value": "abc123"}}' \
  https://turbometrics.de/api/v1/scans

# Status prüfen
curl -H "Authorization: Bearer {token}" \
  https://turbometrics.de/api/v1/scans/01KN3X...

# Letzte 5 Scans
curl -H "Authorization: Bearer {token}" \
  "https://turbometrics.de/api/v1/scans?limit=5&status=finished"

PHP

$token = 'dein-api-token';
$base  = 'https://turbometrics.de/api/v1';

// Scan starten
$ch = curl_init("$base/scans");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['url' => 'https://example.com']),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

$scanId = $response['data']['id']; // z.B. "01KN3X..."

// Scan mit HTTP Basic Auth starten (ab Pro-Plan)
$ch = curl_init("$base/scans");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'url'  => 'https://staging.example.com',
        'auth' => [
            'type'     => 'basic',
            'username' => 'admin',
            'password' => 'geheim',
        ],
    ]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

// auth_type in der Antwort zeigt "basic", Zugangsdaten werden nie zurückgegeben
echo $response['data']['auth_type']; // "basic"

// Status prüfen
$ch = curl_init("$base/scans/$scanId");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $token,
        'Accept: application/json',
    ],
]);
$scan = json_decode(curl_exec($ch), true);
curl_close($ch);

echo $scan['data']['status']; // queued | running | finished | failed

Token-Verwaltung

API-Token erstellst und verwaltest du unter Profil → API:

  • Gib jedem Token einen beschreibenden Namen (z.B. Monitoring-Script, CI/CD)
  • Setze ein Ablaufdatum für sicherheitskritische Umgebungen
  • Der Token-Wert wird nur einmal nach der Erstellung angezeigt
  • Kompromittierte Token sofort löschen und neu erstellen