MCP

Peeko as tools for your agent. One endpoint, one bearer key, and the same reads the dashboard makes.

Markdown

Peeko hosts an MCP server, so the agent that built your site can also answer questions about the traffic it brought in. One endpoint, one key, and the same site scoped reads the dashboard makes.

https://usepeeko.com/api/mcp

It is read only except for one tool that creates a site, and it is scoped to your own data by construction: every call goes through the same site scoped queries the dashboard uses, so a key can only ever read its own team's sites, never raw SQL, and never a visitor's address.

No account yet? Sign up from your agent

You do not need an account, or a key, to connect. Point your agent at the same address with no header and it gets exactly one tool, sign_up.

claude mcp add --transport http peeko https://usepeeko.com/api/mcp
codex mcp add peeko --url https://usepeeko.com/api/mcp
gemini mcp add --transport http peeko https://usepeeko.com/api/mcp

Then ask it: "Use the peeko MCP server to send a Peeko sign in link to my email address."

You finish in a browser. Opening the emailed link is what creates the account, or signs you in, because one link does both. It expires in 15 minutes and works once. Choosing a size and paying happen on the web. An agent never touches checkout, and this tool never returns a session, a token or a key.

Its answer is the same sentence whether or not the address already has an account. That is deliberate, so nobody can use this endpoint to discover who has a Peeko account. It is rate limited per address and per network, so asking repeatedly will not produce more mail.

Once you are signed up and subscribed, come back for a key. The analytics tools stay invisible until you reconnect with one, and the sign up tool disappears from the list once you do.

Get a key first

The API keys card under Your account at usepeeko.com/settings mints one. Name the key, create it, and copy either the raw key or the ready made connect command from the panel that appears once. The same card lists your keys with their prefix, creation date and last use. Rotate replaces a key with a fresh one under the same name and shows the new secret once, and Revoke deletes it. Both take effect immediately, and nothing warns a client that was using the old key.

Send the key as a bearer token, never as a session cookie.

Authorization: Bearer mcpk_your_key_here

A key belongs to a person and sites belong to a team, so a key reaches every site of every team you are a member of while that team's plan is active, and no more. Both are re-checked on every call rather than baked into the key, so removing someone closes their access at the same moment it closes their dashboard, and a team whose subscription lapses disappears from the key until it is back.

Connect a client

Every client below reads the same endpoint. Two of them take a command, the rest take a config file.

Claude Code

claude mcp add --transport http peeko https://usepeeko.com/api/mcp --header "Authorization: Bearer mcpk_your_key_here"

claude mcp list should then show peeko as connected. The tools load at the start of a new session, or check /mcp in the one you are in.

Codex

codex mcp add peeko --url https://usepeeko.com/api/mcp --header "Authorization: Bearer mcpk_your_key_here"

Claude Desktop, Cursor, or any client with a config file

An HTTP server with a header. Cursor reads ~/.cursor/mcp.json for every project, or .cursor/mcp.json for one.

{
  "mcpServers": {
    "peeko": {
      "type": "http",
      "url": "https://usepeeko.com/api/mcp",
      "headers": { "Authorization": "Bearer mcpk_your_key_here" }
    }
  }
}

Then ask the assistant to list your sites and go from there. Call the explain_metrics prompt for exact metric definitions, including the one that catches people out: visitors are distinct daily hashes, so a multi day total sums daily uniques.

Reading the answers

A few payload conventions are worth knowing before you ask an agent to reason about the numbers.

  • Money is in minor units. 499900 with currency USD is $4,999. shownMinor is the one to quote, and kind says whether it came from Stripe or from the site's own report. The two are never added together.
  • Search CTR is a fraction between 0 and 1, so 0.042 is 4.2 percent, and position is an average rank where lower is better.
  • The two search tools read either engine. provider picks google or bing, and the response says which one answered. Left out, it is Google when the site is linked to a Search Console property and Bing when it is linked only to a Bing site.
  • A search window never lines up with get_summary's range. Google publishes about three days late, Bing publishes on no schedule at all, and both report in their own Pacific days. Every search response carries the exact dates it covers. Bing's query rows are weekly buckets dated to a Friday, so a window shorter than a week can hold none, and position then comes back null rather than zero.
  • Ranges follow the site's own time zone, reported as timeZone on every analytics range object. A search range carries no timeZone at all, deliberately, because search days are the engine's own Pacific days. A custom window is written YYYY-MM-DD..YYYY-MM-DD, covers at most 731 days, cannot end in the future, and includes both days.

Tool reference

Every tool the server advertises to a key, with the input schema a client sees. Plus one prompt, explain_metrics, defining every metric and the privacy model.

list_sites

List the sites you can read (id, name, domain, emoji). Call this first, because every other tool needs a site id.

Takes no input.

create_site

Create a new site in Peeko and get the tracking snippet for it. Returns the site id, its public site key, and a ready-to-use prompt describing exactly what to add to the site's HTML. If your key reaches more than one organization you must pass org (call list_sites to see the orgId of existing sites).

