Per-customer API call analytics

Group api_request events by customer_id over 30 days with daily breakdown. The view usage-based pricing needs but most teams cobble together from logs.

Updated · published

markdown for agents →

difficulty intermediate · time to value 3 minutes · execution on demand

Start from this

Group api_request events by customer_id in the last 30 days. Show me top 20 customers by call volume, with daily breakdown for each.

Why this matters

If you sell a usage-based API, the per-customer usage table is your second-most-important dashboard (after MRR). Yet most teams build it badly:

  • The data lives in CloudFlare logs or application logs that aren’t queryable per-customer
  • The “dashboard” is a quarterly SQL someone ran by hand
  • CS can’t pull “how much did Acme use last week?” without pinging engineering
  • Sales has no idea who’s growing usage (= ready to upsell) vs flatlining (= about to churn)

Agentry treats API requests as just-another-analytics-event. One HogQL pass gives you top customers, daily series, growth rate — the lot. Then render the rows in a CS-visible server-side dashboard so the team doesn’t have to ping engineering for every account check.

What you get

  • Top 20 customers by 30-day call volume
  • Daily breakdown per customer (sparkline data)
  • Dashboard rows that CS and sales can both pull up from a server-side panel

Walk through it

You

Group api_request events by customer_id in the last 30 days. Show me top 20 customers by call volume, with daily breakdown for each.

Agent

Two passes — first the ranked top 20, then daily series for each.

Endpoint: POST /v1/projects/:project_id/analytics/query
Exact shape: /v1/openapi.json?path=%2Fv1%2Fprojects%2F%7Bproject_id%7D%2Fanalytics%2Fquery&method=post
Custom HogQL goes in the OpenAPI-defined `query` field.
Concept fields:
  query: "
    select
      properties.customer_id as customer_id,
      count() as total_calls,
      count(distinct toDate(timestamp)) as active_days,
      max(timestamp) as last_call_at
    from events
    where event = 'api_request' and timestamp > now() - interval 30 day
    group by customer_id
    order by total_calls desc
    limit 20
  "
{
  "rows": [
    { "customer_id": "acme_corp",     "total_calls": 18420000, "active_days": 30, "last_call_at": "2026-05-16T13:42:00Z" },
    { "customer_id": "wayne_ent",     "total_calls":  9842000, "active_days": 30, "last_call_at": "2026-05-16T13:41:00Z" },
    { "customer_id": "stark_ind",     "total_calls":  6210000, "active_days": 28, "last_call_at": "2026-05-15T22:18:00Z" },
    { "customer_id": "umbrella_co",   "total_calls":  4810000, "active_days": 30, "last_call_at": "2026-05-16T13:30:00Z" },
    { "customer_id": "initech_llc",   "total_calls":   180000, "active_days":  8, "last_call_at": "2026-05-04T10:00:00Z" }
  ]
}
Agent

Now the daily series for the top customers so you can see the shape.

Endpoint: POST /v1/projects/:project_id/analytics/query
Exact shape: /v1/openapi.json?path=%2Fv1%2Fprojects%2F%7Bproject_id%7D%2Fanalytics%2Fquery&method=post
Custom HogQL goes in the OpenAPI-defined `query` field.
Concept fields:
  query: "
    select
      properties.customer_id as customer_id,
      toDate(timestamp) as day,
      count() as calls
    from events
    where event = 'api_request'
      and timestamp > now() - interval 30 day
      and properties.customer_id in ('acme_corp', 'wayne_ent', 'stark_ind', 'umbrella_co', 'initech_llc')
    group by customer_id, day
    order by customer_id, day
  "
{
  "rows": [
    { "customer_id": "acme_corp",   "day": "2026-04-16", "calls":  580000 },
    { "customer_id": "acme_corp",   "day": "2026-04-17", "calls":  604000 },
    { "customer_id": "acme_corp",   "day": "2026-04-18", "calls":  590000 },
    "...",
    { "customer_id": "initech_llc", "day": "2026-04-29", "calls":   12000 },
    { "customer_id": "initech_llc", "day": "2026-05-04", "calls":  168000 },
    { "customer_id": "initech_llc", "day": "2026-05-05", "calls":       0 }
  ]
}
Agent

Two interesting cases worth flagging to CS/Sales right now:

  • stark_ind: 6.2M calls / month, but active_days = 28 and last call 2 days ago. They’ve gone quiet. Either weekend lull or churn risk. Worth a CS check-in.
  • initech_llc: 180k total but only 8 active days, last call 12 days ago. Classic dormant API customer — they integrated, hit it once big, then ghosted. A “did we miss anything?” email here is high-leverage.

Let me prepare the rolling top-20 for a dashboard panel.

