A PostHog alternative when all you want is an events API
If nobody on your team opens the analytics UI, you are paying for a product you don't use. What an events API gives you, and what you lose without the dashboard.
PostHog and Mixpanel are product-analytics products: a UI where a product manager builds a funnel, session replay, feature flags, dashboards people share. If your team uses those things, they’re worth the money. If your events go in and nothing ever comes out except a weekly number in a script, you’re paying for an interface nobody opens — and Infrai’s analytics namespace is eight endpoints on the same key as the rest of your stack.
That’s the whole distinction. Not cheaper analytics: less analytics, deliberately.
What the eight endpoints are
curl -sS -X POST "https://api.infrai.cc/v1/analytics/track" \
-H "Authorization: Bearer ${INFRAI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"event": "invoice_paid",
"distinct_id": "au_usr_lMmXGySJeGVM1xicqHGJaJbB",
"properties": {"amount_usd": 99, "plan": "pro"}
}'
{
"ok": true,
"data": { "accepted": true, "event_id": "evt_2fVc8nRqLmT4xBzY", "reason": null }
}
Ingestion is track, batch, identify, alias and group. Reading is query/events, query/path and query/retention. There is no ninth endpoint and no dashboard.
The comparison, honestly
| What you need | PostHog | Mixpanel | Infrai analytics |
|---|---|---|---|
| Event ingestion, server-side | yes | yes | yes |
| Identity stitching | yes | yes | yes, alias + identify |
| Funnels and retention | yes, in a UI | yes, in a UI | yes, as API calls |
| A UI for non-engineers | yes, the main value | yes | none |
| Session replay | yes | no | no |
| Autocapture from the browser | yes | partial | no |
| Feature flags, experiments | yes | yes | separate flags namespace |
| Arbitrary group-by exploration | yes | yes | no |
| Same key as your queues, email, storage, AI | no | no | yes |
| Self-hostable | yes | no | no |
Rows four to eight are what you give up, and they are not small. Autocapture means you get data about interactions you never instrumented, which is genuinely useful when you don’t yet know what to measure. A UI means a product manager answers their own question instead of filing a ticket.
When the trade makes sense
Three situations, in my experience of watching teams make this decision.
Nobody opens the dashboard. If the analytics UI has one monthly visitor and the real consumer is a script that emails a number, the UI is overhead. An API plus POST /v1/cron/create and POST /v1/email/send produces that number on the same credential.
The events are commercial, not behavioural. Subscriptions, invoices, exports, usage limits — these are things your backend knows and your database could tell you, and you want them in one timeline rather than in a product-analytics tool built for click paths.
You’re already consolidating. If the queue, the storage, the transactional email and the inference are on one key, adding analytics there means one more call rather than one more vendor.
import os
import requests
API = "https://api.infrai.cc"
KEY = os.environ["INFRAI_API_KEY"]
SESSION = requests.Session()
SESSION.headers.update({"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
def weekly_number(since: str, until: str) -> dict:
"""The report that replaces the dashboard for most teams: how many of the two
events that matter, and did the cohort come back. One call each, no UI."""
paid = SESSION.post(f"{API}/v1/analytics/query/events",
json={"since": since, "until": until,
"filter": {"event": "invoice_paid"}, "limit": 1},
timeout=90).json()["data"]
started = SESSION.post(f"{API}/v1/analytics/query/events",
json={"since": since, "until": until,
"filter": {"event": "subscription_started"}, "limit": 1},
timeout=90).json()["data"]
retention = SESSION.post(f"{API}/v1/analytics/query/retention",
json={"born_event": "signup_completed",
"return_event": "export_requested",
"since": since, "until": until, "interval": "week"},
timeout=120).json()["data"]
return {"invoices_paid": paid.get("total_estimate"),
"subscriptions_started": started.get("total_estimate"),
"cohorts": len(retention.get("cohorts") or [])}
def mail_it(to: str, report: dict) -> bool:
rows = "".join(f"<li>{key.replace('_', ' ')}: <strong>{value}</strong></li>"
for key, value in report.items())
resp = SESSION.post(f"{API}/v1/email/send",
json={"to": to, "subject": "Weekly product numbers",
"html": f"<ul>{rows}</ul>", "message_class": "transactional"},
timeout=60)
return bool(resp.ok)
if __name__ == "__main__":
report = weekly_number("2026-09-14T00:00:00Z", "2026-09-21T00:00:00Z")
print(report, mail_it(os.environ["REPORT_EMAIL"], report))
Twenty lines and a cron entry. That’s the version of “product analytics” a lot of teams actually need, and it arrives in an inbox rather than waiting in a tab.
When to buy the product instead
If a product manager needs to ask a question you didn’t anticipate, buy PostHog. That’s the whole argument for it and it’s a good one — exploration needs a UI, and building one is not a project you should take on to save a subscription.
Session replay is similarly not reproducible here. If watching a user struggle through a flow is how your team finds problems, no events API substitutes for it.
Cost, structurally
Ingestion bills per event and the query endpoints report billing_class: free in discovery, both readable from GET /v1/discovery/analytics.track and verified 2026-09-21. There’s no monthly plan and no event quota to size in advance, which is the difference that matters at small volume: a product sending fifty thousand events pays for fifty thousand events rather than for a tier.
At large volume, the comparison flips toward whoever offers a volume deal, and platform rates here drift downward as vendor contracts improve — so read your own GET /v1/account/usage rather than either vendor’s example.
The limitation worth repeating: there’s no UI, no autocapture and no replay, and adding any of them is not on the roadmap of an events API. What you get instead is that the events, the queue that delivers them, the consent check that gates them, the schedule that reports on them and the email that sends the report are one credential and one invoice.