Agent & CLI API
Lightweight Analytics is agent-first: everything you can do in the dashboard (register sites, pull stats, manage goals) is available through a small JSON API designed to be driven by AI agents (Claude, Codex, or your favorite) and our upcoming CLI.
Working with an agent? Point it at the raw markdown version of this page: https://lightweightanalytics.com/docs/api.md, or at https://lightweightanalytics.com/llms.txt. Both are plain text, no HTML to scrape.
Base URL & authentication
https://lightweightanalytics.com/api/v1
Create an API key at Settings → API keys and send it on every request:
curl -H "Authorization: Bearer la_YOUR_KEY" \
https://lightweightanalytics.com/api/v1/me
X-Api-Key: la_YOUR_KEY works too if setting an Authorization header is awkward. Keys act on your whole account, can be named (one per agent/machine is a good habit), and can be revoked instantly from the same page. We store only a hash of the key, so copy it when it's shown.
In the examples below, export it once:
export LA_API_KEY="la_YOUR_KEY"
Rate limits & errors
Limits are generous: 600 requests/minute per key (bursts up to 120 are fine). Over the limit you get 429 with a Retry-After header. Wait a second and retry.
Every error has the same flat shape:
{ "error": "not_found", "message": "No site with that id belongs to this account. GET /api/v1/sites lists yours." }
| Status | error codes you'll see |
|---|---|
| 400 | domain_required, invalid_domain, invalid_range, invalid_bucket, invalid_breakdown, confirmation_required, invalid_goal_name, invalid_goal_type, invalid_match_type, pattern_required, invalid_value |
| 401 | missing_api_key, invalid_api_key |
| 403 | subscription_required, site_limit_reached |
| 404 | not_found |
| 409 | domain_taken, goal_exists |
| 429 | rate_limited |
Date ranges
Stats endpoints accept either a preset range or explicit dates. All dates are UTC.
?range=one oftoday,yesterday,7d,30d(default),90d,365d,thisMonth,lastMonth,thisYear,all?from=2026-08-01&to=2026-08-11: explicit dates (tois inclusive, whole day)?segment=dev: switch from production traffic to non-production traffic (localhost, staging hosts, etc.)
Account
GET /me
Sanity-check your key and see account status.
curl -H "Authorization: Bearer $LA_API_KEY" \
https://lightweightanalytics.com/api/v1/me
{
"email": "[email protected]",
"name": "Ada Lovelace",
"keyName": "Claude on my laptop",
"keyPrefix": "la_9f2c4e01",
"plan": "annual",
"subscriptionStatus": "active",
"sites": 3,
"siteLimit": 100,
"apiCallsToday": 42,
"rateLimit": "600 requests/minute per key (burst 120)"
}
Sites
GET /sites
List every site on the account. Add ?includeStats=true for all-time pageviews/visitors (slightly slower).
curl -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites?includeStats=true"
{
"sites": [
{
"siteId": "11111111-2222-3333-4444-555555555555",
"domain": "example.com",
"name": "My blog",
"useVisitorCookie": true,
"useLivePings": false,
"isFavorite": true,
"publicDashboardEnabled": false,
"goals": 2,
"allTimePageviews": 48210,
"allTimeVisitors": 19877
}
],
"count": 1,
"siteLimit": 100
}
POST /sites (register a site)
Only domain is required. The response includes the tracking snippet to paste into the site's <head>.
curl -X POST -H "Authorization: Bearer $LA_API_KEY" -H "Content-Type: application/json" \
-d '{"domain": "example.com", "name": "My blog"}' \
https://lightweightanalytics.com/api/v1/sites
{
"siteId": "11111111-2222-3333-4444-555555555555",
"domain": "example.com",
"name": "My blog",
"useVisitorCookie": true,
"useLivePings": false,
"isFavorite": false,
"publicDashboardEnabled": false,
"trackingSnippet": "<script defer src=\"https://cdn.lightweightanalytics.com/script.min.js\" data-siteid=\"11111111-2222-3333-4444-555555555555\" data-c_mode=\"true\" data-l_ping=\"false\"></script>",
"dashboardUrl": "https://lightweightanalytics.com/dashboard?siteId=11111111-2222-3333-4444-555555555555"
}
Optional body fields: useVisitorCookie (default true, first-party visitor id for accurate uniques) and useLivePings (default false, 30-second keep-alive for the live counter).
GET /sites/
Site details, settings, all-time stats, goal count and the current tracking snippet.
curl -H "Authorization: Bearer $LA_API_KEY" \
https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555
PATCH /sites/
Send only the fields you want to change: name, useVisitorCookie, useLivePings.
curl -X PATCH -H "Authorization: Bearer $LA_API_KEY" -H "Content-Type: application/json" \
-d '{"useLivePings": true}' \
https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555
Changing tracking options changes the snippet. The response includes the updated one to re-paste.
DELETE /sites/
Permanently deletes the site and all its analytics data. As a guard you must echo the site's domain in confirm:
curl -X DELETE -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555?confirm=example.com"
{ "deleted": true, "siteId": "11111111-2222-3333-4444-555555555555", "domain": "example.com" }
Stats
GET /sites/
The headline numbers for a period, the same as the dashboard's top row.
curl -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/stats/summary?range=7d"
{
"siteId": "11111111-2222-3333-4444-555555555555",
"domain": "example.com",
"from": "2026-08-05T00:00:00Z",
"to": "2026-08-11T23:59:59.9999999Z",
"pageviews": 4821,
"visitors": 1977,
"sessions": 2410,
"bounceRate": 0.44,
"avgSessionSeconds": 74.2,
"pagesPerSession": 2.0,
"newVisitors": 1544,
"returningVisitors": 433,
"liveVisitors": 3
}
GET /sites/
Pageviews and visitors over time, zero-filled so every bucket is present. bucket is auto (the default: hourly for ranges up to 2 days, daily otherwise), hour or day.
curl -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/stats/timeseries?range=7d"
{
"granularity": "day",
"points": [
{ "date": "2026-08-05", "pageviews": 512, "visitors": 231 },
{ "date": "2026-08-06", "pageviews": 748, "visitors": 342 }
]
}
GET /sites/
Top-N tables, one endpoint for every dimension. by is one of:
pages, referrers, countries, browsers, devices, os, languages, resolutions, hosts, campaigns. limit defaults to 10 (max 100).
curl -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/stats/breakdown?by=referrers&range=30d&limit=5"
{
"by": "referrers",
"items": [
{ "key": "news.ycombinator.com", "count": 1204 },
{ "key": "google.com", "count": 892 },
{ "key": "direct", "count": 611 }
]
}
by=campaigns returns your UTM campaign traffic (always the last 30 days) with a different item shape:
{
"by": "campaigns",
"items": [
{ "source": "newsletter", "medium": "email", "campaign": "launch-week", "count": 302 }
]
}
GET /sites/
Visitors active in the last 5 minutes. Not cached, so poll it as often as you like within the rate limit.
curl -H "Authorization: Bearer $LA_API_KEY" \
https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/stats/live
{ "siteId": "11111111-2222-3333-4444-555555555555", "liveVisitors": 3 }
Goals & campaigns
Goals track conversions (sign-ups, purchases, clicks). Three types:
manual: you fire the event yourself (JSwindow.la('event', 'Signup')or the collect endpoint below)click: fired declaratively via adata-la-goalattribute on an elementurl: fired automatically when a visited URL matchespattern(matchType:contains,startsorregex)
Values are integer cents to avoid floating point.
GET /sites/
curl -H "Authorization: Bearer $LA_API_KEY" \
https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/goals
{
"goals": [
{ "goalId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "name": "Signup", "type": "manual", "matchType": null, "pattern": null, "defaultValueCents": null },
{ "goalId": "ffffffff-1111-2222-3333-444444444444", "name": "Checkout", "type": "url", "matchType": "contains", "pattern": "/thank-you", "defaultValueCents": 2900 }
]
}
POST /sites/
curl -X POST -H "Authorization: Bearer $LA_API_KEY" -H "Content-Type: application/json" \
-d '{"name": "Checkout", "type": "url", "matchType": "contains", "pattern": "/thank-you", "defaultValueCents": 2900}' \
https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/goals
PATCH /goals/
Send only what changes, e.g. adjust the default conversion value for a campaign push:
curl -X PATCH -H "Authorization: Bearer $LA_API_KEY" -H "Content-Type: application/json" \
-d '{"defaultValueCents": 4900}' \
https://lightweightanalytics.com/api/v1/goals/ffffffff-1111-2222-3333-444444444444
DELETE /goals/
Deletes the goal definition and (by default) its recorded conversions. Keep the historical events with ?purgeEvents=false.
curl -X DELETE -H "Authorization: Bearer $LA_API_KEY" \
https://lightweightanalytics.com/api/v1/goals/ffffffff-1111-2222-3333-444444444444
GET /sites/
Conversions and revenue per goal for a period.
curl -H "Authorization: Bearer $LA_API_KEY" \
"https://lightweightanalytics.com/api/v1/sites/11111111-2222-3333-4444-555555555555/goals/stats?range=30d"
{
"goals": [
{ "goalId": "ffffffff-1111-2222-3333-444444444444", "name": "Checkout", "type": "url", "conversions": 41, "revenue": 1189.00, "avgValue": 29.00 }
]
}
Recording pageviews & events
Normal pageviews need no API at all. The tracking snippet handles them. To record custom events or conversions from a server, script or agent, POST to the public collect endpoint (no API key; it validates the siteId):
curl -X POST -H "Content-Type: application/json" \
-d '{"siteId": "11111111-2222-3333-4444-555555555555", "url": "/api-event", "event": "Signup", "value": 2900}' \
https://analytics.lightweightanalytics.com/collect
Returns 202 on success. event names (1-40 chars, letters/numbers/spaces/-/_) that don't exist yet are auto-created as manual goals, so they show up in the dashboard immediately. value is in cents.
Notes for agents
- Fetch this document any time at
https://lightweightanalytics.com/docs/api.md(markdown). The short index is athttps://lightweightanalytics.com/llms.txt. GET /meis the cheapest way to verify a key before doing real work.- Site and goal ids are GUIDs; discover them via
GET /sitesandGET /sites/{siteId}/goalsrather than guessing. - Deletes are destructive and require explicit confirmation parameters by design. Don't retry a
confirmation_requirederror without checking with your human. - A native CLI (macOS & Windows) that wraps this API is coming soon; everything it will do is already possible with plain HTTP.