Skip to content

Cabin Analytics API ​

Verfügbar inPlusScale

Cabin bietet eine API, über die Sie lesend auf Ihre Daten zugreifen. Die Analytics-Antworten sind aggregiert (genau so, wie die Daten auch im Dashboard gespeichert und angezeigt werden) und kommen im JSON-Format.

Im Free-Tarif nutzen Sie stattdessen den KI/MCP-Zugang. Er ist in jedem Tarif enthalten und beantwortet dieselben Fragen in Alltagssprache.

API-Schlüssel ​

Um die API zu nutzen, erstellen Sie in Ihrem Konto unter API keys settings einen API-Schlüssel.

Jeder Schlüssel ist schreibgeschützt. Sie können ihn für alle Ihre Domains freigeben oder auf einzelne Domains beschränken.

Für den KI/MCP-Zugang werden keine API-Schlüssel verwendet. Agenten melden sich stattdessen per OAuth an und brauchen überhaupt keinen Schlüssel.

Bereich API-Schlüssel

  1. Wählen Sie 'New Key'
  2. Geben Sie Ihrem Schlüssel einen Namen
  3. Wählen Sie die Domains aus, auf die der Schlüssel zugreifen darf
  4. Klicken Sie auf 'Create'

Authentifizierung ​

Zur Authentifizierung senden Sie Ihren API-Schlüssel im Header x-api-key jeder Anfrage mit.

Beispielanfrage:

bash
curl -X GET "https://api.withcabin.com/v1/analytics?domain=example.com&date_from=2025-01-01&date_to=2025-02-01&scope=core&limit_lists=20" -H "x-api-key: YOUR_API_KEY"

Endpunkte ​

Die API ist unter https://api.withcabin.com/v1/{endpoint} erreichbar.

/analytics ​

Dieser Endpunkt liefert aggregierte Daten zum Traffic Ihrer Website zwischen zwei Daten, einschließlich aggregierter Tageswerte für Seitenaufrufe und Absprünge.

Query-Parameter ​

domain ​

string required

Der Domainname, für den Sie Analytics-Daten abrufen möchten.

date_from ​

string required

Das Startdatum der Daten (Format: YYYY-MM-DD).

date_to ​

string required

Das Enddatum der Daten (Format: YYYY-MM-DD).

scope ​

string optional

Standard: core. Der Umfang der Daten. Kann jede Kombination aus core,pages,referrals,events enthalten.

limit_lists ​

number optional

Standard: 50. Die Anzahl der Einträge, die in jeder Liste zurückgegeben werden. Werte über 250 werden nicht abgelehnt, aber die Antworten werden groß und langsam. Betrachten Sie 250 daher als praktische Obergrenze.

Das betrifft countries, languages, browsers, operating_systems, devices, screen_sizes, pages und referrals.

Die Prozentwerte in der Antwort beziehen sich unabhängig vom Limit immer auf den gesamten Datensatz.

Über scope ​

Die Scopes pages und referrals liefern zusätzliche Daten zu einzelnen Pfaden Ihrer Domain (siehe die Beispielantwort). Diese Abfragen sind etwas aufwendiger. Wir empfehlen daher core, solange Sie die zusätzlichen Daten nicht brauchen.

Daten zu Energie und Emissionen gibt es nur mit dem Scope pages.

Auch die Scrolltiefe kommt mit pages, und zwar an zwei Stellen: als Objekt scroll_depth bei jeder Seite und als seitenweites scroll_depth auf oberster Ebene. Der seitenweite Wert wird über alle Seiten summiert, bevor limit_lists greift. Er entspricht also nicht der Summe der Seiten, die Sie erhalten haben.

events liefert Ihre eigenen Events, jeweils aufgeschlüsselt nach den Seiten, auf denen sie ausgelöst wurden. Dafür werden dieselben Rohdaten gelesen wie für pages. Eine Abfrage von pages,events kostet also nicht mehr als eine der beiden allein.

Die Aufschlüsselung nach KI-Agenten kommt ohne zusätzliche Anfrage mit core. Diese Aufrufe werden getrennt von summary.page_views gezählt und nie abgerechnet. ai_agents fließt also nicht in Ihre Gesamtzahl der Seitenaufrufe ein, und jeder percentage-Wert ist ein Anteil am Agenten-Traffic, nicht am gesamten Traffic.

Beispielantwort ​