InputTypeRequiredWhat it is
namestringyesWhat to call the site in Peeko, e.g. 'Acme marketing'.
domainstringyesThe site's domain, without a scheme or path, e.g. 'acme.com'.
orgstringnoOrganization id to create the site in. Required only when this key reaches more than one.

get_summary

Headline metrics for a site + range (+ optional filters): views, visitors, sessions, bounce rate, pages/session, avg duration, plus the previous window (for deltas) and returning-vs-new. previous is null for the all range and when the previous window is not a meaningful baseline: the site had no data before it began, or it holds fewer than 10 people.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
filtersobject with page, source, country, device, browser, browser_version, os, os_version, screen, viewport, utm_source, utm_medium, utm_campaignnoOptional dimension filters that narrow every metric (e.g. { country: 'US', device: 'mobile', page: '/pricing' }).

get_top_pages

Most-viewed pages for a site + range (+ optional filters). Each row: { name: path, count: views, pct: share of window views }. Returns 10 rows unless you pass limit (1 to 100).

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
filtersobject with page, source, country, device, browser, browser_version, os, os_version, screen, viewport, utm_source, utm_medium, utm_campaignnoOptional dimension filters that narrow every metric (e.g. { country: 'US', device: 'mobile', page: '/pricing' }).
limitintegernoHow many rows to return, 1 to 100. Defaults to 10.

get_sources

Referring sources (traffic by host) for a site + range (+ optional filters). 'Direct' means no referrer. Each row: { name: source, count: views, pct }. Returns 10 rows unless you pass limit (1 to 100).

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
filtersobject with page, source, country, device, browser, browser_version, os, os_version, screen, viewport, utm_source, utm_medium, utm_campaignnoOptional dimension filters that narrow every metric (e.g. { country: 'US', device: 'mobile', page: '/pricing' }).
limitintegernoHow many rows to return, 1 to 100. Defaults to 10.

get_engagement

Pages ranked by how long visitors stay, which answers 'which pages hold attention longest'. Each row: { path, avgSeconds, transitions }.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
filtersobject with page, source, country, device, browser, browser_version, os, os_version, screen, viewport, utm_source, utm_medium, utm_campaignnoOptional dimension filters that narrow every metric (e.g. { country: 'US', device: 'mobile', page: '/pricing' }).

get_live_now

Who is on the site right now: visitors active in the last 2 minutes: online count, live countries, and recent per-visitor detail (page, country, device, source). online always counts everyone, including the owner's own browsers (tagged with ?me=1, flagged self on a visitor row); others is the same cohort without them, and countSelf says which of the two this site's dashboard shows.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).

get_bot_summary

AI-crawler traffic for a site + range (server-detected, separate from human analytics): totals, 404 count, and breakdowns by category / provider / agent / most-crawled pages / most-requested-but-missing pages. Also returns answers, which are recent moments an assistant fetched a page WHILE ANSWERING SOMEONE (the question itself is never observable), and referrals, humans who clicked through from an AI product. Pass provider (e.g. "OpenAI", "Anthropic") to narrow every field to one company. ⚠ Check responseStatusAvailable before saying anything about 404s: it is false when the site's install cannot observe response codes (its middleware sees the request, not the response), and then missingHits is null and missing is empty because 404s are UNKNOWABLE for that site, not because there were none. Bot identity comes from the user-agent alone and is never verified, so a crawler's name is a claim, not a fact.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
providerstringnoNarrow to one provider, e.g. OpenAI, Anthropic, Google, Perplexity. Case does not matter, and a name Peeko does not know is an error naming the ones it does.

list_goals

The site's goals with their numbers for a range. A goal is something the owner decided counts as a win: a page reached, a click, or a form sent. Each returns its count (how many times it happened) and its conversion (the share of people in the range who did it at least once). previous is null for the all range and when the previous window is not a meaningful baseline: the site had no data before it began, or it holds fewer than 10 people. Archived goals are not included.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.

get_goal

One goal by id, with its definition and its numbers for a range, including the same previous window on the same rule: null for the all range and when that window is not a meaningful baseline (the site had no data before it began, or it holds fewer than 10 people). Use list_goals first to get goal ids.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
goalstringyesGoal id (from list_goals).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.

list_funnels

The site's funnels: an ordered set of pages or goals people are counted through, inside a completion window. Returns each funnel's steps, its step-1 people and its end-to-end conversion for the range.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.

get_funnel

