Seitenberichte-API-Referenz
Request- und Response-Formen der fünf inferenzfreien Berichts-Endpunkte einer Session: Consent, Agentensicht, Ziele, Kündigung und Screenshot.
Zuletzt aktualisiert:
Fünf Endpunkte auf einer laufenden Session beantworten strukturelle Fragen zu einer Seite, ohne je ein Modell aufzurufen: welche Consent-Plattform erscheint und was der Browser schon vor jeder Entscheidung hielt, wie der Baum des Agenten aussieht, wohin jedes Bedienelement führt, ob eine Kündigungsmöglichkeit erreichbar ist, und ein Bild der Seite. Alle fünf sind POSTs gegen eine Session-ID mit deinem bb_-Schlüssel als Bearer-Token, und alle fünf entstehen aus derselben Wahrnehmung, auf der auch der Agent arbeitet. Genau deshalb funktionieren sie auf einem Deployment, das Sessions hat und überhaupt kein Inference-Gateway.
Sie kamen mit Protokoll 1.5. Ein Client, der x-browserberg-protocol: 1.5.0 an einen älteren Server meldet, wird mit protocol_incompatible abgewiesen, statt die Lücke Route für Route zu entdecken. Ein Deployment, das die Berichte anbietet, führt reports im Array features von GET /v1/me; eines ohne sie registriert die Routen gar nicht, ein Aufruf bekommt also not_found.
Drei Fehler lohnen eine eigene Verzweigung. Ein Body, der die Validierung nicht besteht, ist invalid_request (400), mit den ersten zehn Schema-Beanstandungen in details.issues. Eine Session, die es nicht gibt oder die einer anderen Organisation gehört, ist not_found (404) — bewusst dieselbe Antwort für beides. Eine Adresse, die der Browser nicht öffnen kann, wegen DNS, TLS oder eines gesperrten Schemas, ist navigation_failed (422). Eine Seite, die einfach nie fertig lädt, ist dagegen kein Fehler: Nach fünfzehn Sekunden wird der Bericht so genommen, wie die Seite steht, und eine Zeile landet in warnings.
/v1/sessions/:id/consent-report
Welche Consent-Plattform erscheint, was der Browser vor jeder Entscheidung hielt und was ein Ablehnen verändert hat.
Consent-Bericht: Anfrage
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Eine absolute http- oder https-Adresse, höchstens 2048 Zeichen, die vor dem Bericht geöffnet wird. Weglassen, um die Seite zu berichten, auf der die Session schon steht. |
policy
|
string |
`reject`, `accept` oder `detect-only`. Unter `detect-only` wird nichts auf der Seite angefasst, deshalb kommt jede Nach-der-Wahl-Hälfte der Antwort als null zurück.
Standard: reject
|
waitMs
|
integer |
Wie lange auf ein spät gezeichnetes Banner gewartet wird, 0 bis 15000. Gesucht wird alle 250 ms, und beim ersten erkannten Banner endet die Suche.
Standard: 5000
|
screenshot
|
boolean |
Legt eine Aufnahme des sichtbaren Bereichs bei. Hier fest als JPEG in Qualität 60; Optionen nimmt nur der Screenshot-Endpunkt entgegen.
Standard: false
|
Consent-Bericht: Antwort
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Die angefragte Adresse, oder die Seite, auf der die Session angetroffen wurde. |
finalUrl
|
string | Wo die Seite nach allen Weiterleitungen gelandet ist. |
cmp
|
string | null | Die erkannte Plattform — `cookiebot`, `usercentrics`, `onetrust`, `didomi` und ein Dutzend weitere —, oder `heuristic`, wenn ein unbekanntes Banner über seine Beschriftungen erkannt wurde, oder null, wenn kein Banner zu sehen war. |
bannerDetectedAtMs
|
integer | null | Millisekunden nach der Navigation, zu denen ein Banner zuerst gesehen wurde. Null, wenn keines auftauchte. |
dismissal
|
object | null | Null unter `detect-only`. Sonst `{dismissed, method, actedOn?, rounds, stubborn, durationMs}`: `method` ist `click`, `api` oder `none`, `rounds` zählt die Ebenen, und `stubborn` heißt erkannt, bedient und trotzdem noch da. |
cookies
|
object | `{beforeChoice, afterChoice}`, je eine Liste von Cookie-Zeilen. `afterChoice` ist unter `detect-only` null. |
requests
|
object | `{beforeChoice, afterChoice}`, je eine Liste von Host-Zeilen. Dieselbe Null-Regel wie bei den Cookies. |
summary
|
object | `{cookiesBeforeChoice, thirdPartyCookiesBeforeChoice, thirdPartyHostsBeforeChoice, newCookiesAfterReject}`. Der letzte Wert ist null, sofern die Policy nicht `reject` war. |
screenshot
|
object | Nur vorhanden, wenn du eine Aufnahme angefordert hast: `{contentType, base64, width?, height?}`. |
warnings
|
string[] | Was nicht sauber lief — ein Ladevorgang ohne Ende, Cookies, die der Browser nicht herausgibt, ein Banner, das sich sträubt. |
durationMs
|
integer | Vergangene Zeit für den Bericht, Navigation eingeschlossen. |
/v1/sessions/:id/agent-view
Die Seite, wie der Agent sie liest, in bis zu drei Detailstufen, samt den gefundenen Bedienelementen.
Agentensicht: Anfrage
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Wird zuerst geöffnet, wie oben. Weglassen für die Seite, auf der die Session steht. |
fidelities
|
string[] |
Ein bis drei aus `full`, `economy` und `lean`. Jeder Eintrag rendert DENSELBEN Baum; nur die Auslassung unterscheidet sich, die Element-IDs stimmen über alle Darstellungen hinweg überein.
Standard: [full, economy, lean]
|
screenshot
|
boolean |
Legt ein JPEG des sichtbaren Bereichs in Qualität 60 bei.
Standard: true
|
maxChars
|
integer | 1000 bis 400000, eine Obergrenze je gerenderter Sicht. Ein Abschneiden steht in `views[].truncated` und geschieht nie stillschweigend. |
consent
|
string |
`reject`, `accept` oder `off` — wie ein Banner behandelt wird, bevor die Seite gelesen wird.
Standard: reject
|
Agentensicht: Antwort
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Die gelesene Seite. |
views
|
array | Ein Eintrag je angeforderter Detailstufe: `{fidelity, text, chars, lines, truncated}`. |
controls
|
array | Ein Eintrag je interaktivem Element, ohne dessen Teilbaum: `{encodedId, role, name, tag, jsClickable, hasAccessibleName}`. Genug, um Lesbarkeit zu beurteilen, und zu wenig, um die Seite nachzubauen. |
stats
|
object | `{rawNodes, keptNodes, frames, occluded, offscreen, warnings}` — wie viel von der Seite das Beschneiden überstanden hat, und warum. |
world
|
string | `extension` oder `cdp-fallback`: welche Ausführungswelt gelesen hat. |
consentDismissed
|
boolean | Ob vor der Aufnahme des Baums tatsächlich ein Banner geräumt wurde. |
screenshot
|
object | Vorhanden, sofern du `screenshot` nicht auf false gesetzt hast. |
warnings
|
string[] | Die Warnungen der Navigation und die der Seitenvorbereitung, aneinandergehängt. |
durationMs
|
integer | Vergangene Zeit für den ganzen Aufruf. |
Die controls-Liste lesen
jsClickable kennzeichnet ein Element ohne eigene interaktive Rolle, das nur anklickbar ist, weil ein Skript darauf lauscht — üblich bei Portalen, die aus gestylten divs gebaut sind. hasAccessibleName ist false für ein Bedienelement, das weder ein Agent noch ein Screenreader beim Namen ansprechen kann, und das ist meist die nützlichste Zahl der Seite: Eine Oberfläche, deren halbe Schaltflächen namenlos sind, macht einem Agenten aus Gründen zu schaffen, die mit dem Modell nichts zu tun haben.
Der gerenderte text ist Seiteninhalt. Behandle ihn als nicht vertrauenswürdig und zäune ihn ein, bevor er in einen weiteren Prompt gerät — genau wie den Baum, den observe zurückgibt.
/v1/sessions/:id/destination-report
Löst in der Seite auf, wohin jeder Link, jede Schaltfläche und jedes Formular wirklich führt, und wie der Ziel-Wächter das jeweils beurteilen würde.
Ziel-Bericht: Anfrage
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Wird zuerst geöffnet. Weglassen für die Seite, auf der die Session steht. |
limit
|
integer |
Wie viele Bedienelemente aufgelöst werden, in Dokumentreihenfolge, 1 bis 500. Gab es mehr, ist `truncated` true.
Standard: 200
|
consent
|
string |
`reject`, `accept` oder `off`. Ein stehen gelassenes Banner verdeckt die Bedienelemente dahinter.
Standard: reject
|
Ziel-Bericht: Antwort
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Die Seite, auf der gezählt wurde. |
anchorSite
|
string | null | Die registrierbare Site der Seite selbst — daran wird `sameSite` gemessen. |
secure
|
boolean | Die Seite selbst wurde über https ausgeliefert. |
controls
|
array | `{encodedId, role, name, action, destination, site, sameSite, risk, blockedUnderDefault, blockedUnderStrict}`, je aufgelöstem Bedienelement einmal. |
summary
|
object | `{total, sameSite, offSite, offSiteSubmit, insecureSubmit, unsafeScheme, scriptOnly, unresolved}` — die Zahlen, auf die die meisten Aufrufer wirklich verzweigen. |
truncated
|
boolean | Es gab mehr Bedienelemente, als `limit` zuließ. |
warnings
|
string[] | Warnungen aus Navigation und Seitenvorbereitung. |
durationMs
|
integer | Vergangene Zeit für den ganzen Aufruf. |
Wie ein einzelnes Element beurteilt wird
action ist das, was ein Agent mit dem Element täte — click, type, select oder scroll —, entschieden aus Rolle und Tag, nie aus einer Anweisung. destination ist {url, via, method?, target?, hasFileInput?, unresolved?}, und via hält fest, woher das Ziel stammt: href, ancestor-href, formaction, form, implicit-submit, script, wenn nur Code weiß, wohin es geht, oder none, wenn gar nichts gefunden wurde.
risk ist der Befund des Ziel-Wächters für dieses eine Element, einzeln beurteilt. Der Wächter hält sonst beim ersten blockierten Schritt eines Plans an, was für einen Plan richtig und für eine Zählung falsch ist, also läuft jedes Element für sich hindurch. off_site_submit, insecure_submit und unsafe_scheme werden schon unter der Standard-Policy verweigert und setzen blockedUnderDefault; off_site_navigation ist standardmäßig erlaubt — eine SSO-Weiterleitung verlässt die Site aus gutem Grund — und setzt nur blockedUnderStrict. Ein href mit Skript-Schema ist überhaupt kein Risiko: Alte Portale schreiben so etwas an jeden Tabellenlink, der Wächter lässt es in Ruhe, und der Bericht sagt nur, was er gesehen hat.
/v1/sessions/:id/cancellation-report
Sucht auf einer Startseite ein Bedienelement, dessen Beschriftung nach Kündigung klingt, löst dessen Ziel auf und beschreibt die Seite dahinter.
Kündigungs-Bericht: Anfrage
| Name | Typ | Beschreibung |
|---|---|---|
url
erforderlich
|
string | Die Seite, von der aus eine Verbraucherin startet. Pflicht, denn die Frage dieses Berichts ist die Erreichbarkeit von genau hier aus. |
consent
|
string |
`reject`, `accept` oder `off`.
Standard: reject
|
follow
|
boolean |
Öffnet das aufgelöste Ziel des besten Kandidaten und beschreibt es. Geklickt wird das Element nie und abgeschickt wird nichts; die Zieladresse wird direkt geöffnet.
Standard: true
|
screenshot
|
boolean |
Nimmt die Startseite auf, und die gefolgte Seite ebenfalls, wenn eine gefolgt wurde. JPEG des sichtbaren Bereichs in Qualität 60.
Standard: true
|
Kündigungs-Bericht: Antwort
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Die angefragte Startseite. |
finalUrl
|
string | Wo diese Seite nach Weiterleitungen gelandet ist. |
candidates
|
array | `{encodedId, role, name, matchedPhrase, destination, site, sameSite}`. `matchedPhrase` ist der Eintrag aus dem Kündigungs-Wortschatz des Produkts, auf den die Beschriftung passte — so ist nachvollziehbar, warum ein Element aufgefallen ist. |
followed
|
object | null | `{encodedId, targetUrl, confirmationControls, fields, screenshot?}`, oder null, wenn nichts gefolgt wurde. |
screenshot
|
object | Die Startseite, wenn du Bilder angefordert hast. |
warnings
|
string[] | Sagt, wenn kein Kandidat gefunden wurde, und wenn es Kandidaten gab, denen aber keiner gefolgt werden konnte. |
durationMs
|
integer | Vergangene Zeit für den ganzen Aufruf, beide Seiten eingerechnet. |
Was follow wirklich tut
Gefolgt wird dem ersten Kandidaten, dessen Ziel eine Adresse hat und nicht auf eine andere registrierbare Site führt. Einem Kandidaten, dessen Ziel nur ein Skript kennt, kann man ohne Drücken nicht folgen, und eine Kündigung unbeaufsichtigt zu drücken ist genau das, was das Produkt verweigert — er wird also übersprungen, und eine Warnung sagt warum. followed ist deshalb in vier verschiedenen Lagen null: kein Kandidat gefunden, follow war false, alle Kandidaten führten weg von der Site oder waren skriptgesteuert, oder das Ziel ließ sich nicht öffnen.
Wurde eine Seite gefolgt, listet confirmationControls die Schaltflächen und Links darauf, deren Beschriftung nach dem bestätigenden Druck klingt — wieder der Kündigungs-Wortschatz, dazu die üblichen Bestätigungs- und Absende-Wörter auf Deutsch und Englisch. fields listet, was die Seite verlangt, als {role, name, required}, wobei required null ist, wenn die Seite dazu nichts gesagt hat. Das ist die interessante Hälfte für eine Verbraucherfrage: Eine Kündigung, die einen Klick entfernt ist und dann eine Kundennummer verlangt, ist eine andere Erfahrung als eine, die das nicht tut.
/v1/sessions/:id/screenshot
Ein Bild der Seite, nachdem das Consent-Banner behandelt wurde, sichtbarer Bereich oder ganzes Dokument.
Screenshot: Anfrage
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Wird zuerst geöffnet. Weglassen, um die Seite aufzunehmen, auf der die Session steht. |
format
|
string |
`jpeg` oder `png`. JPEG ist Standard, weil niemand die Pixel liest und eine ganzseitige Portalaufnahme als PNG megabyteweise anfällt.
Standard: jpeg
|
quality
|
integer |
1 bis 100. Nur für JPEG; bei PNG wirkungslos.
Standard: 60
|
fullPage
|
boolean |
Nimmt das ganze Dokument statt des sichtbaren Bereichs auf. Die Höhe kommt aus den Layout-Metriken der Seite und wird bei 16384 CSS-Pixeln gekappt, denn eine Aufnahme über 200000 Pixel ist kein Screenshot mehr.
Standard: false
|
consent
|
string |
`reject`, `accept` oder `off`. Nur `off` überspringt das Wegklicken ganz und lässt das Banner im Bild.
Standard: reject
|
waitMs
|
integer |
Zusätzliche Beruhigungszeit nach dem Laden, 0 bis 15000, für Seiten, die spät zeichnen. Danach bekommt das Netz ohnehin bis zu 1,5 s, um still zu werden.
Standard: 0
|
Screenshot: Antwort
| Name | Typ | Beschreibung |
|---|---|---|
url
|
string | Die aufgenommene Seite. |
contentType
|
string | `image/jpeg` oder `image/png`. |
base64
|
string | Die Bildbytes, base64-kodiert, damit ein Bericht ein einziges JSON-Dokument bleibt, das man aufheben kann. |
width
|
integer | Breite der Aufnahme in CSS-Pixeln. |
height
|
integer | Höhe der Aufnahme in CSS-Pixeln, nach der Kappung bei ganzseitigen Bildern. |
fullPage
|
boolean | Gibt zurück, was angefordert wurde. |
consentDismissed
|
boolean | Ob vor dem Auslösen tatsächlich ein Banner geräumt wurde. |
durationMs
|
integer | Vergangene Zeit für den ganzen Aufruf. |
Hinweis · Beobachtungen, niemals Urteile
Der Consent-Bericht sagt, welche Cookies gesetzt waren, bevor überhaupt eine Wahl angeboten wurde. Er sagt nicht, dass eine Site rechtskonform ist, und kein Feld auf der Leitung trägt eine Note. Der Kündigungs-Bericht sagt, ob ein Element mit Kündigungs-Beschriftung gefunden wurde, wohin es führte und was die Seite dahinter verlangte; er sagt nicht, dass eine gesetzliche Anforderung erfüllt ist. Das Urteil gehört der Person, die den Bericht liest, und eine Form, die ein Urteil trüge, würde als solches zitiert.
Zwei Berichte auf einer Session
Eine Session, zwei Berichte, keine Inferenz. Die cURL-Variante zeigt die rohe Form auf der Leitung; die SDKs liefern dasselbe Dokument geparst.
Voraussetzungen: api-key
BASE="https://browserberg.com"
AUTH="Authorization: Bearer $BROWSERBERG_API_KEY"
JSON="Content-Type: application/json"
ID=$(curl -s -X POST "$BASE/v1/sessions" -H "$AUTH" -H "$JSON" -d '{"ttlSeconds": 180}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["session"]["id"])')
# No model call: the report is computed from the page, so it costs browser time only.
curl -s -X POST "$BASE/v1/sessions/$ID/consent-report" -H "$AUTH" -H "$JSON" -d '{
"url": "https://shop.example.com/", "policy": "reject", "waitMs": 5000
}' | python3 -c 'import json,sys; r = json.load(sys.stdin); print(json.dumps({
"cmp": r["cmp"],
"bannerDetectedAtMs": r["bannerDetectedAtMs"],
"summary": r["summary"]}, indent=2))'
curl -s -X DELETE "$BASE/v1/sessions/$ID" -H "$AUTH" > /dev/null
import { Browserberg } from '@browserberg/sdk';
const bb = new Browserberg({
apiKey: process.env.BROWSERBERG_API_KEY!,
baseUrl: 'https://browserberg.com',
});
await using session = await bb.sessions.create({ ttlSeconds: 180 });
// Neither call reaches a model. Both read the page the agent would read.
const consent = await session.consentReport({ url: 'https://shop.example.com/' });
console.log(consent.cmp, consent.summary.thirdPartyHostsBeforeChoice);
const map = await session.destinationReport({ limit: 200 });
console.log(map.summary.total, 'controls,', map.summary.offSiteSubmit, 'posting off-site');
import os
from browserberg import Browserberg
bb = Browserberg(os.environ["BROWSERBERG_API_KEY"], base_url="https://browserberg.com")
with bb.sessions.create(ttl_seconds=180) as session:
consent = session.consent_report("https://shop.example.com/")
print(consent.cmp, consent.summary["thirdPartyHostsBeforeChoice"])
page_map = session.destination_report(limit=200)
print(page_map.summary["total"], "controls,", page_map.summary["offSiteSubmit"], "off-site")