What is sampling
The RUM SDK controls sampling through thesessionSampleRate parameter, a value from 0 to 100 representing the percentage of sessions to collect:
sessionReplaySampleRate is a second-stage sample applied on top of collected sessions: with sessionSampleRate: 20 and sessionReplaySampleRate: 10, sessions with replay recordings account for 2% of total traffic.
How sampling works
Understanding the following four rules resolves most “I configured the sampling rate but it doesn’t behave as expected” confusion.1. The unit is the session, not the user or the event
The sampling decision happens when a session starts: the SDK flips a coin once with probabilitysessionSampleRate. If the session wins the draw, it is reported in full; otherwise it stays completely silent. There is no such thing as “20% of events within a session get reported” — session data is either complete or absent.
The same user’s session may be sampled today and not sampled tomorrow. The default sampling mechanism is not anchored to specific users.
2. The decision is sticky within a session
The draw result is persisted with the session state (stored in a cookie on the web). A session lasts up to 4 hours while the user stays active, and expires after 15 minutes of inactivity; refreshing or navigating between pages does not trigger a new draw. Only when the session expires and a new one starts is the decision made again, using the sampling rate in effect at that time.This means that after you change the sampling rate, new sessions immediately follow the new rate, while existing sessions keep their original decision until they expire naturally. This is exactly the right semantics for gradual rollout, but it also means the change does not take full effect instantly.
3. It is a probability, not an exact quota
Each session’s draw is independent, with no global coordination. A 20% sampling rate is an expected value: the more traffic you have, the closer the actual collection ratio gets to 20% (law of large numbers); with low traffic, fluctuation is noticeable — collecting 13 or 28 out of 100 sessions is perfectly normal.4. The sampling rate is frozen at initialization
sessionSampleRate is fixed when init() is called. It cannot be changed at runtime, and init() cannot be called a second time within a page’s lifecycle. To change the sampling rate, the next initialization (on the web, the next page load) must receive the new value — the dynamic adjustment approaches below are built around this fact.
Choosing a sampling rate
Best practice 1: make the sampling rate dynamically adjustable
A sampling rate hardcoded in your source requires a release for every adjustment. Instead, externalize it to your own configuration service and read it during SDK initialization:- Web
- Android
- iOS
- Never block initialization waiting for configuration. Synchronously waiting for a config API loses early page data, and config service jitter would delay RUM startup. The right pattern is “initialize immediately with the cached value + refresh the cache asynchronously for next time” — the new rate taking effect one page load later is perfectly acceptable.
- Call
stopSession()when the rate changes (web / Mini Program only). Because the decision is sticky within a session (rule 2), a user who lost the draw under 20% will stay silent even after a new page initializes at 100% — for up to 4 hours — because the existing session keeps its old decision.stopSession()expires the current session immediately; the user’s next interaction starts a new session and re-draws under the new rate. Note that this trick does not work on mobile: the sampling rate is frozen into the sampler at initialization, so a new session afterstopSession()still draws under the old rate. By default the new value takes effect at the next cold start; if you need it immediately, see the advanced approach for mobile below. - Always have a fallback for fetch failures. When the config API is unavailable, fall back to the cached or built-in default value so collection is never interrupted.
Advanced for mobile: applying a new sampling rate immediately
A mobile app process can stay alive for days, so “takes effect at the next cold start” may not be enough for scenarios like incident investigation, where you need full collection right now. In that case, use the full rebuild path: callstopInstance() to stop the current SDK instance, then re-initialize with the new sampling rate. The key discipline: when a new config arrives, only record it — don’t rebuild immediately. Wait for a quiet lifecycle moment, such as the app returning to the foreground — rebuilding in the middle of user activity cuts off the current view and session context.
- Android
- iOS
Best practice 2: business-defined custom sampling
The default random draw treats every user equally, but businesses often want differentiation: collect all VIP users, watch canary users closely, always sample users who recently hit errors. You can achieve this by moving the draw from the SDK into your business code: your code decides whether the current session is sampled, and passes only0 or 100 as sessionSampleRate, reducing it to an on/off switch.
- Web
- Android
- iOS
Why hash bucketing instead of random numbers
The base rule useshash(userId) % 100 instead of Math.random(), which brings two properties the default draw lacks:
- User-level stability: the same user always gets the same decision, so you can answer “does user A have data?” — all sessions of a sampled user are present, and an unsampled user definitively has none.
- Monotonic rollout: when the rate goes from 20% to 100%, every previously sampled user stays sampled, and the newly added users are a pure increment, keeping the data continuous and comparable. Changing the
saltreshuffles all buckets.
Caveats
- The decision must be stable within a session. If you draw with
Math.random()on every page load, different pages within the same session may reach different conclusions, while the SDK only honors the session’s first decision — the symptom is “configured 100 but nothing gets reported”, which is very hard to debug. Deterministic hashing avoids this by construction. - Rule changes also require switching sessions. When a user moves from the “not sampled” bucket to the “sampled” bucket, follow best practice 1: on web / Mini Program, call
stopSession()once when the current decision differs from the previously cached one; on mobile, the change takes effect at the next cold start by default, or rebuild the instance following the advanced approach. - Use
sessionSampleRate: 0instead of skippinginit(). Skipping initialization breaksaddAction/addErrorcalls scattered through your code, forcing null checks everywhere; passing 0 initializes the SDK into a silent state with a unified code path. - Platform-side data reflects what was actually collected. With custom sampling, the platform cannot know your true sampling ratio; the session volume you see is the collected volume, and cannot be extrapolated to total traffic. If you need full-traffic estimates, compute them on your side based on your own sampling rules.
Platform support
For mobile platforms, the default recommendation is the “initialize with the cached value at startup + fetch and cache the latest value asynchronously” strategy, with the new rate taking effect on the next cold start. For scenarios that truly need immediate effect (such as incident investigation), follow the advanced approach and rebuild the SDK instance at a quiet lifecycle moment.
FAQ
I changed the sampling rate from 20% to 100% — why do some users still have no data?
I changed the sampling rate from 20% to 100% — why do some users still have no data?
Existing sessions’ decisions are sticky (rule 2). Sessions that lost the draw before the change stay silent until they expire (15 minutes of inactivity, or 4 hours maximum). If you use the dynamic configuration approach, make sure
stopSession() is called when the rate changes.With a 20% sampling rate, why isn't the actual collected ratio exactly 20%?
With a 20% sampling rate, why isn't the actual collected ratio exactly 20%?
Sampling is an independent probabilistic draw, not a quota (rule 3). The more traffic, the closer to the configured value; fluctuation under low traffic is normal. If you need precise control over which users are collected, use the hash bucketing approach from business-defined custom sampling.
Can I report only errors and nothing else?
Can I report only errors and nothing else?
Sampling operates on whole sessions (rule 1), so “unsampled sessions report only errors” is not possible. Alternative: use business-defined custom sampling and treat “users who hit an error last time” as an always-sample cohort, raising your capture rate for error evidence.