So I was busy importing old “payment success” events into mixpanel.
I noticed about 30-40% of the events being rejected, for reasons unknown.
According to the documentation in https://developer.mixpanel.com/reference/events#import-events I should be able to check the error string. Unfortunately the response I'm receiving does not seem to contain key "error" with a error message…
I later on discovered I needed to set verbose to 1! I had forgotten to do that.
Then finally I was getting error messages explaining what was wrong with the event data I was sending.
Apparently all the rejected events were due to a lack of urlencoding of the “properties” data sent to mixpanel. My data contained characters like & and ó etc and this caused Mixpanel to reject.
Below is the code that enabled me to successfully import over 10K events with a shitload of properties. Just wanted to share with you, hopefully this helps somebody :-)
And if somebody has a smarter way of getting / converting the API secret , I'm all ears! (I suspect a MD5 transformation, but not sure).
$mixpanelProject = 'replace with Mixpanel Project Token';
$mixpanelAuthorization = 'replace with Mixpanel API Secret';
$properties['token'] = ;
$properties['time'] = strtotime($orderCreated);
$properties['Order ID'] = $orderID;
$properties['Order Amount'] = $orderAmount;
$properties['Product Names'] => ['Banana', 'Cucumber', 'Grapes', 'Avocado'];
// Add all properties you need
$mixPanelEventData = [
'event' => $eventName,
'properties' => $properties
$mixpanelJSON = json_encode($mixPanelEventData, JSON_PRETTY_PRINT);
$curlHandle = curl_init();
CURLOPT_URL => 'https://api-eu.mixpanel.com/import#past-events', // this url depends on where you store your project data EU or US
CURLOPT_RETURNTRANSFER => true, // Important to set this to true, because you want to receive the response object from Mixpanel
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_ENCODING => 'UTF-8',
CURLOPT_POSTFIELDS => 'data=' . urlencode($mixpanelJSON) . '&verbose=1', // Yeah this looks weird. I tried to set this up properly as an array. Didn't work :-(. Hugely important is the urlencode, not using it will not import anything.
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded; charset=utf-8', // Important if any of the data you're importing contains UTF8 characters
'Authorization: Basic ' . $mixpanelAuthorization
/* Ok maybe I'm an idiot but getting hold of this authorization key was not exactly easy. Documentation points toward the API secret. Using that secret key does NOT work.
However go to https://developer.mixpanel.com/reference/events#import-events click on the "Send" button, paste the API secret and now copy the code appearing in the screen
on the right hand side after --header 'Authorization: Basic <XXXXXXX KEY XXXXXXX>
That's the key that makes everything work. */
$response = curl_exec($curlHandle);
$result = json_decode($response, true);
if (curl_errno($curlHandle)) throw new \Exception('cURL error: ' . curl_error($curlHandle));
if ($result['status'] == 0) throw new \Exception('Mixpanel error: ' . $result['error'] . ' Order ID: ' . $properties['Order ID']);
$httpcode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
if ($httpcode <> 200) throw new \Exception('Mixpanel httpcode: ' . $httpcode);