Reprise Data API - Updated 2026

The Analytics Data API lets you pull your Reprise demo analytics—session activity, viewer metrics, and change events—directly into your own data warehouse or BI tool (Looker, Athena/dbt, Snowflake, and similar). It returns the same numbers you see in the Reprise Analytics UI.

  • Base URL: https://api.us-east.tinybird.co/v0/pipes/
  • Format: JSON by default. Append .csv instead of .json for CSV.
  • Scope: every request is automatically restricted to your own account’s data (see Authentication).

Already integrated with an earlier version of the API? Share the query you run today and your Reprise contact will validate it against this version before you switch over.

Contents

  1. Authentication
  2. Request & response conventions
  3. Pagination
  4. Endpoints
  5. The visitor_key field
  6. Incremental sync
  7. Error handling
  8. Limits & reliability

1. Authentication

Authentication uses a two-step model: a long-lived API key you create once, which you exchange for a short-lived access token that you use on each query.

Step 1 — Create an API key

In Reprise, go to Settings → API Management and create a Data Warehouse API key. The key is shown once—store it securely and treat it like a password. You can create multiple keys (for example, one per integration) and revoke any of them at any time.

Step 2 — Exchange the key for an access token

Call the token endpoint with your API key as a bearer token:

curl -X POST https://app.getreprise.com/api/warehouse/token \
  -H "Authorization: Bearer rprswh_your_api_key"

Response:

{
  "token": "<jwt>",
  "token_type": "jwt",
  "expires_at": 1750000000,
  "api_url": "https://api.us-east.tinybird.co/v0/pipes/replay_session_activity.json"
}
  • token — the access token to use on data requests.
  • expires_at — Unix epoch (seconds) when the token expires (about 48 hours out).
  • api_url — a ready-to-use example URL for the primary feed.

The access token can only ever read your own account’s data. It is read-only and scoped to the analytics endpoints in this guide and nothing else.

Step 3 — Query with the access token

curl "https://api.us-east.tinybird.co/v0/pipes/replay_session_activity.json?start_timestamp=2026-01-01%2000:00:00&limit=1000" \
  -H "Authorization: Bearer <jwt>"

Refreshing tokens

Access tokens are short-lived. Refresh on a schedule—a daily job that calls the token endpoint and uses the returned token for that day’s run is the recommended pattern. The lifetime is comfortably longer than a daily cadence, so a single missed refresh won’t break a sync. Do not mint a new token per request.

Note: newly added endpoints require a freshly issued token. If you created a token before an endpoint existed, re-issue it—your daily refresh does this automatically.


2. Request & response conventions

Response envelope

Every JSON response uses the same envelope:

{
  "data": [{ "...": "one object per row" }],
  "meta": [{ "name": "activity_id", "type": "String" }],
  "rows": 1000,
  "rows_before_limit_at_least": 45678,
  "statistics": { "elapsed": 0.03, "rows_read": 1000, "bytes_read": 123456 }
}
  • data — the rows.
  • meta — column names and types (useful for typing your loader).
  • rows — rows in this response.
  • rows_before_limit_at_least — the total matching rows before limit / offset were applied. This is your has-more signal (see Pagination).

Timestamps and clocks

  • All timestamp parameters use YYYY-MM-DD HH:MM:SS (UTC). URL-encode the space as %20.
  • start_timestamp / end_timestamp filter on the session time, a server-side clock.
  • The per-event create_at field is the viewer’s browser clock. It is reliable for ordering events within a session, but its absolute value can be wrong (a misconfigured viewer device can report a time days off). Don’t treat create_at as a trustworthy wall-clock, and don’t compute durations by subtracting it from the server-side session_created_at.

IDs

IDs (activity_id, published_replay_id, link_id, and others) are opaque strings. activity_id uniquely identifies an event and is never re-emitted. Link IDs are stable across re-publishes; published-replay and snapshot IDs can change when a demo is re-published—so dedupe on activity_id, latest pull wins.


3. Pagination

There are two mechanisms, usable together:

  • Offset paging: limit (page size) + offset (rows to skip).
  • Cursor (incremental): a since_* parameter that returns only rows after a point you’ve already seen. Preferred for ongoing syncs—see Incremental sync.

Has-more: there are more pages when rows_before_limit_at_least > offset + limit. Keep requesting until that is no longer true.

offset, limit = 0, 10000
while True:
    r = get(url, params={**base, "limit": limit, "offset": offset}).json()
    yield from r["data"]
    if r["rows_before_limit_at_least"] <= offset + limit:
        break
    offset += limit