json
{
  query: {
    domain: "example.com",
    date_from: "2025-01-01",
    date_to: "2025-02-01",
    scope: "core,pages,referrals",
    limit_lists: 10
  },

  /* Verfügbar mit scope: core */

  summary: {
    page_views: 1959,
    unique_visitors: 1165,
    bounces: 817,
    bounce_rate: 0.29871244635193134
  },
  daily_data: [
    {
      timestamp: 1735689600000,
      page_views: 22,
      unique_visitors: 16,
      bounces: 12,
      bounce_rate: 0.75
    },
    {
      timestamp: 1735776000000,
      page_views: 29,
      unique_visitors: 26,
      bounces: 23,
      bounce_rate: 0.88
    },
    {
      timestamp: 1735862400000,
      page_views: 24,
      unique_visitors: 16,
      bounces: 13,
      bounce_rate: 0.81
    }
  ],
  screen_sizes: {
    small: 38,
    medium: 372,
    large: 463
  },
  devices: {
    desktop: 873,
    mobile: 288,
    tablet: 4,
    smart_tv: 0,
    console: 0,
    wearable: 0
  },
  browsers: [
    {
      name: "Chrome",
      value: 718
    },
    {
      name: "WebKit",
      value: 117
    },
    {
      name: "Firefox",
      value: 85
    }
  ],
  operating_systems: [
    {
      name: "Windows",
      value: 467
    },
    {
      name: "Mac OS",
      value: 365
    },
    {
      name: "iOS",
      value: 223
    }
    // ...
  ],
  countries: [
    {
      code: "GB",
      value: 362
    },
    {
      code: "US",
      value: 263
    },
    {
      code: "JP",
      value: 41
    }
    // ...
  ],
  languages: [
    {
    code: "en",
      value: 887
    },
    {
      code: "ja",
      value: 37
    },
    {
      code: "ru",
      value: 32
    }
    // ...
  ],
  traffic_sources: {
    email: 0,
    search: 217,
    social: 151,
    unknown: 789
  },
  ai_agents: [
    {
      name: "ChatGPT",
      hits: 34,
      percentage: 0.9444
    },
    {
      name: "Claude",
      hits: 2,
      percentage: 0.0556
    }
  ],

  /* Verfügbar mit scope: pages */

  energy: {
    page_count: 22,
    green_hosting: {
      url: "nicmulvaney.com",
      hosted_by: "Cloudflare",
      hosted_by_website: "https://www.cloudflare.com",
      partner: null,
      green: true,
      hosted_by_id: 779,
      modified: "2025-03-17T20:24:22",
      supporting_documents: [
        {
          id: 18,
          title: "Blog post - The Climate and Cloudflare",
          link: "https://blog.cloudflare.com/the-climate-and-cloudflare/"
        },
        {
          id: 1264,
          title: "Cloudflare 2023 Emissions Inventory",
          link: "https://s3.nl-ams.scw.cloud/tgwf-web-app-live/uploads/Cloudflare_2023_Emissions_Inventory.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=SCWT1WBAW6NZ5SW5GYJ8%2F20250317%2Fnl-ams%2Fs3%2Faws4_request&X-Amz-Date=20250317T202736Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=0d3b2ffd9a885dbc41193e0e72bdee9c4ee8cf37671af3d092453578cc5a179c"
        }
      ]
    },
    average_time_spent_ms: 122892,
    average_co2_grams: 0.0708,
    total_co2_grams: 138.65,
    total_distance_km: 0.55,
    total_kettles: 4,
    transferred_bytes: 1145572683,
    total_bytes: 22060595,
    duration_total_ms: 240746383,
    duration_count: 1272
  },
  pages: [
    {
      path: "/page/1",
      page_views: 271671,
      unique_visitors: 243070,
      average_duration_seconds: 123,
      total_bytes: 8.2,
      co2_grams: 538,
      page_views_percentage: 0.12,
      scroll_depth: {
        average_percentage: 46.2,
        measured_visits: 1840,
        reached_at_least: [
          { percent: 0, visitors_percentage: 100 },
          { percent: 10, visitors_percentage: 88.37 },
          { percent: 20, visitors_percentage: 79.08 },
          { percent: 30, visitors_percentage: 73.75 },
          { percent: 40, visitors_percentage: 69.18 },
          { percent: 50, visitors_percentage: 65 },
          { percent: 60, visitors_percentage: 57.83 },
          { percent: 70, visitors_percentage: 52.61 },
          { percent: 80, visitors_percentage: 46.63 },
          { percent: 90, visitors_percentage: 38.86 },
          { percent: 100, visitors_percentage: 22.01 }
        ]
      }
    },
    {
      path: "/page/2",
      page_views: 167111,
      unique_visitors: 150073,
      average_duration_seconds: 147,
      total_bytes: 4.82,
      co2_grams: 222.6,
      page_views_percentage: 0.08
    },
    {
      path: "/page/3",
      page_views: 106361,
      unique_visitors: 93743,
      average_duration_seconds: 97,
      total_bytes: 4.82,
      co2_grams: 133.4,
      page_views_percentage: 0.05
    },
    // ...
  ],

  // Seitenweit, summiert über alle Seiten, nicht nur über die in `pages`.
  scroll_depth: {
    average_percentage: 41.8,
    measured_visits: 94211,
    reached_at_least: [
      { percent: 0, visitors_percentage: 100 },
      // ... eine Zeile pro Dezil ...
      { percent: 100, visitors_percentage: 18.4 }
    ]
  },

  /* Verfügbar mit scope: referrals */

  referrals: [
    {
      source: "Google",
      page_views: 259,
      unique_visitors: 197,
      has_utm: false,
      page_views_percentage: 0.39
    },
    {
      source: "LinkedIn",
      page_views: 252,
      unique_visitors: 136,
      has_utm: false,
      page_views_percentage: 0.38
    },
    {
      source: "com.linkedin.android",
      page_views: 34,
      unique_visitors: 18,
      has_utm: false,
      page_views_percentage: 0.05
    },
    // ...
  ],

  /* Verfügbar mit scope: events */

  events: [
    {
      name: "signup button (header)",
      count: 14,
      percentage: 0.1129,
      pages: [
        {
          path: "/",
          count: 13,
          percentage: 0.9286
        },
        {
          path: "/pricing",
          count: 1,
          percentage: 0.0714
        }
      ]
    },
    // ...
  ]
}

