DocsMigrations

Migrations

Migrate from Mixpanel

3 min readReviewed July 2026

Mixpanel’s Raw Event Export API returns newline-delimited JSON suitable for a staged historical import. Export a small time range, preserve the raw response, and transform the vendor-specific property envelope into an explicit GraphJSON event schema.

Start with the shared migration workflow.

Export raw events#

Follow Mixpanel’s official Raw Event Export API. Use the server hostname for your project’s data residency and authenticate from a trusted migration worker.

Export bounded date intervals and record:

  • requested start and end dates
  • Mixpanel project and region
  • raw file checksum and row count
  • earliest and latest source event time
  • retry attempts and complete failures

The response is JSON Lines: process it as a stream rather than loading a large export into memory.

Map the fields#

Mixpanel places event metadata and custom properties inside a properties object.

Mixpanel field GraphJSON target Notes
event event Apply reviewed renames only
properties.time request timestamp Verify units requested from the export
properties.distinct_id user_id or anonymous_id Decide from your identity model
properties.$insert_id event_id Prefix to preserve its source namespace
selected custom properties target event fields Allowlist and type-check
group keys account_id or explicit group field Map the group type intentionally

Do not blindly copy every $-prefixed field. Many are Mixpanel ingestion, device, campaign, or SDK details rather than durable product concepts.

Transform one record#

function toGraphJSON(source) {
  const props = source.properties || {};
  const timestamp = Number(props.time);

  if (!source.event || !Number.isInteger(timestamp)) {
    throw new Error("missing event or integer properties.time");
  }

  return {
    timestamp,
    body: {
      event: source.event,
      event_id: props.$insert_id
        ? `mixpanel:${props.$insert_id}`
        : undefined,
      user_id:
        typeof props.distinct_id === "string"
          ? props.distinct_id
          : undefined,
      account_id:
        typeof props.account_id === "string"
          ? props.account_id
          : undefined,
      plan: typeof props.plan === "string" ? props.plan : undefined,
      schema_version: 1,
      migration_source: "mixpanel",
    },
  };
}

Mixpanel can export timestamps in different representations depending on the endpoint option. Confirm the response contract for your request and normalize once. GraphJSON requires whole Unix seconds.

Remove undefined values, validate the serialized size, and quarantine invalid rows rather than coercing them silently.

Resolve distinct_id#

distinct_id may represent an authenticated user, an anonymous device, or an identifier affected by Mixpanel’s identity features. Classifying every value as user_id can merge unlike concepts in GraphJSON.

Sample records from before sign-in, after sign-in, and after an identity merge. Then decide:

  • known application IDs → user_id
  • anonymous browser/device IDs → anonymous_id
  • verified merge relationship → explicit identity_linked event or controlled rewrite

Preserve the unmodified source value in a scoped field during the migration pilot when it helps investigation, then decide whether it belongs in the final contract.

Import and reconcile#

Import to a temporary collection first if the mapping is still changing. Once the transform is stable, send batches of up to 50 records to the final collection with conservative concurrency.

Reconcile:

  • JSONL rows = accepted rows + quarantined rows
  • counts by event and UTC occurrence day
  • unique $insert_id versus unique destination event_id
  • unique authenticated and anonymous identifiers
  • required-property completeness
  • totals for revenue and other business facts

Mixpanel reports may apply project timezone, identity merging, exclusion rules, or computed definitions. Compare raw exported events first, then rebuild each derived metric with the same definition.

Cutover checklist#

  • The export server matches the Mixpanel project region.
  • properties.time is normalized to whole Unix seconds.
  • distinct_id classification has been tested across identity states.
  • Only approved custom properties are imported.
  • $insert_id is preserved when available.
  • Invalid rows are quarantined with reason codes.
  • A pilot interval and business metric both reconcile.
  • New production events are dual-written before retirement.
Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support