The Peeko command line

Read your analytics from the terminal and create a site without leaving it. One package, zero dependencies, the same data your dashboard shows.

Start here

Nothing to install. Node 20 or newer is the only requirement, and the package has no runtime dependencies at all.

npx @usepeeko/cli login
npx @usepeeko/cli init --name "Acme" --domain acme.com
npx @usepeeko/cli summary --site acme.com --range 30d

The package is @usepeeko/cli and the command it installs is peeko. To keep it around rather than fetching it each time, install it globally with npm install -g @usepeeko/cli and type peeko instead of npx @usepeeko/cli.

The command line has no server of its own. Every command is one call against the same MCP endpoint your agent uses, so anything the command line can read, an agent connected to Peeko can read the same day.

Getting a key

A key belongs to a person and reaches every site of every team they are in, re-checked on every call. It is shown once.

Mint one on the API keys card at usepeeko.com/settings. Then hand it to the command line, which reads it from standard input so a setup script can pipe it:

npx @usepeeko/cli login
echo "$PEEKO_KEY" | npx @usepeeko/cli login

The key is written to ~/.config/peeko/config.json with permissions 0600. XDG_CONFIG_HOME is honoured, because that is what ~/.config means. peeko logout deletes the file, and it does not revoke the key: that is the settings page.

Two environment variables override the stored file. login says so when the first one is set, rather than letting you debug a key the command line is not using.

VariableEffect
PEEKO_API_KEYUse this key and ignore the stored one. Intended for CI.
PEEKO_URLPoint at a different Peeko, for example a local dev server.

Commands

Every command maps to one MCP tool. The table uses your dashboard's plain language names; --json prints the tool's payload untouched, which keeps the original field names.

CommandToolPrints
loginnoneStores an API key.
logoutnoneForgets the stored key.
initcreate_siteThe snippet, an install prompt with real keys, the setup guide.
siteslist_sitesSite id, name, domain, owning organization.
summaryget_summaryThe six headline numbers, each with its change.
pagesget_top_pagesMost viewed pages.
sourcesget_sourcesReferring hosts. "Direct" means no referrer.
engagementget_engagementPages ranked by dwell.
liveget_live_nowWho is on the site right now. No range.
botsget_bot_summaryAI crawler traffic.

Options

OptionNotes
--site <id or domain>Optional when the key reaches exactly one site.
--range <key>today, 24h, 7d, 30d, 90d, all. Default 7d.
--jsonThe raw payload instead of a table.
--org <id>init only, and only when the key reaches more than one organization.
--name, --domaininit only. Prompted for when absent.
--help, -hThe command's own page, or the general one.
--version, -vThe version you are running.
--site is never guessed. With several sites and no --site, the command line lists them and stops. Picking the first would report a different site's numbers under the command you typed, and nothing in the output would say so. A domain costs one extra request, because the site list resolves it.

Creating a site

init makes the site, seeds its allowed origins with your domain and its www version, and prints everything you need to finish.

npx @usepeeko/cli init --name "Acme" --domain acme.com

It prints three things: the <script> tag, an installation prompt to paste into a coding agent, and the setup guide's address.

The prompt carries both real keys. The site key, so the agent writes a working snippet rather than a placeholder, and the API key the command line is holding, so the same agent can connect Peeko's MCP server and read the numbers back. Treat the printed prompt as a secret.

If your key reaches more than one organization, --org is required and the error lists the ids and names. Peeko will not choose: the wrong organization bills the wrong party and shows the site to the wrong people, and nothing would ever report it.

Exit codes

The split is by what you have to do, not by where the failure happened, so a wrapper can retry on 2 and must not retry on 1.

CodeMeaning
0The command succeeded.
1The command ran and failed. Running it again unchanged will fail again.
2The command could not be attempted: key rejected, rate limited, or no network.

A missing key and a rejected key give the same sentence and the same code, because the server cannot tell them apart either, and one problem described two ways reads as two problems.

Two things the output will not let you assume

Both are properties of how the data is collected, not gaps in the command line, so no flag turns them off.

A crawler's name is a claim

bots classifies from the user agent alone, and Peeko stores no IP address on the crawler path, so anything can send Googlebot's user agent and be reported as Google.

404s are not observable on every site

When bots says response status is not available, the missing pages list is empty because those responses are unknowable for that site, not because nothing 404'd. Reporting a status needs a framework hook that runs after the response exists. Some frameworks have one and Next's not-found boundary is not one, because it also renders for requests that resolve 200.

And crawler data needs Peeko's middleware package in the site's own server at all. Crawlers do not run JavaScript, so the tracker script is structurally blind to them.

Where to go next

Read your traffic from the terminal.

Get started