Import events from Mixpanel to AWS S3

In this post, we demonstrate how to use CloudFormation and AWS services to import data from Mixpanel and store it in S3. Specifically, we show you how to:

  • Create a Lambda function that polls Mixpanel for data via their import API
  • Store the data in S3
  • Use a CloudWatch event rule and a Lambda function target to trigger the Lambda function on a daily basis

We provide a complete CloudFormation template in YAML format, along with a JavaScript implementation of the Lambda function. This is a quick and easy way to get started with importing data from Mixpanel into your AWS environment.

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  MixpanelAPISecretParameter:
    Type: String
    Description: Your Mixpanel API secret for your project
    MinLength: 1
    NoEcho: true
  S3BucketNameParameter:
    Type: String
    Description: Your Mixpanel API Secret from the Mixpanel UI
    MinLength: 3
    MaxLength: 63
Resources:
   ScheduledEventRule:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: cron(0 12 * * ? *)
      State: ENABLED
      Targets:
        - Arn: !GetAtt MixpanelImportLambdaFunction.Arn
          Id: MixpanelImportLambdaFunction
  DestinationS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref S3BucketNameParameter
      AccessControl: Private
      VersioningConfiguration:
        Status: Enabled
  MixpanelImportLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          MIXPANEL_API_SECRET: !Ref MixpanelAPISecretParameter
          S3_BUCKET_NAME: !Ref S3BucketNameParameter
      Code:
        ZipFile: |
          'use strict';
          
          const https = require('https');
          const AWS = require('aws-sdk');
          exports.handler = async (event) => {
            // Replace these values with your own Mixpanel API key and secret
            const mixpanelApiSecret =  process.env.MIXPANEL_API_SECRET;
            const s3Bucket = process.env.S3_BUCKET_NAME;
            const today = new Date();
            const yesterday = new Date(new Date(today) - 1);
            const fromDate = yesterday.toISOString().slice(0, 10);
            const toDate = today.toISOString().slice(0, 10);

            const options = {
              hostname: 'data.mixpanel.com',
              port: 443,
              path: `/api/2.0/export?from_date=${fromDate}&to_date=${toDate}`,
              method: 'GET',
              headers: {
                'Authorization': 'Basic ' + 
                    new Buffer(mixpanelApiSecret + ":").toString("base64")
              }
            };

            // Make the request to the Mixpanel export API
            const importedDataAsString = await new Promise((resolve, reject) => {
              const req = https.request(options, (res) => {
                res.setEncoding('utf8');
                 let responseBody = '';
                res.on('data', (chunk) => {
                  responseBody += chunk;
                });
                res.on('end', () => {
                  console.error(responseBody)
                  resolve(responseBody);
                });
              });
              req.on('error', (error) => {
                reject(error);
              });
              req.end();
            });
    
            // Store the imported data in S3
            const s3 = new AWS.S3();
            await s3.putObject({
              Bucket: s3Bucket,
              Key: `${toDate}-data.json`,
              Body: importedDataAsString,
            }).promise();
          };
      Handler: index.handler
      Role: !GetAtt MixpanelImportLambdaExecutionRole.Arn
      Runtime: nodejs14.x
      Timeout: 60
  MixpanelImportLambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: mixpanel_import_lambda_policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: 'arn:aws:logs:*:*:*'
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource: !Join
                  - ''
                  - - 'arn:aws:s3:::'
                    - !Ref DestinationS3Bucket
                    - /*





YAML CloudFormation template