scroll_depth lesen ​

average_percentage ist der Mittelwert der tiefsten erreichten Stelle, und measured_visits gibt an, aus wie vielen Besuchen dieser Mittelwert stammt. Es handelt sich immer um eine Stichprobe: Der Messwert wird gesendet, wenn der Besucher die Seite verlässt, und manche Browser geben dem Skript dazu keine Gelegenheit. Deshalb ist measured_visits kleiner als page_views, oft sogar deutlich.

reached_at_least ist kumulativ. Jede Zeile zeigt den Anteil der gemessenen Besuche, die mindestens so weit nach unten gekommen sind. percent: 0 ist also immer 100, und von dort aus fallen die Werte nur noch. Der Abstand zwischen zwei Zeilen zeigt, wo Leute aufgehört haben.

js
// "Welcher Anteil hat über die Hälfte hinaus gelesen?"
const half = page.scroll_depth.reached_at_least.find(r => r.percent === 50)
console.log(half.visitors_percentage) // 65

Die Tiefe wird am Inhaltsblock der Seite gemessen, nicht am gesamten Dokument. Ein langer Footer lässt einen vollständig gelesenen Artikel also nicht wie 75 % aussehen. 100 % bedeutet, dass das Ende des Inhalts auf dem Bildschirm war, nicht dass er gelesen wurde. Unter Scrolltiefe erfahren Sie, wie der Inhaltsblock ermittelt wird und wie Sie ihn selbst markieren.

Fehlt scroll_depth, heißt das „nicht gemessen“, niemals null. Seiten, die kürzer als der Bildschirm sind, bieten nichts zum Scrollen und werden ausgelassen. Dasselbe gilt für Seiten, deren Besuche alle aus der Zeit vor dieser Funktion stammen. Prüfen Sie, ob der Schlüssel vorhanden ist, bevor Sie ihn auslesen.

Tariflimits ​

Folgende Tariflimits bestimmen, was zurückkommt:

  • Aufbewahrung. date_from wird auf den Aufbewahrungszeitraum Ihres Tarifs begrenzt (12 Monate bei Plus, unbegrenzt bei Scale). Fragen Sie ältere Daten an, erhalten Sie die ältesten Daten, die noch vorhanden sind, und keinen Fehler.
  • Energiedaten. Der Block energy und die CO₂-Werte pro Seite setzen das CO₂-Reporting voraus, das es bei Plus und Scale gibt.
  • Eigene Events. Der Block events ist bei Plus und Scale enthalten.
  • KI-Agenten. Der Block ai_agents ist bei Scale enthalten.
  • Scrolltiefe. In jedem Tarif, auch bei Free.

Ein Block, der in Ihrem Tarif nicht enthalten ist, fehlt in der Antwort ganz, statt leer zurückzukommen. Prüfen Sie also, ob der Schlüssel vorhanden ist, bevor Sie ihn auslesen.

Faire Nutzung ​

Bitte halten Sie die Anfragerate in einem vernünftigen Rahmen, etwa 20 pro Minute. Die Daten werden täglich aggregiert, häufigeres Abfragen zeigt Ihnen also nichts Neues. Wenn Sie eine höhere Rate brauchen, schreiben Sie uns.