Webhooks API-Referenz
Nutzlastformat, Header, Signaturverifizierung, Wiederholungen und die vollständige Ereignisliste.
Anfrage
Jede Zustellung ist ein POST mit einem JSON-Körper und diesen Headern: Serpfull-Signature, Serpfull-Timestamp, Serpfull-Event-Id, Serpfull-Event-Type, Serpfull-Delivery-Attempt (1–8), Serpfull-Api-Version (2026-10-26) und User-Agent Serpfull-Webhooks/2.0.
Nutzlast
Felder: id (Ereignis-ID, dieselbe bei Wiederholungen und erneuten Zustellungen), type, api_version, created_at (ISO 8601, UTC), created (Unix-Sekunden), org_id, site (ID, Domain und Name der Website oder null für Organisationsevents) und data (ereignisspezifisch).
{
"id": "6f1c2b8e-3a4d-4e5f-9a1b-2c3d4e5f6a7b",
"type": "uptime.monitor.down",
"api_version": "2026-10-26",
"created_at": "2026-10-26T09:14:03.120Z",
"created": 1793006043,
"org_id": "0d9a…",
"site": { "id": "8b2e…", "domain": "example.co.uk", "name": "Example" },
"data": { "monitor_id": "…", "url": "https://example.co.uk/", "reason": "timeout" }
}Signatur
Serpfull-Signature ist t=TIMESTAMP,v1=SIGNATUR. SIGNATUR ist der hexadezimale HMAC-SHA256 des Strings TIMESTAMP + "." + roher Anfrage-Body, mit dem Signierungsgeheimnis Ihres Endpunkts verschlüsselt. Während einer Geheimnisrotation gibt es zwei v1-Werte; akzeptieren Sie die Anfrage, wenn einer davon übereinstimmt. Vergleichen Sie in konstanter Zeit und lehnen Sie Zeitstempel ab, die mehr als 5 Minuten von Ihrer Uhr abweichen, um Wiederholungen zu blockieren.
In Node.js verifizieren
import crypto from "node:crypto"
// Use the raw request body (a string or Buffer), not re-serialised JSON.
export function verifySerpfull(rawBody, header, secret, toleranceSeconds = 300) {
const parts = header.split(",").map((p) => p.trim().split("="))
const t = Number(parts.find(([k]) => k === "t")?.[1])
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest()
return parts.some(([k, v]) => k === "v1" && v?.length === expected.length * 2 &&
crypto.timingSafeEqual(Buffer.from(v, "hex"), expected))
}
// Express example
app.post("/webhooks/serpfull", express.raw({ type: "application/json" }), (req, res) => {
if (!verifySerpfull(req.body.toString("utf8"), req.get("serpfull-signature") ?? "", process.env.SERPFULL_WEBHOOK_SECRET)) {
return res.status(400).end()
}
const event = JSON.parse(req.body)
// Deduplicate on event.id (also sent as the Serpfull-Event-Id header), then handle event.type.
res.status(200).end()
})In Python verifizieren
import hashlib, hmac, time
def verify_serpfull(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = [p.strip().split("=", 1) for p in header.split(",") if "=" in p]
try:
t = int(next(v for k, v in parts if k == "t"))
except (StopIteration, ValueError):
return False
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return any(k == "v1" and hmac.compare_digest(v, expected) for k, v in parts)
# Flask example
@app.post("/webhooks/serpfull")
def serpfull_webhook():
if not verify_serpfull(request.get_data(), request.headers.get("Serpfull-Signature", ""), os.environ["SERPFULL_WEBHOOK_SECRET"]):
abort(400)
event = request.get_json()
# Deduplicate on event["id"], then handle event["type"].
return "", 200Antworten und Wiederholungen
- 2xx innerhalb von 10 Sekunden: zugestellt.
- Alles andere, ein Timeout oder ein Verbindungsfehler: Wiederholung nach 1 m, 5 m, 30 m, 2 h, 6 h, 12 h und 24 h (insgesamt 8 Versuche).
- 410 Gone: Wir hören auf, dieses Ereignis erneut zu versuchen.
- Zustellungen erfolgen mindestens einmal und können außer der Reihenfolge eintreffen; duplizieren Sie anhand der Ereignis-ID und verwenden Sie created_at zur Sortierung.
Ereignisse
- serp.run.completed: Ein Suchlauf wurde abgeschlossen und Ihr Eintrag wurde angeklickt.
- serp.run.not_found: Ein Suchlauf hat Ihre Website nicht in den Ergebnissen gefunden.
- serp.run.site_challenge: Ihre Website zeigte während eines Laufs eine Challenge-Seite an.
- serp.run.failed: Ein Suchlauf ist fehlgeschlagen (Timeout, blockiert oder anderer Fehler).
- serp.term.auto_paused: Ein Begriff wurde pausiert, nachdem 3 aufeinanderfolgende Läufe Ihre Website nicht gefunden haben.
- serp.failure_rate.high: Über die Hälfte der Läufe einer Website ist innerhalb von 24 Stunden fehlgeschlagen.
- local.run.completed: Ein Local Business-Lauf wurde abgeschlossen und Ihr Eintrag wurde geöffnet.
- local.run.not_found: Ein Local Business-Lauf hat Ihren Eintrag nicht gefunden.
- local.run.site_challenge: Ihre Website zeigte während eines Local Business-Laufs eine Challenge-Seite an.
- local.run.failed: Ein Local Business-Lauf ist fehlgeschlagen.
- local.rank.changed: Eine verfolgte lokale Kartenposition hat sich verschoben.
- aio.run.completed: Ein AI Overview-Lauf wurde abgeschlossen und Ihre Erwähnung wurde angeklickt.
- aio.run.not_found: Ein AI Overview-Lauf hat Ihre Website nicht zitiert gefunden.
- aio.run.site_challenge: Ihre Website zeigte während eines AI Overview-Laufs eine Challenge-Seite an.
- aio.run.failed: Ein AI Overview-Lauf ist fehlgeschlagen.
- aio.citation.gained: Ihre Website wurde im AI Overview für einen verfolgten Begriff zitiert.
- aio.citation.lost: Ihre Website wurde im AI Overview für einen verfolgten Begriff nicht mehr zitiert.
- serp.rank.changed: Eine verfolgte Position hat sich verschoben.
- competitor.rank.changed: Ein verfolgter Konkurrent hat sich um 3+ Plätze verschoben oder ist in die Top 10 oder 30 eingetreten oder daraus ausgeschieden.
- rank_ai.summary_ready: Ein Rank AI-Bericht wurde abgeschlossen, mit Gesundheitsbewertung, Zusammenfassung und Top-Empfehlungen.
- rank_ai.review_ready: Eine Rank AI-Seitenbewertung wurde abgeschlossen, mit Inhaltsbewertung, vorgeschlagenen Änderungen und fehlenden Themen.
- website.verified: Der Besitz einer Website wurde verifiziert.
- uptime.monitor.down: Ein Uptime-Monitor hat einen Ausfall bestätigt.
- uptime.monitor.up: Ein Uptime-Monitor hat sich erholt.
- domains.expiry.warning: Eine Domainregistrierung oder ein SSL-Zertifikat läuft bald ab.
- domains.change.detected: Eine überwachte Domain hat sich geändert (DNS, Registrar, Zertifikat und mehr).
- ssl.certificate.expiring: Ein Zertifikat läuft innerhalb Ihres Warnzeitraums ab.
- audit.completed: Ein SEO-Audit wurde abgeschlossen.
- audit.score_dropped: Der SEO-Wert einer Seite ist um 5 Punkte oder mehr gefallen.
- audit.issue.new: Ein Audit hat Probleme gefunden, die beim letzten Mal nicht vorhanden waren.
- analytics.traffic.spike: Die Besucherzahlen sind im Vergleich zum üblichen Niveau stark angestiegen.
- analytics.traffic.drop: Die Besuche sind im Vergleich zum üblichen Niveau stark zurückgegangen.
- backlinks.domain.new: Neue verweisende Domains wurden gefunden.
- backlinks.domain.lost: Verweisende Domains gingen verloren.
- bot.attack.detected: Schädlicher Bot-Verkehr stieg weit über das Normalmaß an.
- cookies.scan.completed: Ein Cookie-Scan wurde abgeschlossen.
- report.generated: Ein Bericht wurde erstellt.
- exchange.installed: Ihr Partnerblock wurde gefunden und die Website wurde verbunden.
- exchange.link_missing: Ihr Partnerblock fehlt, daher sind Ihre Links pausiert.
- exchange.partner_added: Ein Partner wurde zu Ihrem Block hinzugefügt oder Sie wurden zu seinem hinzugefügt.
- billing.subscription.updated: Ein Abonnement wurde erstellt, geändert oder gekündigt.
- plan.limit.reached: Eine Website hat das gesamte Kontingent eines Plans genutzt.
- billing.payment.failed: Eine Zahlung für Ihr Konto wurde nicht durchgeführt.
- workflow.run.succeeded: Ein Live-Workflow-Lauf wurde erfolgreich abgeschlossen.
- workflow.run.failed: Ein Live-Workflow-Lauf ist fehlgeschlagen.
- workflow.approval.requested: Ein Workflow wartet darauf, dass jemand einen Schritt genehmigt.
- workflow.custom: Ein Workflow hat ein benutzerdefiniertes Ereignis mit eigenem Payload gesendet.
Ereignisse ausführen
serp.run.*, local.run.* und aio.run.* enthalten run_id, website_id, search_term_id, module, outcome, position, page, dwell_seconds, pages_visited, device, country, started_at, finished_at und error_code. completed bedeutet, dass das Listing gefunden und angeklickt wurde, not_found, dass es nicht in den Ergebnissen war, site_challenge, dass Ihre Website eine Challenge-Seite zeigte, und failed umfasst Timeouts, Blockierungen und andere Fehler.
Testereignisse
test.ping wird nur gesendet, wenn Sie auf Testereignis senden drücken (oder webhooks_send_test verwenden). Die Daten enthalten eine Nachricht und Ihre Organisation.
Aktualisiert