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 decides once, with probabilitysessionSampleRate, whether to collect the session. If it does, the session 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 ratio gets to 20% (law of large numbers). With low traffic, it fluctuates: collecting 13 or 28 out of 100 sessions is normal.4. The sampling rate is set when a session is drawn
Each new session is drawn at the rate in effect at that moment: the value delivered from the console when remote configuration is on, otherwise thesessionSampleRate passed to init(). init() can’t be called a second time while the page or app is running. Without remote configuration, changing the sampling rate means the next initialization (on the web, the next page load) must receive the new value.
Choosing a sampling rate
Best practice 1: adjust sampling dynamically with remote configuration
A sampling rate hardcoded in your source requires a release for every change. With remote configuration on, you change sampling settings on the Application management → Remote configuration tab in the console and publish them. Clients pick them up without a code change or release. Typical uses:- Raise the session sample rate while a new version rolls out, then lower it back once it’s stable.
- Set it to 100 while you investigate a production issue, to capture as much evidence as possible.
- Lower it when data volume spikes, or set it to 0 to stop collection in an emergency.
- Lower the rate and turn on Capture additional sessions on error together, so sessions that weren’t drawn still report the lead-up when they hit an error. See best practice 3.
Enabling it and platform support
SetremoteConfigurationEnabled: true at SDK initialization (off by default). On Android and HarmonyOS, call .setRemoteConfigurationEnabled(true); on Electron, enable it in the main-process init(). Keep passing sessionSampleRate at initialization: the SDK uses it until the client first receives a configuration, and again after you disable remote configuration in the console.
Each platform reads a different set of values. Values not listed for a platform have no effect there:
The React Native SDK doesn’t support remote configuration; its sampling rate can only be set in
init().
When changes take effect
- At the next new session: the SDK requests the configuration at startup and whenever a new session starts. A new configuration applies to the next draw, and running sessions keep their decision (rule 2). Clients usually switch within about 4 hours of a publish.
- Stored locally: the configuration is saved on the device, so the first session after the next launch (on the web, the next page load) is drawn with it right away. On the web, a page with nothing stored yet (such as a first visit or a private window) usually waits about 1 second for the configuration before collection starts, and the wait can be longer in a background tab. If the configuration does not arrive in time or the request fails, the first session uses the
init()settings. Uncaught errors and console errors raised during the wait aren’t collected. - Failures don’t change anything: if the request fails, times out, or returns something unreadable, the SDK keeps the configuration already in effect.
- The session sample rate is set to 0 (emergency stop), or raised from 0 to a non-zero value (resume).
- On the web, the replay privacy level becomes stricter.
stopSession() to end the current session.
Confirming the configuration is in effect
After publishing, check adoption in the rollout panel on the Remote configuration tab. It shows the share of recently arrived sessions on each configuration version. If the status stays at No configuration received, the SDK usually has remote configuration disabled or is older than the minimum version above. For publishing, rollback, and adoption details, see Application management - Remote configuration.Best practice 2: business-defined custom sampling
The default random draw treats every user the same, but businesses often want to differentiate: collect everything for VIP users, watch canary users closely, always collect users under investigation. The SDK provides these interfaces so your code decides each session’s sampling rate:
Put the rule parameters (lists, switches, the bucketing salt) in the custom configuration keys of remote configuration and you can deliver them dynamically from the console: changing a list or turning on investigation mode needs no release, and takes effect at the next new session as described in When changes take effect. For example, publish these custom configuration keys in the console:
- Web
- Android
- iOS
beforeSampling, getRemoteConfig(), and setForcedSession(); see each platform’s advanced configuration page. The Electron and React Native SDKs have no beforeSampling: decide before init() yourself, then set sessionSampleRate to 0 or 100.
When support or engineering needs to look into one user right away, you can also call setForcedSession() from your code instead of adding the user to debugUsers, for example when the user submits feedback:
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?”. A sampled user has all sessions, and an unsampled user has none.
- Monotonic rollout: when the rate goes from 20% to 100%, every sampled user stays sampled and new users are only added. The data stays continuous and comparable. Changing the
saltreshuffles all buckets.
Caveats
- Keep the callback light and deterministic.
beforeSamplingruns synchronously while a session is being created, so it must not make network requests or block, and the same input should give the same result. Drawing withMath.random()makes the same user’s decision flip back and forth; deterministic hashing avoids this by construction. If the callback returns a value outside 0–100 or throws, the SDK keeps the incoming rate. - Rule changes take effect at the next new session. When a user moves from the “not sampled” bucket to the “sampled” one, the running session isn’t re-decided. To apply it right away, call
setForcedSession()orstopSession(). - Custom configuration keys are public. They’re delivered to clients, and anyone holding the client token can read them. Never put secrets, access tokens, or personally sensitive information in them. Lists such as
debugUsersshould hold only user IDs that can’t be traced back to a person. - Use
sessionSampleRate: 0instead of skippinginit(). Skipping initialization breaksaddAction/addErrorcalls across your code and forces null checks everywhere. Passing 0 initializes a silent SDK and keeps one code path. - Platform-side data reflects what was actually collected. With custom sampling, the platform can’t know your true sampling ratio. The session volume you see is what was collected, and you can’t extrapolate it to total traffic. If you need full-traffic estimates, compute them on your side based on your own sampling rules.
Best practice 3: full error capture with proportional sampling for the rest
Starting with Web SDK 0.3.0, enable
sessionOnError to capture error sessions without your own beforeSend. Add sessionReplayOnError if you need error replays. See on-error session capture for configuration, billing, and limitations. The recipe below remains available for older versions or custom event filtering. Its failed-request filtering differs from the new options: a failed resource event alone does not trigger on-error capture.sessionSampleRate to 20 will not do it: sampling works per session, and a session that loses the draw reports nothing at all — errors included (rule 1). You would lose roughly four fifths of your errors along with the volume. The custom filtering recipe below collects every session so no error is lost, then buckets on a stable key inside beforeSend so only the winning share keeps full data:
- Error events: always kept — 100% of errors
- Failed requests: always kept — 100% of API failures (HTTP 5xx and request failures are resource events, not error events)
- Successful requests, user actions, long tasks: kept only for the winning 20% — the bulk of your volume converges to 20%
One more thing if you use Session Replay: it needs an alignment step. Natively, replay is a second-stage sample on top of collected sessions. This recipe sets
sessionSampleRate to 100, so the SDK treats every session as fully collected and samples replay across all of them. Some replays land outside the detail bucket and show up as “has a replay, but no behavior data”. Each tab gives the matching fix — skip it if you do not use replay.
- Session bucketing
- User bucketing
The complete data-sampling configuration:Replay alignment: the session ID doesn’t exist yet at
init(), and the replay rate is fixed at initialization. Turn off the automatic draw, switch to manual recording, and do the second draw yourself after initialization. Merge the two options below into the init() above and run the decision once after init():The manual approach has one known limit. When a session expires and renews while the page stays open (15 minutes idle or 4 hours total), the new session gets a new ID and bucket. The SDK exposes no session-renewal event. Pages with very long dwell times need to poll
getInternalContext().session_id for changes to stay strictly aligned.DETAIL_SAMPLE_RATE × REPLAY_SAMPLE_RATE — exactly what native sessionSampleRate: 20 plus sessionReplaySampleRate: 10 yields at 2%.
Three things to know before adopting this
- Volume will not land exactly on 20%. View events are the skeleton of a session and cannot be dismissed, so they still report at 100%. How far volume actually falls depends on the share of resource and action events in your data.
- Absolute counts need to be scaled. The platform still shows 100% of sessions, but only about 20% carry full data. Ratios and percentiles such as error rate and P75 are unaffected, because a random sample still represents the whole. Scale absolute counts, such as total resource requests or clicks, by
1 / 20%. - Session Replay needs one explicit alignment step. This recipe sets
sessionSampleRateto 100, so the SDK assumes every session is collected in full and draws replay across all of them. Skip the alignment step in the matching tab above and replay lands on sessions outside the bucket — you get the recording, but the session behind it holds no behavior data.
Platform support
For minimum versions and the values each platform reads, see Enabling it and platform support. Every platform can call
stopSession() to end the current session so a new rate applies right away.
FAQ
Can I report only errors and nothing else?
Can I report only errors and nothing else?
Strictly reporting only errors is not possible — view events are the backbone of a session and cannot be turned off. But you can get very close, by combining two independent controls:Understand the three costs before adopting this setup:
- The sampling rate decides which sessions are collected. It operates on whole sessions, and an unsampled session reports nothing at all, errors included (rule 1). So lowering the sampling rate is not a way to save volume — it drops your errors along with everything else.
- The event switches decide which events each collected session reports.
trackResources,trackLongTasks,trackUserInteractionsandtrackWebVitalsare independent of sampling and apply to every session that is collected.
sessionSampleRate to 100 so no error is missed, then use the event switches to suppress non-error data. Resource events are usually the bulk of the volume, so trimming them pays off the most.Keep resource collection on instead, and use beforeSend to discard only the successful requests:- View events are still reported. At least one per page, plus throttled updates whenever metrics or event counts change, plus a keep-alive update every 5 minutes while the session is active. This baseline cannot be removed, and
beforeSendcannot discard view events either. - Error evidence is reduced to a stack trace. With
trackUserInteractionsoff, you no longer know what the user clicked before the error, which makes investigation noticeably harder. - Traced requests are not affected by the resource switch. Requests matching
allowedTracingUrlsare still reported with resource collection off. They’re flagged as not indexed and don’t count toward volume, but traffic doesn’t drop to zero.