Fix · HubSpot + Zoom
Get Zoom webinar poll answers, registration questions and per-session attendance into HubSpot
The problem
“Poll and survey answers never make it into HubSpot”
Short answer: Run a small sync after each webinar occurrence. Pull registrant answers from GET /v2/webinars/{id}/registrants (custom_questions), attendance from GET /v2/report/webinars/{uuid}/participants, and polls from GET /v2/past_webinars/{uuid}/polls, then batch update existing HubSpot contacts matched by email. Leave attendees without an email in a review file instead of guessing.
The usual workaround is exporting CSVs from Zoom and importing them by hand after every webinar. The data is all in the Zoom API, keyed by the occurrence UUID, so a recurring webinar can be split by session. The script plans every update as a CSV first, and only writes to HubSpot when you add --apply.
Download
- zoom_hubspot_sync.py: the sync script (dry-run by default, --apply to write, --create-missing off by default)
- README.md: setup, scopes, properties to create and limits
- zoom-webinar-hubspot-sync.zip: everything, including sample data and the offline test
Step by step
1. Create the Zoom app
In the Zoom App Marketplace create a Server-to-Server OAuth app with read scopes for webinars and reports. Set ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID and ZOOM_CLIENT_SECRET as environment variables.
2. Create the HubSpot private app
Give it contacts read and write scopes and set HUBSPOT_TOKEN.
3. Pick the occurrence
For a recurring webinar, pass --occurrence-uuid. Without it, the script lists past instances from GET /v2/past_webinars/{id}/instances. UUIDs that start with / or contain // are double URL-encoded for you.
4. Dry run
Nothing is written. You get out/planned_updates.csv, out/unmatched.csv and out/properties_to_create.csv.
python3 zoom_hubspot_sync.py --webinar-id 123456789 --occurrence-uuid 'THE_UUID'5. Create the properties
Create the contact properties listed in properties_to_create.csv: zoom_attended, zoom_minutes_attended, zoom_last_webinar_id, zoom_last_webinar_date, plus one zoom_q_ property per registration question and one zoom_poll_ property per poll question.
6. Check the review file
Someone who joined twice is merged into one row with total minutes. People who joined without an email go to unmatched.csv for a person to look at.
7. Write to HubSpot
Re-run with --apply. Contacts are found with batch/read by email and updated with batch/update. New contacts are only created if you also pass --create-missing, which stops every webinar from filling the CRM with junk contacts.
What this does not cover
- Tested offline against sample data only, not against the live Zoom or HubSpot APIs. Zoom scope names and endpoints change, so check the current docs.
- Properties hold the latest webinar only. Use a custom event or a webinar object if you need full history per contact.
- Zoom Events is not covered.
Want this done for you?
Kingdom of Kumar runs it for $500/month, first automation live in 48 hours. Claude + Grok Bot + $500/month (my fee). Email me the task you hate doing every week.