4. Endpoints

replay_session_activity

One row per session-activity event (snapshot view, guide step, button click, welcome page, url view) for your published replays (standard demos), enriched with session, link, and replay metadata.

Parameters

Param Type Required Notes
start_timestamp datetime session-created lower bound
end_timestamp datetime session-created upper bound (must be ≥ start_timestamp)
limit / offset int offset paging
since_created_at datetime(ms) incremental cursor, exclusive > on event create_at
activity_type string comma-separated allow-list, e.g. url view,guide button click—returns only those event types (smaller payloads)
visitor_key any include the non-PII visitor_key column
visitor_company any include the visitor_company column
poster any include tags, poster_img, base_href

Rows are ordered by create_at ASC, activity_id ASC.

Selected response columns: activity_id, activity_type, create_at, session_id, session_created_at, visitor_name, visitor_email, link_id, link_title, link_url, published_replay_id, published_replay_title, distinct_user (plus visitor_key, visitor_company, poster columns when requested). Inspect meta for the full list and types.

curl "https://api.us-east.tinybird.co/v0/pipes/replay_session_activity.json?start_timestamp=2026-01-01%2000:00:00&end_timestamp=2026-02-01%2000:00:00&activity_type=guide%20button%20click&limit=10000" \
  -H "Authorization: Bearer <jwt>"

replicate_session_activity

One row per viewing session of a Replicate (LiveDemo) replay.

Parameters

Param Type Required Notes
start_timestamp datetime session lower bound
end_timestamp datetime session upper bound (≥ start_timestamp)
limit / offset int offset paging
since_created_at datetime(ms) incremental cursor, exclusive > on session_date
viewer_pii any also include the raw viewer column (email/IP)—off by default

Rows are ordered by session_date ASC, session_id ASC.

Response columns: client_id, session_id, published_replay_id, demo_title, demo_published_date, session_date, visitor_key (plus viewer when viewer_pii is set).

replay_metrics

One row per replay (or link) with the same aggregate metrics shown in the Reprise Analytics UI. No PII.

Parameters

Param Type Required Notes
start_timestamp datetime session window lower bound
end_timestamp datetime session window upper bound
group_by string published_replay (default) or published_replay_link
replay_id / replay_ids / link_ids string filter to specific replays/links (the *_ids variants are pipe-separated)
limit / offset int offset paging (default limit 200)

Response columns & exact definitions

A “session” here is one viewing session in your selected window. Sessions from Reprise staff are excluded, matching the Analytics UI. Duration-based metrics are computed only over sessions longer than 0.5 seconds (this drops near-instant bounces that would otherwise skew the averages). A session’s duration is the time between its first and last recorded event.

Column Definition
entity_type published_replay or published_replay_link (per group_by)
entity_id the replay or link id
total_sessions number of sessions in the window
distinct_views distinct viewers (a viewer is identified by, in order: URL param u, welcome-form email, signed-in user, then visitor name)
median_seconds median session duration, over sessions > 0.5s
mean_seconds mean session duration, over sessions > 0.5s
mean_click_events mean interactions per session, over sessions > 0.5s
mean_screen_events mean distinct screens viewed per session, over sessions > 0.5s (a revisited screen counts once)
bounce_rate of sessions with at least one interaction, the percentage with exactly one: count(interactions = 1) * 100 / count(interactions ≥ 1) (0 when none qualify)

An interaction is an event whose type is one of: new snapshot, guide button click, Welcome Page, url view, guide close click. Other event types (for example, a plain guide view) are not counted as interactions. These are exactly the numbers the Reprise Analytics UI shows.

replay_change_feed

One row per published replay or link that was modified or deleted, so you can keep your warehouse in sync—re-pull changed rows and remove deleted ones—without re-scanning the activity feed.

Parameters

Param Type Required Notes
since_ingested_at datetime cursor, exclusive > on ingested_at. Omit for a full snapshot, then pass the previous batch’s max ingested_at.
limit / offset int offset paging

Response columns: entity_type (published_replay | published_replay_link), entity_id, published_replay_id (parent replay, for links), change_type (deleted | modified), changed_at (when the change happened, informational), ingested_at (the cursor field). Rows are ordered by ingested_at ASC.

To apply: for each row, if change_type is deleted, tombstone entity_id in your warehouse; otherwise re-pull that entity from the relevant feed.

replay_session_summary

One row per session (the activity feed is one row per event). Use it for session-level metrics—how long sessions lasted, how many events each had, and unique-viewer counts—without pulling and collapsing every activity event yourself. Join to replay_session_activity on session_id when you need both.

