Activity Metrics

Activity metrics let you track custom counters like views, clicks, and impressions for activities. These metrics are stored on the activity and can be used in ranking expressions to build engagement-driven feeds.

Activity metrics require a paid Feeds plan. Free-tier applications cannot use this feature. Paid (self-service) plans get the default metrics listed below. Enterprise plans can additionally configure up to 10 custom metrics per app.

Tracking Metrics

Track one or more metric events in a single request. Each event specifies an activity, a metric name, and an optional delta (defaults to 1). Events are independently rate-limited per user per activity per metric.

const response = await client.trackActivityMetrics({
  events: [
    { activity_id: "activity_123", metric: "views" },
    { activity_id: "activity_123", metric: "clicks", delta: 2 },
  ],
});

for (const result of response.results) {
  console.log(
    `${result.activity_id}/${result.metric}: allowed=${result.allowed}`,
  );
}

Server-side calls must include user_id. Client-side calls use the authenticated user's ID automatically.

Default Metrics

All paid plans include the following built-in metrics:

MetricMax per hour per user per activity
views10
clicks5
impressions50

These defaults are always available. Enterprise apps can override their rate limits or disable them by setting the limit to 0 via custom metrics configuration.

Metric names must be alphanumeric with underscores or dashes only, and a maximum of 64 characters (e.g. views, link_clicks, video-watch-time).

Custom Metrics Configuration

Enterprise plans can configure up to 10 custom metrics per app using the updateApp API. Custom metrics are merged on top of the defaults:

  • Add a new metric: Include it in activity_metrics_config with a rate limit value.
  • Override a default's rate limit: Include the default metric name with a new limit value.
  • Disable a default metric: Set its value to 0.
  • Unmentioned defaults are preserved: Only metrics you explicitly include in the config are affected.
// Add custom metrics and override the default "views" limit
await client.updateApp({
  activity_metrics_config: {
    shares: 5, // new custom metric: 5 per hour per user per activity
    saves: 10, // new custom metric: 10 per hour
    views: 20, // override default views limit from 10 to 20
    impressions: 0, // disable the default impressions metric
  },
});

The resulting effective config for this app would be:

MetricLimitSource
views20overridden from default (10)
clicks5default (unchanged)
impressionsdisabled by custom config
shares5custom
saves10custom

You can read back the current custom config via getApp:

const app = await client.getApp();
console.log(app.app.activity_metrics_config);
// { shares: 5, saves: 10, views: 20, impressions: 0 }

To clear all custom overrides and revert to defaults, set an empty config:

await client.updateApp({ activity_metrics_config: {} });

Custom metrics configuration is available on Enterprise plans only. Paid self-service plans use the default metrics. The activity_metrics_config returned by getApp only contains your custom overrides — default metrics that have not been overridden are not listed.

Delta Values

The delta field controls how much the metric counter changes:

  • Positive delta: Increments the metric (e.g. delta: 5 adds 5)
  • Negative delta: Decrements the metric (e.g. delta: -1 subtracts 1)
  • Default: If delta is omitted, it defaults to 1

The absolute value of delta counts against the rate limit. For example, delta: 100 counts as 100 against the limit, not just 1.

Batching

You can track up to 100 events in a single request. Events can target different activities and different metrics. Each event is independently rate-limited.

// Track metrics for multiple activities in one request
const response = await client.trackActivityMetrics({
  events: [
    { activity_id: "activity_1", metric: "views" },
    { activity_id: "activity_1", metric: "clicks" },
    { activity_id: "activity_2", metric: "views" },
    { activity_id: "activity_3", metric: "impressions", delta: 5 },
  ],
});

Rate Limiting

Each metric is rate-limited per user (or IP) per activity per metric within a 1-hour sliding window. When the limit is exceeded, the event is rejected and allowed is set to false in the response — this is not an error, just a signal that the event was not counted.

Always check the allowed field in the response:

const response = await client.trackActivityMetrics({
  events: [{ activity_id: "activity_123", metric: "views" }],
});

for (const result of response.results) {
  if (!result.allowed) {
    console.log(`Rate limited: ${result.activity_id}/${result.metric}`);
  }
}

Reading Metrics

Metrics are included in the activity response as a metrics object:

// metrics is available on any activity response
const activity = await client.getActivity({ id: "activity_123" });
console.log(activity.metrics);
// { views: 42, clicks: 15, impressions: 200 }

// Access individual metrics
const views = activity.metrics?.views ?? 0;
const clicks = activity.metrics?.clicks ?? 0;

The raw JSON response looks like this:

{
  "id": "activity_123",
  "type": "post",
  "text": "Hello world",
  "metrics": {
    "views": 42,
    "clicks": 15,
    "impressions": 200
  }
}

Metrics are synced from the buffer to the database every 5 minutes.

Using Metrics in Ranking

Metrics can be referenced in ranking expressions to build engagement-driven feeds. Use the metrics object in your ranking score expression:

// Create a feed group ranked by engagement metrics
const response = await serverClient.feeds.createFeedGroup({
  id: "trending",
  ranking: {
    type: "expression",
    score: "metrics.views * 2 + metrics.clicks * 3",
    defaults: {
      metrics: {
        views: 0,
        clicks: 0,
      },
    },
  },
  activity_selectors: [{ type: "following" }],
});

When using metrics in ranking expressions, you must provide defaults for each metric. Activities without any tracked metrics need default values for the expression to evaluate correctly.

Ranking Expression Examples

// Simple view-based ranking
metrics.views

// Weighted engagement score
metrics.views * 2 + metrics.clicks * 3 + metrics.impressions * 0.1

// Combined with popularity and time decay
decay_linear(time) * (popularity + metrics.views * 0.5)

// Conditional boost for popular content
metrics.views > 100 ? popularity * 2 : popularity

Limitations

  • Paid plan required: Activity metrics are not available on the free tier
  • Custom metrics (Enterprise only): Up to 10 custom metrics per app; self-service plans use defaults only
  • Metric names: Alphanumeric characters, dashes, and underscores only; maximum 64 characters
  • Sync delay: ~5 minutes between tracking and availability in responses/ranking
  • No filtering: Metrics cannot be used in query filters (only in ranking expressions)
  • Rate limit windows: Rate limits use 1-hour time windows, not permanent deduplication
  • Batch size: Maximum 100 events per request