The Scenario
The legacy database migration wrapped up on Friday. By Monday morning you're looking at a sheet with 5,000 rows of historical purchase events — distinct_id, amount, timestamp, product_id — that need to land in PostHog before Tuesday's funnel analysis. The data engineer who handled the export is off-site. The funnels in PostHog currently show a gap that goes back six months, and your product lead wants to close it before the quarterly review.
The bad version:
- Write a Python script to read the sheet, format each row as a PostHog capture event, and POST them one at a time to the /capture endpoint — discover mid-run that the batch endpoint exists and does this more efficiently
- Realize your timestamp column is formatted as MM/DD/YYYY and PostHog expects ISO 8601, so spend an hour normalizing every cell before the script will accept them
- Run the script, watch it time out at row 3,400, restart from that row, discover your distinct_ids have trailing spaces that create duplicate person profiles
That's a full day of work for a data problem that should have taken minutes. And the quarterly review is tomorrow morning.
SheetXAI handles this without a line of code.
The Easy Way: One Prompt in SheetXAI
SheetXAI is an AI agent that lives inside your Google Sheet. It reads the data, understands the schema, and through its built-in PostHog integration it formats and sends the batch ingest call for you. No script, no API client, no timestamp normalization by hand.
For each row in this sheet, send a PostHog event named 'legacy_purchase' with distinct_id from column A, amount from column B, and timestamp from column C as event properties. Use the batch endpoint and handle any timestamp format conversion needed.
What You Get
- Each row becomes one PostHog event with the correct property mapping
- Timestamps are converted to ISO 8601 automatically before the request is sent
- Rows that fail (missing distinct_id, malformed timestamp) are flagged with an error note in column E
- A summary count of successfully ingested vs. failed rows is written below the data
What If the Data Is Not Quite Ready
Timestamps are in the wrong format
The timestamp column C is formatted as MM/DD/YYYY HH:MM. Convert each value to ISO 8601 before sending the PostHog batch event, using UTC timezone, and write the converted timestamp into column D so I can verify it.
Some rows are missing distinct_ids
Before sending any PostHog events from this sheet, check column A for blank or null distinct_ids. Write "SKIP - no distinct_id" in column E for those rows, then send events only for rows where column A has a value.
Events need extra properties from a second tab
Join the "Events" tab (distinct_id in column A, amount in column B, timestamp in column C) with the "Users" tab (distinct_id in column A, country in column B, plan in column C) on distinct_id, then send each matched row as a PostHog 'legacy_purchase' event with amount, timestamp, country, and plan as properties.
Full cleanup and ingest in one shot
In the "Raw Events" sheet: strip trailing spaces from column A, convert column C timestamps from MM/DD/YYYY to ISO 8601, remove any rows where column B is blank or zero, then batch-send the cleaned rows to PostHog as 'legacy_purchase' events with distinct_id from column A, amount from column B, and timestamp from column C. Write event status into column D.
The pattern is to ask for data normalization and the API action in a single prompt — SheetXAI handles both passes without needing you to clean the sheet first.
Try It
Get the 7-day free trial of SheetXAI and open any Google Sheet with a historical event log — purchase records, activation milestones, legacy session data — then ask it to ingest the rows into PostHog. Or explore related tasks: Bulk Add Users to a PostHog Static Cohort From a Google Sheet and the PostHog hub.