One funnel by id, step by step: people at each step, the share of step 1 that reached it, and how many days the computation actually covered (a funnel is capped at the smaller of 90 days and the site's retention window, and counts fewer people than really converted over a long one). Use list_funnels first to get funnel ids.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
funnelstringyesFunnel id (from list_funnels).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.

get_revenue

Money for a range, always split by kind. All amounts are in the currency's MINOR unit (cents for USD, whole yen for JPY), so 499900 with currency USD is $4,999. verifiedMinor is what Stripe confirmed, reportedMinor is what the site's own page reported, and shownMinor is the one to quote: the verified sum when the range holds any verified sale, the reported sum otherwise, with kind saying which. Also returns revenue by source and by page, a series per bucket, and totals in any other currencies, which are never converted. A site with no sales returns zeros, empty arrays and a null currency rather than a different shape.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.

get_search_summary

Search engine headline numbers for a window: clicks, impressions, CTR and average position, with the immediately preceding equal window to compare against. Google Search Console and Bing Webmaster Tools both answer here, chosen by the provider argument; omitted, it is Google when the site is linked to a Search Console property and Bing when it is linked only to a Bing site, and the response reports which one in provider. CTR is a FRACTION between 0 and 1, so 0.042 is 4.2 percent. Position is an average rank where LOWER IS BETTER, so a move from 12 to 7 is an improvement. Google publishes this data about three days late, so the window ends three days ago; Bing publishes on no schedule at all, so its window ends on the latest day Bing has actually reported, which may be several days back. Either way window gives the exact dates covered and they are Pacific days, not the site's own: do not assume the window lines up with get_summary's range. FOR BING, POSITION IS THE ONE NUMBER THAT IS NOT DAILY: Bing reports no rank with its totals, so clicks, impressions and CTR are daily figures while position is the impression-weighted mean of the WEEKLY query buckets the window covers, each dated to the Friday its week ended on. A window shorter than a week may hold no weekly bucket, in which case position is null and the query list is empty; that is an absence of rank data for those days, not a rank of zero and not a claim about the site. today and 24h are served as 7d because no data that recent exists, and range reports what was actually served. previous is null for the all range, which has nothing before it, and when the previous window is not a meaningful baseline: it holds no impressions, or Peeko's data for that provider does not reach back through the whole of it. Every number is for the one site named by the site argument. A site that is not linked to the provider asked for is an error, not zeros, and so is a site linked to neither. A site whose first sync has not completed is an error, not zeros. When the last refresh from the provider failed, the response carries stale: true and lastError, and the numbers are the last ones that landed.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
providerone of google, bingnoWhich search engine: google or bing. Omitted, this is Google when the site is linked to a Search Console property and Bing when it is linked only to a Bing site. The response says which one answered.

get_search_queries

The search queries that brought people to a site, in one of four views, from Google Search Console or Bing Webmaster Tools. top is the biggest by clicks. winners and losers are the queries that gained and lost the most clicks against the previous equal window. opportunities are queries ranking between 8 and 20 with at least 50 impressions: real demand just off the first page. Every row carries clicks, impressions, CTR as a FRACTION between 0 and 1 (0.042 is 4.2 percent), average position where LOWER IS BETTER, and deltaClicks, the clicks gained or lost against the previous window, which is null on EVERY row whenever that window is not a baseline: the all range has none, and otherwise it may hold no impressions or Peeko's data for that provider may not reach back through the whole of it. provider picks the engine and the response reports which one answered; omitted, it is Google when the site is linked to a Search Console property and Bing when it is linked only to a Bing site. Google drops rare queries it cannot anonymize, so these rows always sum to LESS than get_search_summary's totals for the same window, and the difference is not missing data. BING'S QUERY ROWS ARE WEEKLY BUCKETS, each dated to the Friday its week ended on, while its totals are daily: a 7d window therefore holds ONE bucket and compares it against ONE bucket, so a short range gives few rows and small movements by design. The Google window ends about three days ago and the Bing window ends on the latest day Bing has reported; window gives the dates either way. A window shorter than a week may hold no weekly bucket, in which case position is null and the query list is empty: no Bing rows exist for those days, which is not the same as the site having no queries. Every row is for the one site named by the site argument, and range reports the window actually served, which is 7d when you ask for today or 24h. winners and losers are an error whenever there is no window worth comparing against: the all range, which has nothing before it, and a previous window that is not a meaningful baseline, meaning it holds no impressions or Peeko's data does not reach back through the whole of it. The Search tab hides the same two lists in the same two cases. A site whose first sync has not completed is an error, not zeros. When the last refresh from the provider failed, the response carries stale: true and lastError, and the numbers are the last ones that landed.

InputTypeRequiredWhat it is
sitestringyesSite id (from list_sites).
rangeone of today, 24h, 7d, 30d, 90d, all or string matching ^(\d{4})-(\d{2})-(\d{2})\.\.(\d{4})-(\d{2})-(\d{2})$noTime window. One of: today, 24h, 7d, 30d, 90d, all, or a custom window as YYYY-MM-DD..YYYY-MM-DD (calendar days in the site's time zone, at most two years, not in the future). Default 7d.
providerone of google, bingnoWhich search engine: google or bing. Omitted, this is Google when the site is linked to a Search Console property and Bing when it is linked only to a Bing site. The response says which one answered.
viewone of top, winners, losers, opportunitiesnoWhich view. One of: top, winners, losers, opportunities. Default top.
limitintegernoHow many rows to return, at most 100. Default 20.

Next

On this page