Skip to main content
SDK v7.x · Last verified March 2026 · iOS · Android · Web · Flutter
// 1. Mark a post as viewed (impression + reach)
await post.analytics.markAsViewed();

// 2. Read impression & reach counters
const { impressionsCount, reachCount } = post.analytics;

// 3. Query who viewed the post
const viewers = await post.analytics.getReachUsers({ limit: 50 });
Full walkthrough below ↓
Platform note — code samples below use TypeScript. Every method has an equivalent in the iOS (Swift), Android (Kotlin), and Flutter (Dart) SDKs — see the linked SDK reference in each step.
social.plus tracks two key content performance metrics for every post:
  • Impressions — total number of times the post was viewed (same user can contribute multiple impressions)
  • Reach — number of unique users who viewed the post
Both are updated in near-real time. This guide shows you how to record impressions as users scroll a feed, read the counters, and query the list of users who reached a post — the building blocks for a creator analytics dashboard.
After completing this guide you’ll have:
  • Post impression tracking firing on every feed scroll event
  • Unique reach (impression) and total views (markAsViewed) both captured
  • A “Who viewed this” list queryable per post for creator dashboards

Quick Start: Record a View and Read the Counters

TypeScript
import { PostRepository } from '@amityco/ts-sdk';

// Called from your IntersectionObserver when a post is ≥50% visible
function onPostVisible(post: Amity.Post) {
  post.analytics.markAsViewed();
  console.log(`Impressions: ${post.impression} · Reach: ${post.reach}`);
}
Full reference → Post Impressions

Step-by-Step Implementation

1

Record impressions as posts scroll into view

Call markAsViewed() when a post becomes meaningfully visible. In web apps, use an IntersectionObserver; in native apps, use your list’s viewability callbacks. Aim for a threshold of ≥50% visible for at least 1 second to avoid counting rapid scroll-past views.
TypeScript
import { PostRepository } from '@amityco/ts-sdk';

// Subscribe to the feed so we have live post objects
const unsubscribe = PostRepository.getPosts(
  { targetType: 'community', targetId: communityId },
  ({ data: posts }) => {
    posts.forEach(post => trackVisibility(post));
  },
);

function trackVisibility(post: Amity.Post) {
  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          post.analytics.markAsViewed();
          observer.disconnect();   // count each scroll-into-view once per session
        }
      });
    },
    { threshold: 0.5 }
  );
  observer.observe(document.getElementById(post.postId)!);
}
Full reference → Post Impressions
2

Display impression and reach counts

Read post.impression and post.reach from the live post object. Both update in near-real time as views come in.
TypeScript
import { PostRepository } from '@amityco/ts-sdk';

// Subscribe to a single post for live counter updates
const unsubscribe = PostRepository.getPost(postId, ({ data: post }) => {
  updateDashboard({
    impressions: post.impression,
    reach: post.reach,
  });
});
Full reference → Post Impressions
3

Query the list of users who viewed a post

Get a paginated list of every unique user who reached a post — useful for “Seen by” UI, audience profiling, and creator insights.
TypeScript
import { UserRepository } from '@amityco/ts-sdk';

const unsubscribe = UserRepository.getViewedUsers(
  { postId: post.postId, limit: 20 },
  ({ data: users, hasNextPage, onNextPage, loading, error }) => {
    if (loading) return;
    if (error) console.error(error);

    renderViewedByList(users);

    if (hasNextPage) {
      loadMoreButton.onclick = onNextPage;
    }
  },
);
Full reference → Post Impressions
4

Build a creator dashboard card

Combine impressions, reach, reaction count, and comment count into a compact analytics card shown on the creator’s post detail view.
TypeScript
import { PostRepository } from '@amityco/ts-sdk';

const unsubscribe = PostRepository.getPost(postId, ({ data: post }) => {
  const stats = {
    impressions: post.impression,
    reach: post.reach,
    reactions: post.reactionsCount,      // total reactions across all types
    comments: post.commentsCount,
    // engagement rate: (reactions + comments) / reach * 100
    engagementRate: post.reach > 0
      ? ((post.reactionsCount + post.commentsCount) / post.reach * 100).toFixed(1)
      : '0',
  };

  renderCreatorStats(stats);
});

Analytics Metrics Reference

MetricWhere to readWhat it means
post.impressionPost live objectTotal views (one user can generate multiple)
post.reachPost live objectUnique users who viewed the post
post.reactionsCountPost live objectTotal reactions across all reaction types
post.commentsCountPost live objectTotal comments including replies
Viewed users listUserRepository.getViewedUsersPaginated list of Amity.User objects

Connect to Moderation & Analytics

Admin Console — Post analytics dashboard
Impression and reach data aggregates across all posts and is visible in Admin Console → Analytics → Content. Filter by community, date range, or content type to spot high-performing content.
Stories have their own impression tracking via the Story SDK — the same impression/reach concept applies.Stories & Ephemeral Content
Although there’s no real-time webhook per impression, you can combine post.created and post.updated webhooks with your own analytics pipeline to build server-side engagement scoring.Webhook Events

Common Mistakes

Calling markAsViewed() on every re-render — React and Flutter components can re-render dozens of times. Debounce the call or use an Intersection Observer to fire it only when the post enters the viewport for the first time.
// ❌ Bad — fires on every render
useEffect(() => { post.analytics.markAsViewed(); });

// ✅ Good — fire once with Intersection Observer
const observer = new IntersectionObserver(([entry]) => {
  if (entry.isIntersecting) {
    post.analytics.markAsViewed();
    observer.disconnect();
  }
});
Confusing impressions with reach — Impressions count total views (one user can contribute many). Reach counts unique viewers. Display the right metric for the right context.
Building analytics dashboards without caching — Fetching reach user lists on every refresh is expensive. Cache the data for at least 30 seconds and show loading states for fresh queries.

Best Practices

  • Feed scroll: fire when ≥50% of the post card is visible for ≥1 second
  • Post detail view: fire immediately on open — the user navigated intentionally
  • Clip reel: fire when the clip starts playing (it’s clearly in focus)
  • Don’t fire on posts that are briefly visible during a fast scroll-to-top gesture
  • markAsViewed() is a fire-and-forget call — never await it in a render path
  • If it fails due to network conditions, don’t retry in the same session; impressions are best-effort
  • Impression accuracy is near-real time; don’t expect sub-second updates in the dashboard
  • If your app shows creators a “Seen by” list, disclose this to viewers in your privacy policy
  • The getViewedUsers API returns user objects — only show identifiable information (name, avatar) where appropriate for your app’s context
  • Respect user block relationships: don’t show a blocked user’s name to a creator in the viewed list
  • Lead with reach (unique viewers) as the headline metric — it’s more meaningful than raw impressions for most creators
  • Show a 7-day or 28-day trend line, not just the current snapshot
  • Display engagement rate (reactions + comments / reach) to help creators judge content quality
  • For communities with many posts, sort by reach descending to surface top-performing content first

Dive deeper: Posts API Reference has full parameter tables, method signatures, and platform-specific details for every API used in this guide.

Next Steps

Your next step → Advertising & Monetization

You’re tracking views — now monetize that attention with native ads in the feed.
Or explore related guides:

Short-Form Video Clips

Track impressions on clip posts as users scroll a vertical reel

Stories & Ephemeral Content

Story-specific impression and reach analytics

Build a Social Feed

The feed query that powers impression tracking at scale