⚡

HotKey Analytics

Sign in to your dashboard

Open your authenticator app and enter the 6-digit code.

HotKey Media · internal analytics

⚡ HotKey
Analytics
Monetization
Operations

Dashboard

⬇ Content CSV

Sites

SiteVisitors

Top Sources

SourceVisitors

Top Pages

PageVisitors

Devices

DeviceVisitors

Widget Placements

PlacementRevenue
Visitors (30 min) —
Pageviews (30 min) —
last 30 min
PageVisitors

Sources

last 30 min
SourceVisitors

Devices

last 30 min
DeviceVisitors

🖱 Widget clicks

last 30 min — these are the money moments

Monetization Funnel

where visitors drop out between landing and earning

🚫 Traffic, no widget seen

widget missing or below the fold
PageViews

👀 Seen, never clicked

weak placement or listings
PageImpressions

🖱 Clicked, no revenue yet

leads not converting on Esy's side
PageClicks

Movers vs previous period

📈 Gainers

📉 Losers

Page decay

second half of the selected range vs the first half — money pages quietly sliding

💸 Revenue decaying

📉 Traffic decaying

Engaged vs. Earns

each bubble is a page (size = visitors). Top-left = deeply read but under-monetized — your best widget-placement opportunities.

Content Performance

every page's traffic, engagement, and revenue side by side — click a column to sort. RPM = revenue per 1,000 pageviews.

Web Vitals