Endpoint: POST /v1/projects/:project_id/analytics/query
Exact shape: /v1/openapi.json?path=%2Fv1%2Fprojects%2F%7Bproject_id%7D%2Fanalytics%2Fquery&method=post
Custom HogQL goes in the OpenAPI-defined `query` field.
Concept fields:
  query: "<first query above>"
{ "rows": [/* dashboard rows */], "row_count": 20 }
Agent

Dashboard data ready. Recommended follow-ups:

  1. api-usage-as-upsell-signal — find the customers whose calls grew >30% MoM (= ready to upsell)
  2. rate-limit-alert-routing — review customers crossing 80% of their cap
  3. Draft a weekly-window delta summary for CS to review and share

The output

Top 20 API customers (30d)

Customer        Calls (30d)    Active days   Last call         Trend
─────────────────────────────────────────────────────────────────────
acme_corp       18,420,000          30        2 min ago        steady █████
wayne_ent        9,842,000          30        3 min ago        growing ███▟
stark_ind        6,210,000          28        2 days ago       ★ check in
umbrella_co      4,810,000          30        15 min ago       steady ████
initech_llc        180,000           8        12 days ago      ★★ dormant

Public view:
  Use Agentry's returned public_url after publishing a real query blueprint.

Suggested next investigations:
  - api-usage-as-upsell-signal (who's growing?)
  - rate-limit-alert-routing (who's hitting caps?)
  - on-demand weekly-window delta summary for a human to share with CS

Setting it up

Emit one api_request analytics event per request. Don’t worry about volume — Agentry’s analytics ingest handles millions/day per project. The trick is what to put in customer_id:

  • For API-key auth: the customer (org/project) the key belongs to, NOT the raw key
  • For OAuth: the OAuth client id
  • For Bearer JWT: a sub-claim you control

Tag a few useful properties for downstream segmentation:

// Hono / Express middleware after auth
app.use(async (c, next) => {
  const customer = await resolveCustomer(c.req.header("authorization"));
  c.set("customer", customer);

  // emit analytics — DO NOT await this, send fire-and-forget
  fetch(`https://api.agentry.sh/v1/analytics/`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${env.AGENTRY_SERVER_API_KEY}`,
      "Content-Type": "application/json",
      "User-Agent": "myapi-edge/1.0",  // REQUIRED — Cloudflare 403s default UAs
    },
    body: JSON.stringify({
      event: "api_request",
      distinct_id: customer.id,
      properties: {
        customer_id: customer.id,
        plan: customer.plan,
        endpoint: c.req.routePath,
        method: c.req.method,
        sdk_version: c.req.header("x-sdk-version"),
        ip_country: c.req.header("cf-ipcountry"),
      },
    }),
  }).catch(() => {}); // never block the user request on analytics
  await next();
});

Per-request volume warning: at very high RPS (>10k/sec) consider batching events client-side and POSTing a compacted batch every few seconds. The /v1/analytics/ endpoint accepts an array of events in one POST.

Variations

  • “Same top-20 but split by API endpoint — which endpoints are the heavy hitters?”
  • “Hour-of-day distribution for the top 5 customers. Are they bursty or steady-state?”
  • “Per-customer error rate (4xx + 5xx ÷ total). Sometimes a top customer’s usage is mostly failing requests.”
  • “Show me customers whose usage dropped >50% in the last 7 days vs prior 7. Early churn signals.”

Adapt this workflow in your own agent.

Ask your agent to map the starter prompt to your saved signal map, live events, and the current HTTP API before answering.

Agentry gives coding agents production context from errors, product analytics, and deploys through one HTTP API. Fetch and read https://agentry.sh/install.md completely, then install Agentry's full first-time telemetry baseline in this repository. I authorize device-code login, saving the owner key only to ~/.agentry/credentials.json, creating one Agentry project for this repository, and read-only inspection of shipped source plus hidden CI/provider configuration. Deeply analyze the product's business logic and complete value flow before choosing telemetry. Save the complete source-backed plan and show me its exact contents and hash. Until I explicitly approve that plan or request a replacement, do not edit app/CI source, place runtime/CI credentials, commit, exercise product traffic, start proof, push, or deploy. After approval, I authorize only the reviewed targets: place the required scoped browser/server/CI credentials through the established environment or secret mechanism, preserve existing telemetry, implement and test the baseline, commit it, push that reviewed commit when the shipped CI/provider path requires it, exercise safe proof paths with test/non-customer data, and perform one deployment through the reviewed shipped CI/provider path. Ask first if proof would charge money, contact a third party, change real customer data, or require new external access. After the plan is saved, immediately before every onboarding state-changing POST, GET current onboarding state, perform only its single returned next_action, then read state again; do not batch or infer later stages. Continue until status is verified, installation_complete is true, and next_action is null. Keep all secrets, source snapshots, proof markers, and scratch files outside the repository.

+ Full access
5.5 Extra High
  1. 1. Open your repo in Codex, Claude Code, Cursor etc.
  2. 2. Paste the install prompt.
  3. 3. Your agent reads the install doc and shows you an implementation plan for approval.