Skip to content

Cabin Analytics API ​

Beschikbaar opPlusScale

Cabin biedt een API met alleen-leestoegang tot je gegevens. Antwoorden met statistieken zijn geaggregeerd (net zoals ze in het dashboard worden opgeslagen en getoond) en komen in JSON-formaat.

Op het Free-abonnement gebruik je in plaats daarvan AI/MCP-toegang. Die zit in elk abonnement en kan dezelfde vragen in gewone taal beantwoorden.

API-sleutel ​

Om de API te gebruiken, maak je een API-sleutel aan in het onderdeel API keys settings van je account.

Elke sleutel geeft alleen leestoegang en kan gelden voor al je domeinen of beperkt worden tot bepaalde domeinen.

API-sleutels worden niet gebruikt voor AI/MCP-toegang. Agents loggen in met OAuth en hebben helemaal geen sleutel nodig.

Onderdeel API-sleutels

  1. Kies 'New Key'
  2. Geef je sleutel een naam
  3. Kies tot welke domeinen je toegang wilt geven
  4. Klik op 'Create'

Authenticatie ​

Om je verzoeken te verifiëren, zet je je API-sleutel in de header x-api-key van je verzoeken.

Voorbeeldverzoek:

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"

Endpoints ​

De API is bereikbaar op https://api.withcabin.com/v1/{endpoint}.

/analytics ​

Dit endpoint geeft geaggregeerde gegevens over het verkeer op je website tussen twee datums, inclusief dagelijkse totalen voor paginaweergaven en bounces.

Queryparameters ​

domain ​

string required

De domeinnaam waarvoor je statistieken wilt ophalen.

date_from ​

string required

De begindatum van de gegevens (formaat: YYYY-MM-DD).

date_to ​

string required

De einddatum van de gegevens (formaat: YYYY-MM-DD).

scope ​

string optional

Standaard: core. De reikwijdte van de gegevens. Kan elke combinatie van core,pages,referrals,events bevatten.

limit_lists ​

number optional

Standaard: 50. Het aantal items per lijst. Waarden boven 250 worden niet geweigerd, maar de antwoorden worden dan groot en traag, dus zie 250 als de praktische bovengrens.

Dit geldt voor countries, languages, browsers, operating_systems, devices, screen_sizes, pages en referrals.

Percentages in het antwoord blijven, ongeacht de limiet, berekend over de volledige dataset.

Over scope ​

De scopes pages en referrals voegen extra gegevens toe voor afzonderlijke paden op je domein (zie het voorbeeldantwoord). Die zijn iets zwaarder, dus we raden aan core te gebruiken, tenzij je de extra gegevens nodig hebt.

Gegevens over energie en uitstoot zijn alleen beschikbaar met de scope pages.

Scrolldiepte komt ook mee met pages, op twee plekken: een object scroll_depth bij elke pagina, en een scroll_depth voor de hele site op het hoogste niveau. Die voor de hele site is opgeteld over elke pagina voordat limit_lists wordt toegepast, en komt dus niet overeen met de som van de pagina's die je hebt gekregen.

events geeft je eigen events terug, elk met een uitsplitsing naar de pagina's waarop het plaatsvond. Het leest dezelfde onderliggende gegevens als pages, dus pages,events samen opvragen kost niet meer dan een van de twee.

De uitsplitsing van AI-agents komt mee onder core, zonder extra verzoek. Deze hits worden los van summary.page_views geteld en zijn nooit factureerbaar. ai_agents telt dus niet mee in je totaal aan paginaweergaven, en elk percentage is een aandeel van het agentverkeer, niet van al het verkeer.

Voorbeeldantwoord ​

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

  /* Beschikbaar met 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
    }
  ],

  /* Beschikbaar met 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
    },
    // ...
  ],

  // Voor de hele site, opgeteld over elke pagina, niet alleen die in `pages`.
  scroll_depth: {
    average_percentage: 41.8,
    measured_visits: 94211,
    reached_at_least: [
      { percent: 0, visitors_percentage: 100 },
      // ... één rij per deciel ...
      { percent: 100, visitors_percentage: 18.4 }
    ]
  },

  /* Beschikbaar met 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
    },
    // ...
  ],

  /* Beschikbaar met 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 lezen ​

average_percentage is het gemiddelde diepste punt dat werd bereikt, en measured_visits is het aantal bezoeken waarop dat gemiddelde is gebaseerd. Het is altijd een steekproef: de meting wordt verstuurd als de bezoeker weggaat, en sommige browsers geven het script daar de kans niet voor. Daardoor is measured_visits lager dan page_views, vaak veel lager.

reached_at_least is cumulatief. Elke rij is het aandeel van de gemeten bezoeken dat minstens zo ver op de pagina kwam. percent: 0 is dus altijd 100, en vanaf daar dalen de rijen alleen maar. Het verschil tussen twee rijen laat zien waar mensen stopten.

js
// "welk aandeel las verder dan halverwege?"
const half = page.scroll_depth.reached_at_least.find(r => r.percent === 50)
console.log(half.visitors_percentage) // 65

De diepte wordt gemeten ten opzichte van het contentblok van de pagina, niet het hele document. Een lange footer laat een volledig gelezen pagina dus niet op 75% uitkomen. 100% betekent dat de onderkant van de content in beeld was, niet dat alles gelezen is. Zie scrolldiepte voor hoe het contentblok wordt gevonden en hoe je het zelf markeert.

Ontbreekt scroll_depth, dan betekent dat "niet gemeten", nooit nul. Pagina's die korter zijn dan het scherm hebben niets om te scrollen en worden weggelaten, net als pagina's waarvan alle bezoeken van vóór deze functie zijn. Controleer of de sleutel bestaat voordat je hem uitleest.

Limieten per abonnement ​

Je abonnement bepaalt wat je terugkrijgt:

  • Bewaartermijn. date_from wordt beperkt tot de bewaartermijn van je abonnement (12 maanden op Plus, onbeperkt op Scale). Vraag je oudere gegevens op, dan krijg je de oudste gegevens die je nog hebt in plaats van een foutmelding.
  • Energiegegevens. Het blok energy en de CO₂-cijfers per pagina vereisen CO₂-rapportage, die in Plus en Scale zit.
  • Eigen events. Het blok events zit in Plus en Scale.
  • AI-agents. Het blok ai_agents zit in Scale.
  • Scrolldiepte. In elk abonnement, Free inbegrepen.

Een blok waar je geen recht op hebt, wordt weggelaten uit het antwoord in plaats van leeg teruggegeven. Controleer dus of de sleutel bestaat voordat je hem uitleest.

Redelijk gebruik ​

Houd het aantal verzoeken redelijk, rond de 20 per minuut. De gegevens worden dagelijks geaggregeerd, dus vaker opvragen laat je niets nieuws zien. Heb je een hogere limiet nodig, neem dan contact op.