i need to normalize a profile property to be uniformly lower case. (There is a known specific value that needs to be changed). Previously this was done by JQL, the new solution must NOT use JQL. This property may have no value or one of many possible values. There are events with the same property, but these are not of interest and should be ignored.
Hi Joonas L., Please stand by as I search the Mixpanel knowledge base for an AI-powered answer. I normally take about 15 seconds or so. In case AI cannot answer your question, feel free to reach out to Mixpanel support at mixpanel.com/get-support. If you have follow-up questions for this thread, please tag me again with your question: @kapa.ai <follow-up question> 👋
Feel free to ask a follow-up question once I've answered by mentioning me with @kapa.ai
There’s partial coverage in the docs for what you want (normalizing a profile property value), but they don’t describe a one-click “lowercase all existing values” feature. You’ll need to: 1. Export the affected profiles, 2. Transform the property to lowercase outside Mixpanel, and 3. Write it back using the Engage (User Profiles) API with $set (or an SDK wrapper).Below is the closest supported pattern from the docs, adapted to your use case.---
1. Export the relevant profiles
The docs recommend using the Engage Query API or Data Pipelines for profile export instead of JQL. [JQL alt methods]> - User Profile export: Engage Query API or Data Pipelines. [JQL alt methods]Use that to pull all profiles where the target property is defined (and optionally equals the specific “bad” value you know you need to fix).---
2. Transform the property to lowercase
Do this in your own script / ETL (Python, SQL, etc.). The docs don’t show this step, but they do show that you can freely set profile properties via $set, so you’re allowed to overwrite the value. [User operators]You can also use this pass to: - Skip profiles where the property is empty / null / not set.
- Only change the known specific value (e.g. "Premium" → "premium"), leaving others untouched.---
3. Re‑write profiles with $set (no JQL)
Use the Engage “profile-set” endpoint or a server SDK to push the normalized value back.Direct HTTP Engage APIThe profile-set operation overwrites existing values. [Ingestion profile-set; User operators]LBKS# Example pattern from docs, adapted to your case PROJECT_TOKEN = ""LBKSimport json import requestsLBKSdef transform_to_mp_format(user): distinct_id = user.pop("user_id") # user now contains only properties to $set, including your normalized one return { "$distinct_id": distinct_id, "$token": PROJECT_TOKEN, "$ip": "0", # don't change geo "$set": user # includes your lowercased property }LBKSusers = get_users_from_database() # your exported & transformed users profiles = [transform_to_mp_format(u) for u in users]LBKSresp = [requests.post](http://requests.post)( "[https://api.mixpanel.com/engage](https://api.mixpanel.com/engage)", params={"verbose": "2"}, headers={"Content-Type": "application/json"}, data=json.dumps(profiles) ) print(resp.json())LBKSThis pattern (batching $set updates via /engage) is exactly what the docs recommend for bulk profile updates. [User operators]Or via SDKs- Python SDK: people_set() overwrites profile properties. [Python people methods]
- Java SDK: MessageBuilder.set() overwrites profile properties. [Java storing profiles]Example (Python SDK): LBKSfrom mixpanel import MixpanelLBKSmp = Mixpanel("YOUR_PROJECT_TOKEN")LBKS# for each profile you want to fix mp.people_set("some_distinct_id", { "your_property": "normalized_lowercase_value" }, meta={"$ip": 0})LBKS---
Notes specific to your requirements
- Ignore events: Use only the profile export / Engage API; don’t touch event properties. The $set operator in Engage only affects user profiles, not events. [User operators]
- Property may be missing: If the property is not present on a profile, simply don’t include it in $set for that profile; the docs recommend omitting unavailable properties rather than sending null/empty. [Tracking plan methodology]
- Known specific value: In your transform step, only change that value (e.g. if value == "Premium": value = "premium"), so you don’t accidentally alter other values.Because the docs don’t describe any built‑in “bulk lowercase” or in‑UI normalization for existing profile data, this export–transform–reimport pattern via Engage is the supported non‑JQL way to normalize a profile property.
kapa.ai is still learning and improving, please let me know how I did by reacting below.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
Let me check the knowledge base for you again. One moment! 📚
You’re running into a limitation of how Engage “behaviors” work: when you use an event selector / behaviors filter, Engage can return distinct_ids that match the event condition even if they don’t have a user profile property set. [Engage query]From the docs: - behaviors is used “when exporting user profiles using an event selector”. [Engage query]
- include_all_users=true (the default with filter_by_cohort) “include all distinct_ids even if they don’t have a user (or group) profile.” [Engage query]However, the knowledge sources do not document a segmentation-expression syntax for where that can explicitly say “only users where profile property X is set” (as opposed to event properties). They only say: > where: “An expression to filter users (or groups) by. See the expressions section above.” [Engage query]and that JQL’s People() can filter on user.properties.some_prop. [JQL People]Because the detailed “segmentation expressions” reference is not included in the provided docs, I cannot confidently give you a precise where expression that is guaranteed to only match profiles where a given profile property is set.What is clearly documented: - Engage Query can:
- Filter by where (segmentation expression). [Engage query]
- Limit returned properties via output_properties. [Engage query]
- Profiles may be missing properties; in reports this shows up as “undefined” when segmenting by a profile property. [Falsy values]Given the gaps in the docs you provided, the only fully documented, reliable approach I can describe is: 1. Query via Engage using either:
- a broad where (or even no where) plus output_properties including your target property, or
- your existing event-based behaviors filter if you need that subset. [Engage query]
2. Post‑filter client‑side: in your own code, keep only profiles where the returned $properties object actually contains the property and it’s not null/empty. This is consistent with how Mixpanel treats missing properties as “undefined” in reports. [Falsy values]Example shape of the Engage response you’d filter: LBKS{ "results": [ { "$distinct_id": 1, "$properties": { "your_property": "some value" } }, { "$distinct_id": 2, "$properties": { // your_property missing here -> drop this one in your code } } ] }LBKSTo directly answer your question: - The knowledge sources do not explicitly document a where syntax that guarantees “only profiles where profile property X is set” at the API level.
- The documented and safe pattern is to:
- Use Engage Query to fetch candidate profiles, then
- Filter in your own code to keep only those whose $properties include the property you care about.
kapa.ai is still learning and improving, please let me know how I did by reacting below.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
