Statable Analytics

Privacy-first, cookieless web analytics hosted in the EU.

Community: Submitted by a user or imported; check the owner before granting accessOnlineNo sign-inGlobalFreeCan modify data

What it can do

  • Create Site: Add a site to this account and get back its id and tracking code. Response: {"site_id":123,"name":"https://example.com","timezone":"Europe/Amsterdam",...}. The url must include the scheme
  • Update Site: Change a site's url, timezone or week start. Omitted fields are left alone. Returns the updated site. Changing the url to one this account already has is refused (site_exists).
  • List Goals: The goals CONFIGURED on a site — what it measures, not how those goals performed. Use top_goals for conversion numbers. Response: {"goals":[{"id":7,"name":"Signup","event_name":"Signup"|nu

What data it sees

Do you need an account

No: the server works without sign-in

Privacy-first, cookieless web analytics hosted in the EU. Ask your assistant about visitors, pages, traffic sources, countries, devices, goals, funnels and live traffic for your sites, and set up sites, goals and funnels without leaving the chat.

25 tools. query_stats runs aggregates, time series and top-N breakdowns over any metric and dimension with filters and period-over-period comparison. Shortcuts: top_pages, top_sources, top_countries, top_goals, funnel_report, current_visitors, visitors_over_time. Setup tools create and update sites, goals, funnels, tracking features and site filters. Nothing deletes data.

Auth. OAuth 2.1 with PKCE: sign in to Statable in the browser and pick the sites the assistant may read. API keys (Authorization: Bearer stbl_...) are supported for scripts and hosts without OAuth; a stdio bridge is on npm as @statable/mcp.

All numbers come back in the site's own timezone. Docs: https://statable.com/docs/integrations/mcp/

Server tool list (25)

Raw names from tools/list. Only developers need these.

create_siteAdd a site to this account and get back its id and tracking code. Response: {"site_id":123,"name":"https://example.com","timezone":"Europe/Amsterdam",...}. The url must include the scheme. A url this account already has is REFUSED (site_exists) rather than returned — two sites collecting the same traffic is worse than an error. Retrying with the same arguments replays the first result instead of creating a second site. Follow with get_tracking_snippet to obtain the script tag to install.
update_siteChange a site's url, timezone or week start. Omitted fields are left alone. Returns the updated site. Changing the url to one this account already has is refused (site_exists).
list_goalsThe goals CONFIGURED on a site — what it measures, not how those goals performed. Use top_goals for conversion numbers. Response: {"goals":[{"id":7,"name":"Signup","event_name":"Signup"|null,"path":"/thanks"|null,"operator":"e|b|c","scroll_depth":N|null}]}.
create_goalAdd a goal to a site. A goal is EITHER a custom event (event_name), OR a page (path with operator), OR a scroll depth — give one of the three, not several. operator: "e" exact, "b" begins with, "c" contains. A duplicate name is refused (goal_exists).
update_goalReplace a goal's definition. Send the whole goal, not just the changed fields — omitted fields are cleared, not kept.
create_funnelAdd a funnel: an ordered list of 2+ steps. Each step is one of {"type":"page","value":"/pricing"}, {"type":"event","value":"Signup"}, {"type":"entry_page"|"exit_page","value":"/"}. An exit_page step must be last. scope "visitor" (default) or "session". A duplicate name is refused (funnel_exists). Read the result with funnel_report.
update_funnelReplace a funnel's definition. Send the whole funnel including every step — omitted steps are removed, not kept.
get_tracking_settingsWhat the installed script actually does: which tracking features are on, which exist, and what each costs in bytes. Response: {"site_id":123,"version":N,"bundle":"...","enabled":["hash","outbound"],"features":[{"id":"outbound","label":"...","enabled":true,"locked":false,"default":false,"requires":[...],"size_br":123}]}. Locked features cannot be changed; `requires` lists features that must be on for this one to work. Needs the same access as changing them (see update_tracking_settings).
update_tracking_settingsSet which tracking features the site's script includes. REPLACES the whole selection: send every feature you want enabled, not just the new one — anything omitted is turned off. Call get_tracking_settings first for the valid ids. Rebuilds and republishes the script, so the change reaches visitors after the CDN cache clears.
get_subscriptionThe account's plan state — not a site's. Response: {"status":"trialing"|"active"|"past_due"|"expired"|"trial_expired"|"none","is_trial":true,"ends_at":"RFC3339","only_hobby":false}. ends_at is when the CURRENT state stops being true (trial end while trialing, period end otherwise) and is omitted when unknown. status "none" with only_hobby true means a free account, not a missing one. Takes no arguments.
get_site_filtersWho gets counted on a site, and who may look at the stats. Response: {"site_id":123,"hostnames":{"allowed":[],"blocked":[]},"blocked_ips":["1.2.3.4"],"countries":{"allowed":[{"code":"UA","created_at":"RFC3339"}],"blocked":[]},"public_dashboard":false}. An empty allowed list means "no restriction", not "nothing allowed". public_dashboard true means the stats are readable by anyone with the link.
update_site_filtersChange who gets counted, and who may look. Send only the sections you want to change — an omitted section is left untouched. A section you DO send replaces that setting entirely: {"countries":{"blocked":["RU"]}} also clears the country allow list, and {"blocked_ips":[]} empties the blocklist. Read the current state with get_site_filters and send it back with your edit applied. Filtering takes effect on new traffic; it does not remove data already collected. Answers with the resulting state plus "updated":["hostnames",...] naming what changed.
get_tracking_snippetThe script tag to install on a site, and the url it loads. Response: {"site_id":123,"type":"default","script_url":"https://...","snippet":"<script ...></script>"}. Install snippet verbatim; do not rebuild a tag from script_url. A hobby site's snippet also carries data-id, which is the only place the counter in its bundle reads the site id. Read-only: it reports the code, it does not install or verify anything.
list_sitesList the sites this API key can read. Response: {"sites":[{"site_id":123,"name":"<as entered by the owner — often a full URL like https://example.com/>","hash":"07k1eD84d2","timezone":"Europe/Amsterdam","hobby":false,"stats_start_date":"YYYY-MM-DD"|null,"created_at":"RFC3339"}]}. hobby=true is a free-plan site: it counts only through the bundled /t/ script get_tracking_snippet returns, so install that snippet verbatim. Use site_id with the other tools. With date_range each site also carries metrics NESTED under stats.metrics (never flat on the site object). Units: visit_duration = average seconds per visit; bounce_rate = percent 0-100; views_per_visit = pageviews/visits ratio; visitors/pageviews/visits = counts. Stats period boundaries are server-time approximations; use query_stats for timezone-exact numbers.
query_statsRun a read-only analytics query for one site: aggregate totals, a time series, or a top-N breakdown. Returns {"results":[{"dimensions":{...omitted for aggregates},"metrics":{...}}]}. All dates and time buckets are in the site's own timezone (see list_sites .timezone). The event:name breakdown lists custom events (data-statable-event); its `events` metric is the raw event count. The event:goal breakdown lists the site's configured goals with `visitors` (converters), `events` (conversions) and `conversion_rate` (percent). `events`/`conversion_rate` are breakdown-only (rejected on aggregate/time-series or a dimension that doesn't compute them). Units: visit_duration = average seconds per visit; bounce_rate = percent 0-100; views_per_visit = pageviews/visits ratio; visitors/pageviews/visits = counts.
top_pagesTop pages by visitors for a site. Returns {"results":[{"dimensions":{"event:page":"/path"},"metrics":{"pageviews":N,"visitors":N}}]}.
top_sourcesTop traffic sources by visitors for a site. Returns {"results":[{"dimensions":{"visit:source":"Google"},"metrics":{"visitors":N,"bounce_rate":percent0to100}}]}.
top_countriesTop countries by visitors for a site. Returns {"results":[{"dimensions":{"visit:country":"US"},"labels":{"visit:country":"United States"},"metrics":{"visitors":N}}]} — the dimension value is the ISO alpha-2 code (drops straight into a country filter); the display name is in labels.
top_custom_eventsTop custom events (data-statable-event) by count for a site — pageview/engagement excluded. Returns {"results":[{"dimensions":{"event:name":"Signup"},"metrics":{"events":N,"visitors":N}}]}, where events is the raw occurrence count and visitors is unique users who triggered it.
top_goalsThe site's configured goals ranked by conversions. Returns {"results":[{"dimensions":{"event:goal":"Signup"},"metrics":{"visitors":N,"events":N,"conversion_rate":percent0to100}}]}, where visitors is unique converters, events is total conversions, and conversion_rate is visitors as a percent of all visitors in the period.
list_funnelsList the conversion funnels configured for a site (discovery — get a funnel_id for funnel_report). Returns {"funnels":[{"id":45,"name":"Signup flow","scope":"visitor","steps_count":3}]}.
list_prop_keysList the custom-property keys a site has recorded (with the event each belongs to) — discovery for the event:props:<key> breakdown in query_stats. Returns {"props":[{"key":"plan","event":"Signup","count":N,"first_seen":"RFC3339"}]}.
funnel_reportRun a saved conversion funnel and return its per-step result (NON-tabular). Returns {"funnel":{"id","name","scope"},"entering":N,"all_visitors":N,"steps":[{"index":0,"name":"Visited /pricing","kind":"page","visitors":N,"conversion_rate":percent0to100,"dropoff":N}, ...]}. steps are ORDERED; conversion_rate is cumulative vs the first step (entering), NOT step-to-step; dropoff is visitors lost vs the previous step. Get funnel_id from list_funnels first.
current_visitorsRealtime count of unique visitors active in the last 5 minutes for a site. Returns {"site_id":123,"visitors":N}.
visitors_over_timeDaily visitors and pageviews time series for a site, in the site's own timezone. Returns {"results":[{"dimensions":{"time:day":"YYYY-MM-DD"},"metrics":{"visitors":N,"pageviews":N}}]}.