Track which SDK versions your customers are on

Look at sdk_version on api_request events. What % are on latest vs N-1 vs older? Critical for the 'when can we deprecate?' conversation.

Published

difficulty beginner · time to value 1 minute

Start from this

Look at the sdk_version property of api_request events. What % of distinct customers are on the latest vs N-1 vs N-2 or older?

Why this matters

Deprecating an old SDK version is easy — git rm, ship a note. Deprecating without breaking 30% of your customers is the hard part. Without per-version distribution data you’re guessing whether the upgrade-or-fail email goes to 12 customers or 1,200.

Most teams find out only after the deprecation breaks production for a large account. That call is uniquely terrible because it’s 100% preventable — the data was always there in your API logs, you just never queried it.

Agentry treats SDK version as a property on api_request events. One HogQL pass gives you the distribution + customer list per version. The deprecation conversation becomes specific instead of “let’s wait another quarter to be safe.”

What you get

  • Distribution of distinct customers per SDK version
  • Same distribution by request volume (sometimes one customer on the old SDK is 90% of your old-SDK traffic)
  • A clear list of “customers still on N-2 or older” — your outbound list

Walk through it

You

Look at the sdk_version property of api_request events. What % of distinct customers are on the latest vs N-1 vs N-2 or older?

Agent

Distinct customers per version, then a rollup.

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.sdk_version as ver,
      count(distinct properties.customer_id) as customers,
      count() as requests
    from events
    where event = 'api_request' and timestamp > now() - interval 30 day
    group by ver
    order by ver desc
  "
{
  "rows": [
    { "ver": "3.4.0", "customers":  142, "requests":  4200000 },
    { "ver": "3.3.2", "customers":  610, "requests": 38400000 },
    { "ver": "3.3.1", "customers":  240, "requests":  6800000 },
    { "ver": "3.3.0", "customers":  120, "requests":  2400000 },
    { "ver": "3.2.4", "customers":   42, "requests":   180000 },
    { "ver": "3.1.9", "customers":   18, "requests":   840000 },
    { "ver": "2.8.1", "customers":    3, "requests":  2200000 }
  ]
}
Agent

Two ways to slice the picture:

By customer count:

VersionCustomers% of customers
3.4.0 (latest)14211.7%
3.3.x (N-1)97080.1%
3.2.x (N-2)423.5%
3.1.x181.5%
2.x30.2%

By request volume: (this is where it gets interesting)

VersionRequests% of traffic
3.4.04.2M7.6%
3.3.x47.6M86.6%
3.2.x0.18M0.3%
3.1.x0.84M1.5%
2.x2.2M4.0% ★

Notable: 3 customers on v2.8.1 are 4% of your total API traffic (2.2M requests/30d). One of them is presumably one of your big enterprise accounts that never upgraded. Deprecating 2.x without warning would be a Tier-1 incident.

Recommended:

  1. Safe to deprecate 3.1.x and older (~63 customers, mostly low-volume). Send a 90-day notice.
  2. Find who the v2.8.1 customers are — they need an account-manager-led upgrade conversation, not an email blast.
  3. Render the table so the API team can pull it up at every release planning.
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: "<same query above>"
{ "rows": [/* dashboard rows */], "row_count": 20 }
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,
      sum(case when properties.sdk_version = '2.8.1' then 1 else 0 end) as v2_calls,
      sum(case when properties.sdk_version like '3.%' then 1 else 0 end) as v3_calls
    from events
    where event = 'api_request' and timestamp > now() - interval 30 day
    group by customer
    having v2_calls > 0
    order by v2_calls desc
  "
{
  "rows": [
    { "customer": "wayne_ent", "v2_calls": 1840000, "v3_calls": 0 },
    { "customer": "umbrella_co", "v2_calls": 240000, "v3_calls": 4810000 },
    { "customer": "stark_legacy", "v2_calls": 120000, "v3_calls": 0 }
  ]
}
Agent

Interesting: umbrella_co is mostly on v3 (4.8M v3 calls) but still has 240k v2 calls — probably one stale microservice they forgot about. Easy ask for them to find and update.

wayne_ent and stark_legacy are 100% on v2. These are the conversations that need an AM, not a blast email.

The output

SDK version distribution (30d)

Version    Customers    %        Requests       %       Notes
─────────────────────────────────────────────────────────────────
3.4.0       142         11.7%    4,200,000     7.6%    latest
3.3.2       610         50.4%   38,400,000    69.9%    N-1 (stable)
3.3.1       240         19.8%    6,800,000    12.4%    N-1
3.3.0       120          9.9%    2,400,000     4.4%    N-1
3.2.4        42          3.5%      180,000     0.3%    N-2 (deprecate)
3.1.9        18          1.5%      840,000     1.5%    deprecate
2.8.1         3          0.2%    2,200,000     4.0%    ★ enterprise — AM-led

Deprecation plan:
  Tier 1: 3.1.x and older (≤63 customers, low volume)
    → 90-day email notice
    → Hard sunset at +90 days
  Tier 2: 2.8.1 customers (3 customers, HIGH volume)
    → AM-led upgrade
    → wayne_ent and stark_legacy need migration help
    → umbrella_co has a stray service (240k calls) to track down

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

Setting it up

Tag every api_request event with the SDK version the client sent (or the absence of a version, which usually means “raw HTTP / curl”):

// API middleware
const sdkVersion =
  c.req.header("x-sdk-version") ??     // SDK convention
  c.req.header("user-agent")?.match(/myapi-sdk-js\/([\d.]+)/)?.[1] ??
  "unknown";

fetch(`https://api.agentry.sh/v1/analytics/`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${env.AGENTRY_PUBLIC_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,
      sdk_version: sdkVersion,
      sdk_language: c.req.header("x-sdk-language") ?? "unknown",
      endpoint: c.req.routePath,
    },
  }),
}).catch(() => {});

If your SDK doesn’t send a version header today, ship a version that does in the next release. Old clients will continue to show as “unknown” — that group itself is informative (“how many requests are coming from people who don’t even use our SDK?”).

Variations

  • “Same query split by sdk_language — what % of traffic is from the Python SDK vs JS vs Go?”
  • “For each old SDK version, which API endpoints do those clients hit? Tells me what to keep backward-compatible on the API surface.”
  • “Error rate per SDK version — old versions sometimes drive higher error rates because they don’t handle new error codes.”
  • “Day-over-day trend on v3.4.0 adoption — is the latest release picking up users at expected rate?”

Adapt this playbook 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.

Set up Agentry in this repo by following https://agentry.sh/install.md. I authorize you to: run its device-code login and save the key to ~/.agentry/credentials.json, read this repo, propose an instrumentation plan for my approval, and edit code after I approve it.

+ 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.