Parameters

Param Type Required Notes
start_timestamp datetime filters on session created_at
end_timestamp datetime upper bound on session created_at
since_created_at datetime incremental cursor, exclusive > on session_created_at
limit / offset int offset paging; rows ordered by session_created_at ASC

Response columns: client_id, session_id, session_created_at (server clock), first_activity_at / last_activity_at (viewer clock—see below), session_duration_seconds, activity_count, published_replay_id, published_replay_title, link_id, link_title, visitor_key. For a session with zero activity events, first_activity_at / last_activity_at are NULL (not epoch) and session_duration_seconds / activity_count are 0.

On session length and “session end”:

  • session_duration_seconds is the time from the first to the last activity event in the session. Both ends are on the same viewer-device clock, so the duration is valid even when the device clock is off. This is the field to use for “how long was the session.” It is 0 for sessions with fewer than two events.
  • last_activity_at is the best-effort “session end” wall-clock time, but it is the viewer’s clock. Do not subtract it from session_created_at (a server clock)—the two clocks aren’t comparable and the result is meaningless. Use session_duration_seconds for elapsed time.
  • A very small fraction of sessions have an implausibly large duration from a skewed device clock; treat extreme values as suspect.

5. The visitor_key field

visitor_key is a stable, irreversible hash of a viewer’s identity. Use it to count unique viewers (COUNT(DISTINCT visitor_key)) and to join a viewer’s sessions together—without ingesting any personal data. The same viewer always maps to the same key; no email, name, or IP is exposed. It is available on replay_session_activity (pass visitor_key) and is the default identity on replicate_session_activity and replay_session_summary.


6. Incremental sync

For an ongoing sync, use the cursor rather than re-scanning a window each run.

Activity / Replicate feeds — cursor on event/session time:

  1. First run: full window with start_timestamp / end_timestamp.
  2. Each run: pass since_created_at = the max create_at (activity) / session_date (replicate) from the previous batch.
  3. create_at is millisecond precision. At very small page sizes a page boundary can split events that share the same millisecond. Use a large page size (10,000 is recommended) or overlap slightly (>= on your stored cursor) and dedupe on activity_id—which you should do anyway, since re-published demos can re-emit denormalized columns.

Change feed — cursor on since_ingested_at. Because rows can arrive in the warehouse slightly after the change happened, the cursor is on arrival time (ingested_at), which guarantees late-arriving rows are never skipped. It is second-precision, so overlap by a second (or use >=) and keep the latest row per entity_id.


7. Error handling

Errors return the appropriate HTTP status and a JSON body with an error field.

Token endpoint (/api/warehouse/token)

Status error Meaning / action
401 missing_bearer_token No Authorization: Bearer <key> header.
401 invalid_or_revoked_key The API key is wrong or has been revoked. Create a new key.
403 api_access_not_enabled Your account doesn’t have Data API access enabled. Contact Reprise.
503 warehouse_jwt_not_configured Temporary server-side config issue. Retry; if it persists, contact Reprise.
200 Success; use the returned token.

Data endpoints

{ "error": "start_timestamp (String) query param is required" }
Status Cause Action
400 Missing/invalid parameter—e.g. no start_timestamp, or end_timestamp earlier than start_timestamp Fix the parameters. The error text names the problem.
403 Invalid authentication token—expired, malformed, or wrong token Refresh your access token and retry.
403 “doesn’t have access to this resource” Your token predates this endpoint. Re-issue it (your daily refresh does this).
429 Rate limited Back off and retry—see Limits.
5xx Transient server error Retry with exponential backoff.

Note: a backwards date range returns a 400 error (not an empty result), so a misconfigured sync fails loudly instead of silently importing zero rows.


8. Limits & reliability

  • Page size: use limit to control payload. Very large single responses are best avoided—page with limit / offset or the cursor. 10,000 rows/page is a good default.
  • Rate limiting: excessive concurrent requests may return 429. Retry with exponential backoff and limit concurrency. If you expect heavy, regular volume, tell your Reprise contact so the team can validate your query pattern.
  • Availability: the API is built for large analytical pulls—there is no per-request row cap. (A formal SLO will be published; ask your Reprise contact for the current target.)

Need help getting started or migrating from an earlier version? Reach out to your Reprise contact and share the query you run today—they’ll validate it against this API before you switch over.


Was this article helpful?
0 out of 0 found this helpful
Have more questions?
Submit a request
Share it, if you like it.