Start free

Home › Track AI agent API usage per customer

Track AI agent API usage per customer and endpoint

More of your customers now call your API through AI agents, MCP servers and automations as well as through their own apps. That's real usage, and you'll usually want to bill it the same way. What helps is seeing it as its own bucket: how much of Acme's traffic on /v1/orders/:id comes from their agents and how much from their app. Then you can plan capacity, price agent-heavy plans and spot which endpoints agents rely on.

The honest part first: there is no agent field

API Meter records method, normalized endpoint, status, latency, sizes, customer and plan (see ingest docs). There's no separate "agent" or "bot" field. The approach below puts the bucket on the plan label: pro for app traffic, pro-agent for agent traffic. The customer stays the same, so their total is unchanged.

How it works

  1. Your auth middleware already knows the customer and their plan.
  2. A small function decides whether the call came from an agent. Prefer signals you control: a separate API key type for agents, an OAuth client type, or a header your own SDK or MCP server sets (for example x-client-type: agent). A user-agent match is a fallback guess.
  3. Agent calls are sent with plan: "<plan>-agent". Everything else is sent as usual.
  4. Every request is counted toward the customer's usage either way. Nothing is blocked or filtered.

Snippet: Express with the ingest API

This posts batches to POST /api/ingest/<slug> with your msi_ ingest key, the same endpoint as the standard ingest snippets: up to 500 events per request, a capped queue of 5,000, a 2 s flush and a 5 s timeout, and it fails open so your API never waits on us.

// Express: count AI agent / bot calls per customer and endpoint as their own bucket.
// API Meter has no agent field, so the bucket rides on the plan label: "pro" vs "pro-agent".
const SLUG = process.env.API_METER_SLUG;            // your API's slug
const KEY = process.env.API_METER_KEY;              // msi_... ingest key (server-side only)
const URL_ = `https://api-meter-lab.pages.dev/api/ingest/${SLUG}`;
const AGENT_UA = /(gpt|claude|anthropic|openai|perplexity|langchain|llamaindex|bot|agent)/i;

// Decide the traffic type. Prefer signals you control (a separate agent API key,
// an OAuth client type, or a header your SDK/MCP server sets); user-agent is a fallback guess.
function trafficType(req) {
  if (req.get("x-client-type") === "agent") return "agent";
  if (req.apiKey && req.apiKey.kind === "agent") return "agent";
  if (AGENT_UA.test(req.get("user-agent") || "")) return "agent";
  return "app";
}

let queue = [];
function flush() {
  if (!queue.length) return;
  const events = queue.splice(0, 500);
  fetch(URL_, { method: "POST", headers: { authorization: `Bearer ${KEY}`, "content-type": "application/json" },
    body: JSON.stringify({ events }), signal: AbortSignal.timeout(5000) }).catch(() => {}); // fail-open
}
setInterval(flush, 2000).unref();

function apiMeter(req, res, next) {
  const t0 = Date.now();
  res.on("finish", () => {
    const base = (req.customerPlan || "unknown").slice(0, 25);
    const type = trafficType(req);
    if (queue.length >= 5000) return;               // capped queue: drop rather than grow
    queue.push({
      method: req.method,
      path: req.route ? req.baseUrl + req.route.path : req.path,   // template, e.g. /v1/orders/:id
      status: res.statusCode,
      latency_ms: Date.now() - t0,
      customer: req.customerId,                     // same customer either way
      plan: type === "agent" ? `${base}-agent` : base, // agent bucket, billed the same
    });
    if (queue.length >= 500) flush();
  });
  next();
}
module.exports = { apiMeter, flush };
// app.use(apiMeter)  after your auth middleware sets req.customerId / req.customerPlan

Call flush() on shutdown (for example in a SIGTERM handler) so the last batch is sent. Plan labels are lowercased and cut to 32 characters by the API; the snippet trims the base plan to 25 so -agent always fits.

Using the proxy instead

If your traffic goes through the API Meter proxy, return the same label from your origin in the response header X-MSA-Plan (for example X-MSA-Plan: pro-agent) next to X-MSA-Customer-Id. The proxy reads both and strips them before the response reaches the caller.

res.set("X-MSA-Customer-Id", req.customerId);
res.set("X-MSA-Plan", trafficType(req) === "agent" ? `${req.customerPlan}-agent` : req.customerPlan);

Reading the numbers

Limits, stated plainly

FAQ

Should I block AI agents?

This guide doesn't cover that. Agents calling your API with a customer's key are that customer's usage, and API Meter counts them like any other request.

Does agent traffic cost more on API Meter?

No. A request is a request; the bucket only changes how it's labelled.

Can I do this with the Python, Django or Go snippets?

Yes. They send the same plan field, so set it to <plan>-agent for agent calls in the same place. See ingest docs.

Try it

Start free See the live demo

Related: API usage analytics per customer · Per-customer usage billing · Ingest docs · Integrations · Compare tools · Track AI agent API usage