Guides
Migrate from Simple Analytics
Replace the live tracker, verify new data, and handle historical data separately. Supported script settings and sa_event() calls can carry over.
1. Audit the existing install and choose a cutover
Search all app roots, HTML templates, generated-page builders, tag-manager entries, and proxy rules. Look for simpleanalytics, sa_settings, sa_event, sa_pageview, noscript.gif, auto-events.js, and any first-party proxy.js.
Record the hostname used in your Simple Analytics dashboard, script attributes, event names, and any manual pageview logic. Check separate marketing pages and cached HTML, not just the main application.
If historical continuity matters, plan the historical data transfer before switching. Record the actual cutover time in UTC and keep Simple Analytics export access until your archive is verified. Installing the new tag starts new collection; it does not copy old data.
2. Register the matching hostname
Sign in and add the site in your dashboard, or follow the agent setup guide to register it through POST /api/sites with an Totallytics access token.
Use a bare hostname, such as example.com. Totallytics lowercases hostnames but does not strip www. If both www.example.com and example.com should report under one site, explicitly set data-hostname="example.com". If you want separate reports, register each hostname separately.
Match the hostname used for historical exports deliberately. Do not assume that a hostname accepted by Simple Analytics will be grouped the same way here. The script and optional pixel need the same intended destination. See Site management and Simple Analytics' hostname override documentation.
Site ownership and dashboard visibility are configured separately in Totallytics. A Simple Analytics API key cannot create or manage Totallytics sites.
3. Swap the script and optional pixel
Replace the existing core tag, preserving supported attributes.
Before:
<script async src="https://scripts.simpleanalyticscdn.com/latest.js"></script>
<noscript>
<img
src="https://queue.simpleanalyticscdn.com/noscript.gif"
alt=""
referrerpolicy="no-referrer-when-downgrade"
/>
</noscript>After, with explicit attribution to example.com:
<script
async
data-hostname="example.com"
src="https://analytics.bitgate.dev/latest.js"
></script>
<noscript>
<img
src="https://analytics.bitgate.dev/noscript.gif?hostname=example.com"
alt=""
referrerpolicy="no-referrer-when-downgrade"
/>
</noscript>Keep the tag once per shared HTML document. The optional noscript pixel belongs in the server-rendered body and does not inherit the script's settings. Its page path normally comes from Referer; retain an appropriate referrer policy or provide values explicitly from your template. In JSX, use referrerPolicy.
Update each independent entrypoint and the source of generated pages, then rebuild. Confirm the live HTML changed after deployment and any cache invalidation.
Do not just add a second default tag for a comparison period. The core scripts share window.sa_loaded and window.sa_event. The first script to execute can make the second exit, so neither pageview comparison nor custom-event delivery is reliable with a naive dual install.
An old Simple Analytics integrity hash will not match Totallytics' modified script. Review that policy rather than carrying the hash over. Totallytics does not serve Simple Analytics' alternate latest.dev.js, light, or SRI script endpoints.
4. Preserve settings and event behavior
Totallytics serves a vendored Simple Analytics v11 script with a different collector destination. It supports these configuration patterns:
| Setting | Migration check |
|---|---|
data-hostname |
Use the exact registered destination. |
data-ignore-pages |
Keep intended excluded paths and wildcard patterns. |
data-allow-params |
Retain the permitted query parameters, not arbitrary URL data. |
data-ignore-metrics |
Preserve deliberately excluded metrics. |
data-strict-utm |
Preserve the choice of strict UTM parameter names. |
data-non-unique-hostnames |
Keep the configured referrer hostnames treated as non-unique. |
data-path-overwriter, data-metadata-collector |
Keep the named global callbacks available before collection. |
window.sa_settings |
Keep configuration before the async tag; it can override tag attributes. |
This compatibility applies to the vendored script, not every feature or future release of Simple Analytics.
SPA and manual pageviews
Automatic collection handles the initial load, history.pushState, and popstate when the tracked path changes. Hash routing needs data-mode="hash". There is no direct replaceState listener; a query-only change does not create a new page path.
Do not add manual route tracking on top of automatic collection. If the existing integration uses data-auto-collect="false", keep its initial and navigation calls to window.sa_pageview() and wait for the script to load. That function is only exposed in manual mode. Consecutive identical tracked paths are suppressed, even when called manually.
See Installation and the upstream custom pageview guide.
Custom and automatic events
Existing custom calls keep their shape:
if (typeof window.sa_event === "function") {
window.sa_event("newsletter_signup", { source: "footer" });
}This guard avoids an error but skips the event if the tag is not ready. Preserve a pre-load queue if your application already needs one, or wait for load. Keep browser globals out of server-side execution. Event names replace non-alphanumeric runs with underscores and trim outer underscores. A callback is not proof of storage; it can also run after a local validation failure without sending a request.
Simple Analytics' automated-events helper is a separate script. Totallytics serves neither /auto-events.js nor /auto.js. Do not change only the hostname of that helper. Replace required outbound, download, or email-click tracking with explicit event calls, or audit and test your existing helper separately.
5. Update CSP and proxy destinations
Add the Totallytics origin to the existing policy:
script-src 'self' https://analytics.bitgate.dev;
connect-src 'self' https://analytics.bitgate.dev;
img-src 'self' https://analytics.bitgate.dev;Merge these sources; do not replace the whole CSP. Update script-src-elem if it is defined, and preserve existing nonces and hashes. No new 'unsafe-inline' allowance is required for the external tag.
/latest.js needs script permission. /simple.gif pageviews and events, append fallbacks, and /noscript.gif need image permission. Duration and scroll updates use navigator.sendBeacon() to /append, covered by connect-src. Remove old Simple Analytics origins only after confirming no retained component needs them.
First-party proxies need a separate audit
A local-looking /proxy.js may still send everything to Simple Analytics. Check its upstream source and the actual collector requests. The upstream proxy guide helps identify routes you may already have.
The simplest cutover uses the direct Totallytics tag and retires unused Simple Analytics proxy routes. Totallytics' tag has a fixed collector origin; proxying /latest.js alone does not make collection first-party. There is no Totallytics proxy-generator endpoint or supported runtime collector-base setting.
If you must keep first-party collection, treat it as a separate integration: the script's actual destination and proxy routes must agree. Preserve query strings for /simple.gif and /noscript.gif, and methods and bodies for /append or server-side /events. Do not cache collector responses. Recheck attribution and visitor counts after proxy changes rather than assuming header or IP behavior stayed identical.
6. Verify the live cutover
Build and deploy the updated site. On a real route in a normal browser, inspect /latest.js and the next /simple.gif request: check the destination, type=pageview, hostname, and path. Test one SPA navigation and an existing custom-event action. Check append traffic when leaving the page if duration and scroll matter to your setup.
Confirm the matching data in the dashboard or authenticated breakdown API. Use a narrow time window and compare the page count before and after a visit, or send one permitted, uniquely named verification event. The agent verification workflow includes commands.
A 200 response is only an acknowledgement, not proof that a row was stored. Localhost, bot/headless tests, Do Not Track, blockers, and an unregistered hostname can make a network-only check misleading. Poll reads while waiting for asynchronous processing; do not repeatedly send the test event.
Update reporting integrations separately
Totallytics does not mirror Simple Analytics' /{hostname}.json Stats API. Replace those clients with the Totallytics Stats API: /api/sites/{hostname}/overview and /api/sites/{hostname}/breakdown, using Unix-second from/to parameters and an owner bearer token for private sites.
Do not forward Simple Analytics' Api-Key, User-Id, fields, or date-format start/end parameters unchanged. Visitor calculation differs, and Totallytics' avg_duration_s is an average rather than Simple Analytics' median time on page. Exact metric parity is not a migration success criterion.
Historical data
Site owners can import historical analytics directly from /app/{hostname}/settings under Import data. The only supported import provider is SimpleAnalytics. CSV uploads and ongoing sync are not supported.
Import workflow
- Sign in as the site owner and open
/app/{hostname}/settings. - Under Import data, select provider SimpleAnalytics.
- Fill in User ID, API key, Source hostname, From, and To.
- Click Start import.
Simple Analytics API credentials serve as source configuration and are distinct from Totallytics Bearer tokens (see Authentication). The source hostname can differ from your Totallytics site to accommodate domain migrations. Credentials are validated against Simple Analytics before the import queues. Once queued, the background task runs independently of browser sessions. The settings panel polls every 3 seconds while active, showing queued, running, completed, failed, or canceled statuses alongside chunk progress, rows_imported, and errors.
Usage is limited to 1 active import per website and 20 new jobs per user per rolling 24 hours. The source API key is omitted from job responses. Its stored value is cleared when the job completes; failed and canceled jobs retain it for retry.
Cutover and date boundaries
- Date ranges use inclusive UTC days. Start dates must be on or after 2010-01-01, with at most 1,826 days between the two dates.
- The planner rounds the start date down to the first day of the selected month. Choose the first of the month deliberately; starting mid-month may pull in earlier data from that month.
- To cut over tracking cleanly, pick midnight UTC on your switch date and set the import end date to the preceding day. Importing the full cutover day after an intraday switch can overlap live tracking.
Deduplication and data fidelity
Totallytics does not maintain a deduplication ledger. Re-importing overlapping periods creates duplicate rows. Inspect existing reports and maintain external logs before scheduling runs.
- Counts: The counter covers completed chunks and includes pageviews, custom events, and engagement append records. It is not a pageview or visitor total, and partial writes from an interrupted chunk may not be included.
- Fidelity: Visitor identities are approximated. Original session continuity and custom event metadata are not restored. Source UUIDs are not persistent deduplication keys.
- Idempotency: Completed chunks are recorded, but interrupted chunks can replay partial writes.
Cancelstops later work without rolling back stored rows or aborting a chunk already writing.Retryresumes from the last cursor on failed or canceled runs; verify existing counts first. - API usage: Do not backfill data using
POST /events, which discards historical timestamps. Use the Imports API for historical records.
Verify the import
Verify imports by reviewing specific paths, events, and matching UTC intervals. Visitor numbers will not match Simple Analytics exactly.
To preserve full raw metadata, download standalone archives directly from Simple Analytics via their website export interface or data points export API.
Export pageviews and events separately. Select metadata fields explicitly if you need them in the archive, and inspect the CSV header and a sample before closing your Simple Analytics account. The source Stats API contains aggregated reports, not a replacement for raw exports.