API & MCP

Command-line interface (CLI)

Install the HoneyLog CLI and read your traffic reports, bot activity and raw logs from a terminal or a script.

The HoneyLog CLI reads your traffic data from a terminal. It covers the same reports as the dashboard: traffic over time, top pages, bots and countries, and the individual requests. You can print them as a table, or pipe them into jq, a spreadsheet or a cron job.

It is a thin wrapper over the REST API. Every command calls one endpoint, the same one an MCP server tool calls. You sign in once with your browser, like an MCP connector, or give it a team API token. Either way it shares the same rate limit. Everything it does is read-only.

It is a single Python file with no dependencies. All it needs is Python 3.8 or newer, which macOS and most Linux distributions already include.

Installation

macOS and Linux

curl -fsSL https://honeylog.io/cli/honeylog.py --create-dirs -o ~/.local/bin/honeylog && chmod +x ~/.local/bin/honeylog

Then check it with honeylog --version. If the shell answers honeylog: command not found, then ~/.local/bin is not on your PATH. Add export PATH="$HOME/.local/bin:$PATH" to ~/.bashrc or ~/.zshrc and open a new terminal, or run the CLI as python3 ~/.local/bin/honeylog.

You can read the script in your browser before running it.

Windows

Invoke-WebRequest https://honeylog.io/cli/honeylog.py -OutFile honeylog.py
py honeylog.py --version

On Windows, write py honeylog.py wherever this page writes honeylog.

Updating

Run the installation command again. It replaces the file with the current version. honeylog --version tells you which version you have.

Setup

Sign in once with your browser:

honeylog login

The CLI opens HoneyLog in your browser. Sign in if you need to, choose the team the CLI should read, and click Allow access. Back in the terminal, the CLI confirms with logged in to https://honeylog.io until ….

You sign in once per computer, not once per terminal: the CLI saves the token, and every new terminal uses it. The login lasts 60 days. After that, the CLI tells you it has expired and you run honeylog login again. In the last week, it reminds you whenever you run a command in a terminal.

Only team owners and managers can sign in, the same people who can create API tokens. HoneyLog admins can also choose to read every site.

Where the login is saved

The token goes in credentials.json, in a folder only your user account can read:

System File
macOS and Linux ~/.config/honeylog/credentials.json, or $XDG_CONFIG_HOME/honeylog/credentials.json
Windows %APPDATA%\honeylog\credentials.json

HONEYLOG_CONFIG_DIR sets a different folder. In HoneyLog, the token appears in Settings → API Tokens, MCP & CLI as Honeylog CLI on {computer} (CLI, authorized by {you}), with its expiry date. Deleting it there signs that computer out.

Over SSH or on a server

Where the CLI cannot open a browser (over SSH, or on a machine without a display), it prints the sign-in address instead. Open it in a browser on any computer and allow access. The browser then lands on an http://127.0.0.1:… page that does not load. Copy that address from the address bar and paste it in the terminal. honeylog login --no-browser works this way on purpose.

Signing out

honeylog logout

This revokes the token in HoneyLog and deletes it from this computer. A new honeylog login also revokes the token it replaces, so each computer keeps one.

Scripts, cron jobs and CI

A login expires, and a CI runner has no browser. For anything that runs on its own, use a team API token instead:

  1. In HoneyLog, select the team you want to read, then open Settings → API Tokens, MCP & CLI.
  2. Create a token with sites.read for sites and custom views. If you also want networks, give it networks.read, which includes sites.read.
  3. Copy the token while the dialog is on screen.

Then give it to the CLI through the HONEYLOG_TOKEN environment variable, which takes precedence over the login. Reading it from a prompt keeps it out of your shell history. This works in bash and zsh:

export HONEYLOG_TOKEN
printf 'Honeylog API token: '; read -rs HONEYLOG_TOKEN; echo

In PowerShell 7.1 or newer:

$env:HONEYLOG_TOKEN = Read-Host "Honeylog API token" -MaskInput

The variable lasts until you close the terminal. In a CI system, store the token as a secret and expose it as HONEYLOG_TOKEN. A token created by connecting an MCP client works here too, since it is the same kind of team token.

Verify the installation

honeylog sites

If the login works, you get every site, custom view and network it can read, each with the wireable identifier the other commands need:

type     wireable                                      name                timezone
-------  --------------------------------------------  ------------------  -----------
site     site:9b2f6c1e-4d0a-4c55-8f0e-2a7d3b1c9e44     example.com         Europe/Rome
view     site:9b2f6c1e-4d0a-4c55-8f0e-2a7d3b1c9e44@12  example.com / Blog  Europe/Rome
network  network:4                                     All brands

If it doesn't, the CLI prints the reason and exits with a code that tells the cases apart:

Exit code Message What to check
2 not logged in Run honeylog login, or set HONEYLOG_TOKEN
3 the login … expired The 60 days are over: run honeylog login again
3 HTTP 401: Unauthenticated. The token was mistyped, or has been deleted. After a login, the CLI also says to run honeylog login again
3 HTTP 403: The v1 API requires a team API token. The token is a personal token, not a team token
3 HTTP 403: …subscription… or …blocked for your plan. The team's plan has no API access, or the subscription is inactive
8 cannot reach https://honeylog.io Network, proxy or firewall

Usage examples

# Find a site and its wireable
honeylog sites --search example.com

# The metric and dimension names that site accepts
honeylog fields site:SITE_UUID

# Pageviews and sessions, last 30 days vs the 30 days before
honeylog timeseries site:SITE_UUID --metrics views,sessions \
  --range last_30_days --compare-range previous_period

# Which bots came yesterday, with the totals over all of them
honeylog breakdown site:SITE_UUID --dimension bot_name \
  --range yesterday --with-totals

# Every URL GPTBot requested today, with its status, as CSV
honeylog --format csv raw-logs site:SITE_UUID --range today \
  --columns datetime_second,full_url,status_code \
  --where '[[["bot_name","=","GPTBot"]]]' --all > gptbot.csv

# Requests per minute over the last two hours
honeylog raw-logs-timeseries site:SITE_UUID --last 2h \
  --granularity 1m

Filters can come from a file (@file) or from standard input (-):

cat > ai-crawlers.json <<'EOF'
[[["bot_name", "IN", ["GPTBot", "ClaudeBot", "PerplexityBot"]]]]
EOF
honeylog breakdown site:SITE_UUID --range last_7_days \
  --where @ai-crawlers.json

When the output is piped, it is JSON by default, so it combines with jq:

# Top countries of the last 7 days, as tab-separated text
honeylog breakdown site:SITE_UUID --dimension country_code \
  --range last_7_days \
  | jq -r '.data[] | "\(.country_code)\t\(.views)"'

# One site group per command: loop to cover all your sites
for site in $(honeylog sites --type sites \
    | jq -r '.data.sites[].wireable'); do
  honeylog --format csv breakdown "$site" --dimension bot_name \
    --range yesterday > "bots-${site#site:}.csv"
done

Common options

Global options go before the command (honeylog --format csv breakdown …, not honeylog breakdown … --format csv):

Option What it does
--format table|json|jsonl|csv Output format. Defaults to table in a terminal and json when the output is piped
--token TOKEN An API token instead of the login. Prefer HONEYLOG_TOKEN: other users of the machine can see command-line flags in the process list
--base-url URL The HoneyLog address. Without it, the CLI uses HONEYLOG_BASE_URL, then the address of the last honeylog login, then https://honeylog.io
--timeout SECONDS Seconds to wait for each request. Defaults to 60
--retries N Retries after a rate limit or a network error. Defaults to 3
--no-retry Fail on the first rate limit or network error
-v, --verbose Print each request to stderr, and where the token comes from. The token itself is never printed
--version Print the CLI version

These options set the period on every command that reads data:

Option What it does
--range PERIOD A named period, resolved in the site's timezone each time the command runs, so a scheduled --range yesterday always means the day before. The report commands take yesterday, last_7_days, last_14_days, last_30_days, last_90_days, last_365_days, this_month, last_month, this_year and last_year. The raw-log commands take today, yesterday and last_7_days
--start, --end An exact window instead of --range, as Unix timestamps or dates such as 2026-10-01 or 2026-10-01T10:00:00Z, in the site's timezone
--compare-range PERIOD A comparison window relative to the main one: previous_period, previous_month or previous_year. Report commands only
--compare-start, --compare-end An exact comparison window, for dates --compare-range cannot express
--last DURATION The window up to now, such as 30m, 6h or 2d. Raw-log commands only

Dates and defaults in the API documentation explains each period, and the default window when you give none.

These options filter and page:

Option What it does
--where FILTER Filter on dimensions, as JSON, @file or - for standard input. The syntax is described under Filter expressions
--having FILTER Filter on metric values, such as [[["views",">",100]]]. Report commands only
--limit N, --offset N One page of rows
--all Follow the pages and return every row, up to --max-rows (10,000 by default). The window is fixed by the first page, so the rows don't shift while the clock moves

Every option also accepts the parameter's name in the API, with dashes: --where-expression, --having-expression and --order-direction work just like --where, --having and --order.

Commands

Besides login and logout, each command corresponds to one API endpoint and one MCP tool:

Command API endpoint MCP tool What it returns
sites GET /api/v1/sites honeylog_list_sites The sites, custom views and networks the token can read, with their wireable
fields WIREABLE GET /api/v1/{wireable}/fields honeylog_describe_fields The metrics and dimensions a site group accepts, or the raw-log columns
timeseries WIREABLE GET /api/v1/{wireable}/stats/views honeylog_traffic_timeseries Metrics over time, with an optional comparison period
breakdown WIREABLE GET /api/v1/{wireable}/stats/pages honeylog_traffic_breakdown Metrics per page, bot, country, referrer or any other dimension
raw-logs WIREABLE GET /api/v1/{wireable}/raw-logs honeylog_raw_logs The individual requests
raw-logs-timeseries WIREABLE GET /api/v1/{wireable}/raw-logs/timeseries honeylog_raw_logs_timeseries Requests per minute, hour or day

WIREABLE is a single site group, written exactly as sites prints it: site:{uuid}, site:{uuid}@{customViewId} for a custom view, or network:{id}. honeylog COMMAND --help lists every option of a command.

sites

--search TEXT keeps the site groups whose domain or name contains the text, or the site with that uuid. --type sites or --type networks lists only one kind. --team-id ID keeps a single team when the token reads more than one. A long list comes in pages. The CLI says when there are more entries, and --all fetches all of them.

fields

--source raw_logs lists the raw-log columns instead of the report metrics and dimensions. Read this list before naming a field in --metrics, --dimension, --columns or a filter: the API rejects names outside it, and the list differs between site groups.

timeseries

--metrics takes a comma-separated list from fields. It defaults to views,sessions,viewers,avg_time_on_page,bounce_rate_sessions. --granularity takes minute, hour, day, week, month or year, and defaults to day. In table, CSV and JSON Lines output each time bucket is one row, with one column per metric under its display name ("Pageviews"), and a Compared … column for each metric when you compare.

breakdown

--dimension sets what the rows are grouped by, pathname by default. --dimensions adds further dimensions to break each row down by. --metrics works as in timeseries. --sort takes one of the requested metrics or the dimension, and --sort-order takes asc or desc. --limit returns 20 rows unless you ask for up to 1,000.

--with-totals adds the metrics over every row that matches the filters, not only the rows shown: use them for shares and overall figures instead of summing a page. They appear under the table, and in meta.totals in JSON.

raw-logs

--columns picks the columns, from fields WIREABLE --source raw_logs. By default you get datetime_second,bot_name,country_code,full_url,full_user_agent. --order asc returns the oldest requests first.

Raw logs read short windows: the last 24 hours unless you ask for another, and at most 8 days. A page holds 20 rows unless --limit asks for up to 1,000, and paging stops at an offset of 10,000. --all uses the largest page the API allows. When paging stops at that limit, the CLI says so: narrow the window or add a filter to reach the rest. For counts and top lists, breakdown is cheaper and complete.

raw-logs-timeseries

This command has a short alias, hits. It returns one row per bucket, with bucket (its start, in the site's timezone), timestamp and hits. Buckets without traffic are included with 0. --granularity takes 1m on windows up to 4 hours, 5m up to 24 hours, 15m up to 72 hours, and 1h or 1d on any window. Without it you get the finest size the window allows. Filters use the raw-log field names. In JSON output, meta.total_hits is the sum over the window.

Output formats

  • table is meant for reading. Long cells are cut at 60 characters, so use another format for full URLs and user agents.
  • json returns the API's own answer, {"data": …, "meta": …}, unchanged. With --all, data holds the rows of every page.
  • jsonl prints one JSON object per row, and csv a header followed by one line per row. With --all, both print each page as it arrives.

Messages, warnings and errors go to stderr, so they never end up in a redirected file.

Rate limits and retries

Every command spends one call from your plan's API allowance per request: the same allowance as the REST API and the MCP server, not a separate one. With --all each page is one call, and raw-logs --all makes one more at the start, to ask for the largest page size.

When the per-minute limit is reached, the CLI waits as long as the API tells it to and tries again, up to 3 times and for at most 5 minutes in total. --retries changes the number of attempts and --no-retry turns retries off.

Exit codes

Code Meaning
0 Success
1 Unexpected error
2 Wrong usage: not logged in and no token, an unknown option or a malformed WIREABLE or filter. Nothing was sent
3 Token rejected, or no API access (HTTP 401 or 403). Also an expired login, or a honeylog login that did not complete
4 No such site, custom view or network (HTTP 404)
5 Invalid request (HTTP 422), for example an unknown metric. The errors are printed per field
6 Still rate limited after the retries (HTTP 429)
7 Server error (HTTP 5xx), including 504 for a query that took too long: narrow the window or add filters
8 HoneyLog could not be reached

Good to know

  • Read-only: the CLI cannot change a setting, delete data or touch your billing, whatever the token carries.
  • One site group per command: every request reads a single site, custom view or network. To cover several, loop over the output of sites.
  • Dates are interpreted in the site's timezone, which every JSON answer repeats in meta.timezone, so the figures match the dashboard.
  • The token stays private: -v prints each request without it, the saved login is readable only by your user account, and the CLI refuses to send the token over plain http except to localhost.