real-visitor page speed (p75, Google's threshold percentile). LCP = load, CLS = layout shift, INP = responsiveness. Green meets Google's "good" bar; red is "poor" and can cost rankings.

Improve — striking-distance queries

search queries ranked 4–20 on pages that already earn revenue. These are the cheapest wins: better internal links, refreshed content, or a dedicated page pushes them to page one.

Write — under-built high-RPM trades

trades that earn far more per pageview than the portfolio average but have thin content. Headroom = monthly $ if traffic grew to the portfolio's 75th percentile at current RPM.

Refresh — decaying earners

pages whose revenue or traffic dropped sharply between the first and second half of the period. Rescue these before rankings follow the trend.

Geo & Trades Performance

Rolled up from page URL slugs across every site — pageviews, widget CTR, conversions, revenue, and RPM per dimension. Click a column header to sort.

⬇ Export CSV

Search Performance

Search clicks & impressions joined to revenue by landing page. Revenue / query distributes each page's conversion revenue across the queries that drove its clicks — which search terms actually feed the money pages. Import data in Settings → Data & Integrations.

No search data yet — import it in Settings → Data & Integrations.

A/B Experiments

Variant performance from tracker-tagged widget events. Configure with data-experiments="listing_copy:2" on the tracker tag, and vary your page by window.hkaVariant('listing_copy'). Significance is a two-proportion z-test vs the control variant.

No experiments running. Add data-experiments to the tracker tag to start one.

Tracking Health

Per-site last-seen pageview, tracker version, and 24h volume. A site going silent during active hours is the earliest sign of a broken tracker (the worst kind of outage — silent data loss).

⚠️ Alerts

Conversion data freshness

Backups & delivery

⬇ Download backup (.ndjson.gz)

Esy Widget Performance

impression → view → click funnel from the tracker · conversions & revenue from your CSVs, joined by unique placement IDs

Click economics by widget type

listing (request-info) vs search (find-schools) — which widget kind earns the clicks
⬇ Export conversions CSV

This month's pace

month-to-date vs the same point last month, with a straight-line projection to month-end

Revenue Attribution

⏱ Time to convert

widget click → Esy payout lag

🧭 Path to conversion

page journey of recent converters (exact-attributed)

Manual revenue adjustments

For revenue with no per-conversion CSV row — e.g. Esy's "3rd Party Aggregators" line. Added to the chosen day's totals; re-entering the same site + date + label updates the amount.

Recent Conversions

Accounting — estimated vs. paid

Estimated = revenue we tracked (conversions). Paid = what you enter from your Esy statement. The difference surfaces scrubbing / bad-lead clawbacks. Negative = paid less than tracked. Click a Paid cell to edit.

AI Briefs

compiles the selected sites + period into a markdown report with an analysis prompt — copy it into Claude

      

Your account

Scan this with Google Authenticator, 1Password, or Authy, then enter the 6-digit code to turn on two-factor:

Or enter manually:

Users

Sites

1 — Install the tracker (once per site, in <head>)


        

2 — Esy widget codes (no changes needed)

The tracker intercepts ESY.Widget() and rewrites placement into a unique tracking ID per impression (e.g. school_listing--hk3f9x2k1q) before the widget loads. Esy echoes that ID back in the conversion CSV, so each uploaded conversion joins to the exact impression — page, device, browser, and traffic source. Your existing widget codes keep their simple placement labels:


          

Verify on one page that the widget still renders with the rewritten placement (it should — Esy treats placement as a free-form label). Kill switch if ever needed: add data-esy-placement="off" to the tracker script tag. Run an A/B test by adding data-experiments="listing_copy:2" and varying your page by window.hkaVariant('listing_copy').

Import history from Plausible

Pulls day × page × source × device traffic from the Plausible Stats API into this dashboard as historical events. Then upload your old conversion CSVs in the Revenue tab — they'll attribute by page URL + date against the imported traffic. Safe to re-run (a range re-import replaces itself). Get an API key at plausible.io → Settings → API Keys.

Search Console (revenue per query)

Pulls day × page × query from Google Search Console and joins it to revenue by landing page, so you can see which search terms feed the money pages.

API (recommended): create a Google Cloud service account with the Search Console API enabled, add its email as a Full user on the Search Console property, then paste its JSON key. Property URL looks like sc-domain:gotradeschools.com (Domain property) or https://gotradeschools.com/ (URL-prefix).

CSV fallback: Search Console → Performance → Export → Queries.csv or Pages.csv, then upload here (powers top-queries / top-pages, not revenue-per-query).

Bing Webmaster Tools (revenue per query)

Bing is the bulk of these sites' search traffic. Pulls Bing's keywords, pages, and positions and joins them to revenue by landing page — shown under Search → Bing. Just an API key: in Bing Webmaster Tools → Settings → API access → generate an API key. The site URL is the verified property (e.g. https://gotradeschools.com). Bing returns a trailing snapshot, so re-importing replaces it.

Automated Esy import

Logs into Esy hourly, downloads today's conversion CSV, and imports it automatically (dedupe-safe, like a manual upload). No more downloading by hand. To enable, set two Railway service variables on the app service, then redeploy:

railway variables --service app \
  --set "ESY_EMAIL=you@example.com" \
  --set "ESY_PASSWORD=your-esy-password"

Optional: ESY_IMPORT_HOURS (default 1). Set it to 0 for manual only — no hourly auto-pull, just the button below (and the one on the Revenue page). Credentials are stored encrypted by Railway and only used server-side to fetch your own data.

CSV Import History

Read-only API tokens

For spreadsheets, automation, and AI. A token allows GET requests to /api/* only (never writes). Send it as Authorization: Bearer <token> or append ?token=…. The token is shown once at creation — copy it then.

AI access (MCP): point Claude at <this app>/mcp with the token as a bearer header — e.g. claude mcp add hotkey-analytics --transport http https://…/mcp --header "Authorization: Bearer hka_…". Claude then gets 18 read-only tools (stats, revenue, funnel, search, vitals, …).

Bot cleanup

Bots are blocked two ways automatically: data-center IPs are rejected at ingest, and an auto-sweep every 20 minutes removes burst-pattern bot visitors (groups of direct, no-engagement visitors landing in the same minute). This button runs the same sweep manually over the selected site + date range — useful for cleaning up older data.