12 min read
Migration Guide
Complete guide to migrating from Google Analytics, Mixpanel, Amplitude, and other platforms to pxlpeak.
Introduction
Migrating analytics platforms requires careful planning to ensure data continuity and minimal disruption. This guide provides step-by-step instructions for migrating from major analytics platforms to pxlpeak.
Migration Overview
code
Migration Process:
┌─────────────────────────────────────────────────────────────────────────────┐
│ MIGRATION TIMELINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ PHASE 1: PLANNING (Week 1-2) │
│ ├── Audit current implementation │
│ ├── Map events to pxlpeak schema │
│ ├── Plan parallel tracking period │
│ └── Set success criteria │
│ │
│ PHASE 2: IMPLEMENTATION (Week 2-3) │
│ ├── Install pxlpeak tracking │
│ ├── Configure events and properties │
│ ├── Set up conversions and goals │
│ └── QA and validation │
│ │
│ PHASE 3: PARALLEL TRACKING (Week 3-6) │
│ ├── Run both platforms simultaneously │
│ ├── Compare data between platforms │
│ ├── Fix discrepancies │
│ └── Build confidence in pxlpeak data │
│ │
│ PHASE 4: CUTOVER (Week 6-7) │
│ ├── Migrate dashboards and reports │
│ ├── Train team on pxlpeak │
│ ├── Remove old tracking (optional) │
│ └── Document migration │
│ │
└─────────────────────────────────────────────────────────────────────────────┘Migrating from Google Analytics 4
GA4 Event Mapping
code
// Map GA4 events to pxlpeak
const ga4EventMapping = {
// Automatic events
'page_view': {
pxlpeak_event: 'page_viewed',
properties: {
'page_location': 'page_url',
'page_title': 'page_title',
'page_referrer': 'referrer'
}
},
'session_start': {
pxlpeak_event: 'session_started',
properties: {
'ga_session_id': 'ga_session_id'
}
},
'first_visit': {
pxlpeak_event: 'first_visit',
properties: {}
},
// Enhanced measurement
'scroll': {
pxlpeak_event: 'page_scrolled',
properties: {
'percent_scrolled': 'scroll_depth'
}
},
'click': {
pxlpeak_event: 'link_clicked',
properties: {
'link_url': 'link_url',
'link_text': 'link_text',
'outbound': 'is_outbound'
}
},
'file_download': {
pxlpeak_event: 'file_downloaded',
properties: {
'file_name': 'file_name',
'file_extension': 'file_type'
}
},
'video_start': {
pxlpeak_event: 'video_started',
properties: {
'video_title': 'video_title',
'video_provider': 'video_provider',
'video_url': 'video_url'
}
},
// Ecommerce events
'view_item': {
pxlpeak_event: 'product_viewed',
properties: {
'items': 'products',
'currency': 'currency',
'value': 'value'
}
},
'add_to_cart': {
pxlpeak_event: 'product_added_to_cart',
properties: {
'items': 'products',
'currency': 'currency',
'value': 'value'
}
},
'begin_checkout': {
pxlpeak_event: 'checkout_started',
properties: {
'items': 'products',
'currency': 'currency',
'value': 'cart_total',
'coupon': 'coupon_code'
}
},
'purchase': {
pxlpeak_event: 'order_completed',
properties: {
'transaction_id': 'order_id',
'value': 'order_total',
'currency': 'currency',
'items': 'products',
'tax': 'tax',
'shipping': 'shipping'
}
}
};
// Transform GA4 items array to pxlpeak products
function transformGA4Items(items: GA4Item[]): PxlpeakProduct[] {
return items.map(item => ({
product_id: item.item_id,
product_name: item.item_name,
product_brand: item.item_brand,
product_category: item.item_category,
product_variant: item.item_variant,
product_price: item.price,
product_quantity: item.quantity
}));
}GA4 Migration Script
code
// Wrapper to track to both GA4 and pxlpeak during migration
class DualTracking {
private ga4Enabled = true;
private pxlpeakEnabled = true;
trackEvent(ga4Event: string, params: Record<string, any>) {
// Track to GA4
if (this.ga4Enabled && typeof gtag !== 'undefined') {
gtag('event', ga4Event, params);
}
// Track to pxlpeak with mapping
if (this.pxlpeakEnabled) {
const mapping = ga4EventMapping[ga4Event];
if (mapping) {
const pxlpeakProps = this.transformProperties(params, mapping.properties);
pxlpeak.track(mapping.pxlpeak_event, pxlpeakProps);
}
}
}
private transformProperties(
ga4Props: Record<string, any>,
mapping: Record<string, string>
): Record<string, any> {
const result: Record<string, any> = {};
for (const [ga4Key, pxlpeakKey] of Object.entries(mapping)) {
if (ga4Props[ga4Key] !== undefined) {
if (ga4Key === 'items') {
result[pxlpeakKey] = transformGA4Items(ga4Props[ga4Key]);
} else {
result[pxlpeakKey] = ga4Props[ga4Key];
}
}
}
return result;
}
// Disable GA4 after migration
disableGA4() {
this.ga4Enabled = false;
}
}
// Usage during migration
const tracker = new DualTracking();
// Track purchase (goes to both platforms)
tracker.trackEvent('purchase', {
transaction_id: 'T12345',
value: 99.99,
currency: 'USD',
items: [
{
item_id: 'SKU001',
item_name: 'Premium Plan',
price: 99.99,
quantity: 1
}
]
});GA4 Data Export
code
// Export historical data from GA4 via BigQuery
const ga4DataExport = {
// GA4 BigQuery export query
query: `
SELECT
event_date,
event_name,
event_timestamp,
user_pseudo_id,
user_id,
device.category as device_type,
device.web_info.browser as browser,
geo.country as country,
traffic_source.source,
traffic_source.medium,
traffic_source.name as campaign,
(SELECT value.string_value FROM UNNEST(event_params)
WHERE key = 'page_location') as page_url,
(SELECT value.int_value FROM UNNEST(event_params)
WHERE key = 'ga_session_id') as session_id
FROM \`project.analytics_123456789.events_*\`
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20260112'
`,
// Export format
format: 'JSON',
// Import to pxlpeak via API
importEndpoint: 'POST /v1/import/historical'
};
// Import historical data to pxlpeak
async function importGA4Data(exportedData: GA4ExportRow[]) {
const batches = chunk(exportedData, 1000);
for (const batch of batches) {
const events = batch.map(row => ({
event: mapGA4Event(row.event_name),
timestamp: new Date(row.event_timestamp / 1000).toISOString(),
user_id: row.user_id,
anonymous_id: row.user_pseudo_id,
properties: {
page_url: row.page_url,
device_type: row.device_type,
browser: row.browser,
country: row.country,
source: row.source,
medium: row.medium,
campaign: row.campaign
}
}));
await pxlpeak.import.batch(events);
}
}Migrating from Mixpanel
Mixpanel Event Mapping
code
// Map Mixpanel events and properties to pxlpeak
const mixpanelMapping = {
// Default events
'$pageview': {
pxlpeak_event: 'page_viewed',
properties: {
'$current_url': 'page_url',
'$referrer': 'referrer',
'$browser': 'browser',
'$device': 'device_type'
}
},
// User profile properties
profile_mapping: {
'$email': 'email_hash', // Hash before storing
'$name': null, // Don't migrate PII
'$created': 'signup_date',
'$last_seen': 'last_seen',
'plan': 'plan',
'company': 'company_name'
},
// Super properties → pxlpeak traits
super_properties: {
'user_type': 'user_type',
'subscription_plan': 'plan',
'company_size': 'company_size'
}
};
// Mixpanel to pxlpeak tracking wrapper
class MixpanelMigrationWrapper {
track(event: string, properties?: Record<string, any>) {
// Original Mixpanel tracking
if (typeof mixpanel !== 'undefined') {
mixpanel.track(event, properties);
}
// pxlpeak tracking with property transformation
const pxlpeakProps = this.transformProperties(properties || {});
pxlpeak.track(this.transformEventName(event), pxlpeakProps);
}
identify(distinctId: string) {
// Mixpanel identify
if (typeof mixpanel !== 'undefined') {
mixpanel.identify(distinctId);
}
// pxlpeak identify
pxlpeak.identify({ user_id: distinctId });
}
people_set(properties: Record<string, any>) {
// Mixpanel people.set
if (typeof mixpanel !== 'undefined') {
mixpanel.people.set(properties);
}
// pxlpeak traits (filter out PII)
const safeTraits = this.filterPII(properties);
pxlpeak.identify({ traits: safeTraits });
}
private transformEventName(event: string): string {
// Convert Mixpanel naming to pxlpeak convention
return event
.replace(/\s+/g, '_')
.toLowerCase()
.replace(/[^a-z0-9_]/g, '');
}
private transformProperties(props: Record<string, any>): Record<string, any> {
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(props)) {
// Skip Mixpanel internal properties
if (key.startsWith('$') || key.startsWith('mp_')) {
continue;
}
// Transform key to snake_case
const newKey = key
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.replace(/^_/, '');
result[newKey] = value;
}
return result;
}
private filterPII(props: Record<string, any>): Record<string, any> {
const piiKeys = ['$email', '$name', '$phone', 'email', 'name', 'phone'];
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(props)) {
if (!piiKeys.includes(key)) {
result[key] = value;
}
}
return result;
}
}Mixpanel Data Export
code
// Export data from Mixpanel
const mixpanelExport = {
// Use Mixpanel Export API
endpoints: {
events: 'https://data.mixpanel.com/api/2.0/export',
profiles: 'https://mixpanel.com/api/2.0/engage'
},
// Export parameters
params: {
from_date: '2025-01-01',
to_date: '2026-01-12',
event: null // null for all events
}
};
// Export and transform Mixpanel data
async function exportMixpanelData(apiSecret: string) {
// Export events
const eventsResponse = await fetch(mixpanelExport.endpoints.events, {
headers: {
'Authorization': `Basic ${btoa(apiSecret + ':')}`
},
body: JSON.stringify(mixpanelExport.params)
});
const events = await eventsResponse.json();
// Transform and import to pxlpeak
const pxlpeakEvents = events.map((event: MixpanelEvent) => ({
event: transformEventName(event.event),
timestamp: new Date(event.properties.time * 1000).toISOString(),
user_id: event.properties.$user_id,
anonymous_id: event.properties.distinct_id,
properties: transformProperties(event.properties)
}));
// Import in batches
await pxlpeak.import.batch(pxlpeakEvents);
}Migrating from Amplitude
Amplitude Event Mapping
code
// Map Amplitude events to pxlpeak
const amplitudeMapping = {
// Event name transformation
event_transform: (name: string) => {
// Amplitude typically uses sentence case
// Transform to snake_case
return name
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.replace(/\s+/g, '_')
.replace(/^_/, '')
.replace(/_+/g, '_');
},
// User properties mapping
user_properties: {
'user_type': 'user_type',
'subscription_plan': 'plan',
'company_name': 'company',
'signup_date': 'signup_date',
'last_active': 'last_seen'
},
// Device properties
device_properties: {
'device_type': 'device_type',
'os_name': 'os',
'os_version': 'os_version',
'device_brand': 'device_brand',
'device_model': 'device_model'
}
};
// Amplitude to pxlpeak wrapper
class AmplitudeMigrationWrapper {
logEvent(eventName: string, eventProperties?: Record<string, any>) {
// Original Amplitude tracking
if (typeof amplitude !== 'undefined') {
amplitude.logEvent(eventName, eventProperties);
}
// pxlpeak tracking
const transformedName = amplitudeMapping.event_transform(eventName);
pxlpeak.track(transformedName, this.transformProperties(eventProperties));
}
setUserId(userId: string) {
// Amplitude
if (typeof amplitude !== 'undefined') {
amplitude.setUserId(userId);
}
// pxlpeak
pxlpeak.identify({ user_id: userId });
}
setUserProperties(properties: Record<string, any>) {
// Amplitude
if (typeof amplitude !== 'undefined') {
const identify = new amplitude.Identify();
for (const [key, value] of Object.entries(properties)) {
identify.set(key, value);
}
amplitude.identify(identify);
}
// pxlpeak
pxlpeak.identify({ traits: this.transformUserProperties(properties) });
}
private transformProperties(props?: Record<string, any>): Record<string, any> {
if (!props) return {};
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(props)) {
// Transform camelCase to snake_case
const newKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
result[newKey] = value;
}
return result;
}
private transformUserProperties(props: Record<string, any>): Record<string, any> {
const result: Record<string, any> = {};
for (const [ampKey, pxlKey] of Object.entries(amplitudeMapping.user_properties)) {
if (props[ampKey] !== undefined) {
result[pxlKey] = props[ampKey];
}
}
return result;
}
}Amplitude Data Export
code
// Export from Amplitude
const amplitudeExport = {
// Amplitude Export API
endpoint: 'https://amplitude.com/api/2/export',
// Export parameters
params: {
start: '20250101T00',
end: '20260112T23'
}
};
// Export and import Amplitude data
async function migrateAmplitudeData(apiKey: string, secretKey: string) {
// Get export download URL
const response = await fetch(
`${amplitudeExport.endpoint}?start=${amplitudeExport.params.start}&end=${amplitudeExport.params.end}`,
{
headers: {
'Authorization': `Basic ${btoa(apiKey + ':' + secretKey)}`
}
}
);
// Download and parse data
const zipFile = await response.blob();
const events = await parseAmplitudeExport(zipFile);
// Transform to pxlpeak format
const pxlpeakEvents = events.map((event: AmplitudeEvent) => ({
event: amplitudeMapping.event_transform(event.event_type),
timestamp: new Date(event.event_time).toISOString(),
user_id: event.user_id,
anonymous_id: event.device_id,
properties: {
...transformEventProperties(event.event_properties),
device_type: event.device_type,
platform: event.platform,
os_name: event.os_name,
country: event.country
}
}));
// Import to pxlpeak
await pxlpeak.import.batch(pxlpeakEvents);
}Parallel Tracking Period
Validation Checklist
code
// Data validation during parallel tracking
const parallelTrackingValidation = {
daily_checks: [
{
metric: 'Total events',
tolerance: '5%',
query: 'Compare daily event counts'
},
{
metric: 'Page views',
tolerance: '3%',
query: 'Compare page view counts'
},
{
metric: 'Unique users',
tolerance: '10%',
query: 'Compare unique user counts'
},
{
metric: 'Conversion events',
tolerance: '2%',
query: 'Compare conversion counts'
}
],
weekly_checks: [
{
metric: 'Conversion rate',
tolerance: '5%',
query: 'Compare funnel conversion rates'
},
{
metric: 'Traffic sources',
tolerance: '5%',
query: 'Compare source/medium breakdown'
},
{
metric: 'Device breakdown',
tolerance: '5%',
query: 'Compare device type distribution'
}
],
discrepancy_investigation: [
'Check timestamp handling differences',
'Verify user identification logic',
'Compare bot filtering rules',
'Check sampling differences',
'Verify event triggering conditions'
]
};
// Comparison dashboard
function createComparisonDashboard() {
return {
widgets: [
{
title: 'Daily Events Comparison',
type: 'line_chart',
metrics: [
{ source: 'old_platform', metric: 'total_events' },
{ source: 'pxlpeak', metric: 'total_events' }
]
},
{
title: 'Conversion Comparison',
type: 'bar_chart',
metrics: [
{ source: 'old_platform', metric: 'conversions' },
{ source: 'pxlpeak', metric: 'conversions' }
]
},
{
title: 'Discrepancy %',
type: 'kpi',
calculation: '(pxlpeak - old) / old * 100'
}
]
};
}Common Discrepancies
code
// Known discrepancy causes and solutions
const discrepancySolutions = {
event_count_difference: {
causes: [
'Different bot filtering rules',
'Sampling in old platform',
'Event deduplication logic',
'Timezone handling'
],
solutions: [
'Align bot filtering configuration',
'Disable sampling for comparison period',
'Document deduplication differences',
'Standardize on UTC timestamps'
]
},
user_count_difference: {
causes: [
'Different identity resolution',
'Cookie vs localStorage',
'Cross-domain handling',
'Anonymous user treatment'
],
solutions: [
'Align identity resolution approach',
'Use consistent storage method',
'Configure cross-domain tracking',
'Document expected differences'
]
},
conversion_difference: {
causes: [
'Different attribution windows',
'Conversion definition differences',
'Event timing differences'
],
solutions: [
'Align attribution windows',
'Match conversion definitions exactly',
'Verify event trigger timing'
]
}
};Dashboard Migration
Report Mapping
code
// Map old reports to pxlpeak
interface ReportMigration {
old_report: string;
pxlpeak_equivalent: string;
customization_needed: string[];
notes: string;
}
const reportMigrations: ReportMigration[] = [
{
old_report: 'GA4 Acquisition Overview',
pxlpeak_equivalent: 'Traffic Sources Dashboard',
customization_needed: ['Add specific channels', 'Configure attribution'],
notes: 'pxlpeak uses different channel definitions'
},
{
old_report: 'GA4 Engagement Overview',
pxlpeak_equivalent: 'Engagement Dashboard',
customization_needed: ['Configure scroll depth events'],
notes: 'More granular engagement tracking available'
},
{
old_report: 'Mixpanel Funnels',
pxlpeak_equivalent: 'Funnel Analysis',
customization_needed: ['Recreate funnel steps'],
notes: 'Verify event mapping for each step'
},
{
old_report: 'Amplitude Retention',
pxlpeak_equivalent: 'Cohort Analysis',
customization_needed: ['Configure cohort definitions'],
notes: 'May need to adjust date ranges'
}
];
// Create equivalent pxlpeak dashboard
async function createMigratedDashboard(oldDashboard: Dashboard) {
const widgetMappings = oldDashboard.widgets.map(widget => ({
type: mapWidgetType(widget.type),
metrics: widget.metrics.map(mapMetric),
dimensions: widget.dimensions.map(mapDimension),
filters: widget.filters.map(mapFilter)
}));
return pxlpeak.dashboards.create({
name: `Migrated: ${oldDashboard.name}`,
widgets: widgetMappings
});
}Team Training
Training Plan
code
## Migration Training Plan
### Session 1: Platform Overview (1 hour)
- pxlpeak interface walkthrough
- Key differences from old platform
- Navigation and common tasks
### Session 2: Tracking & Events (1 hour)
- Event naming conventions
- Property standards
- How to validate tracking
### Session 3: Reporting & Dashboards (1 hour)
- Creating and customizing dashboards
- Building reports
- Scheduling and sharing
### Session 4: Advanced Features (1 hour)
- Attribution analysis
- Funnel analysis
- Cohort analysis
- Custom queries
### Resources
- Documentation links
- Video tutorials
- Slack channel for questions
- Office hours scheduleMigration Checklist
code
## Complete Migration Checklist
### Phase 1: Planning
- [ ] Document current tracking implementation
- [ ] Create event mapping document
- [ ] Identify key reports to migrate
- [ ] Plan parallel tracking period
- [ ] Get stakeholder buy-in
### Phase 2: Implementation
- [ ] Install pxlpeak SDK
- [ ] Implement event tracking
- [ ] Set up user identification
- [ ] Configure conversions
- [ ] QA all tracking
### Phase 3: Parallel Tracking
- [ ] Run both platforms 2-4 weeks
- [ ] Compare daily metrics
- [ ] Investigate discrepancies
- [ ] Document differences
- [ ] Get sign-off on data quality
### Phase 4: Cutover
- [ ] Migrate dashboards
- [ ] Train all users
- [ ] Update documentation
- [ ] Disable old platform tracking
- [ ] Archive old platform data
### Post-Migration
- [ ] Monitor for issues
- [ ] Collect user feedback
- [ ] Optimize implementation
- [ ] Document lessons learnedNext Steps
- Assess Current State - Document your existing implementation
- Create Mapping - Map all events and properties
- Plan Timeline - Set realistic migration schedule
- Start Parallel Tracking - Install pxlpeak alongside existing
- Validate & Cut Over - Ensure data quality before switching
Related Guides: