Is Netflix Party is now Teleparty safe?
Code analysis indicates Teleparty scrapes your complete Netflix viewing history and sends it to its own servers.
The extension code requests Netflix viewing history across all profiles in batches of 10,000 entries, including device names and last-watched timestamps. That data is transmitted to metis.teleparty.com/v1/record. This behavior has not been directly verified through dynamic analysis.
Who publishes itDelaware C Corporation - 1 other listing from the same operator, none carrying a finding
Delaware C Corporation - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same operator - 1 listing
Published under a different store account, but sharing the registered address, contact or declared legal entity this one gave the store.
Shared hosts - 5 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Streaming-Site URLs and Titles Sent to Teleparty Telemetry
When you start or join a watch party, Teleparty sends an event to metis.teleparty.com: your tab's URL/title, a permanent install ID, and, if signed in, your email.
Mirrored to PostHog.
Titles often reveal the show you're watching.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You start or join a Teleparty on a streaming site (Netflix, Disney+, Hulu, YouTube, HBO Max, etc.).
The same thing happens for several other in-extension events: video_start, video_pause, socket_open, socket_close, party_start, and others.
Teleparty quietly PUTs an event to metis.teleparty.com containing the tab's full URL and title, your install ID, and your email if you're signed in.
The same event is mirrored to PostHog (us.i.posthog.com) with the URL and title additionally repeated as current_tab_url / current_tab_title.
- Full URL of your active tabhttps://www.netflix.com/watch/81251335?trackId=14170287
The exact page you're on, including any tracking/session parameters in the URL.
- Tab titleThe Crown: Season 4: Episode 3 “Fairytale” - Netflix
The title shown in your browser tab, on Netflix or Hulu this is normally the show and episode you're watching.
- Install ID (perm_id)f6f29d1ca0c13901
A persistent identifier unique to your Teleparty install, sent with every event so the server can link your activity. See C#2121.
- Email address (if signed in)alice.smith@gmail.com
If you've signed in to Teleparty (via Google or Firebase auth), the email on your account is included in every event.
- Display nameAlice
The nickname you've set in Teleparty.
- Firebase user IDYk2qP9XfQTWvHb3a8nGJjLZmTk13
Internal Firebase auth UID for your Teleparty account, sent only if you're signed in.
- Subscription planpremium_monthly
Whether you're on free, premium_monthly, or premium_annual.
- Device + browser fingerprint{name: "chrome", version: "5.5.5", os_name: "Linux", platform: "Linux x86_64", install_id: "f6f29d1ca0c13901"}
Browser name and version, OS name, platform (Linux/Win/Mac), and the install ID repeated as device.install_id.
- App session IDapp-sess-71fe-9c12-4a30
A per-session ID so events fired in the same browser session can be grouped together.
- Client timestamp (ms)1744640329122
Wall-clock time the event was generated, to the millisecond.
- Extension version5.5.5
Which version of Teleparty you're running (added explicitly on the PostHog mirror as `extension_version`).
200 OK with empty body. Captured live during dynamic analysis: PUT https://metis.teleparty.com/v1/record (body=659 bytes) with page.url, page.name, user.perm_id, device.install_id all present and matching the values stored in chrome.storage.local.
- Content-Type
- application/json
{ "name": "video_start", "app": { "name": "chrome_ext", "version": "5.5.5" }, "app_session": "app-sess-71fe-9c12-4a30", "client_timestamp_ms": 1744640329122, "device": { "name": "chrome", "version": "5.5.5", "type": "browser", "manufacturer": "Linux x86_64", "model": "", "os_name": "Linux", "os_version": "", "install_id": "f6f29d1ca0c13901" }, "page": { "name": "The Crown: Season 4: Episode 3 “Fairytale” - Netflix", "url": "https://www.netflix.com/watch/81251335?trackId=14170287" }, "user": { "id": "Yk2qP9XfQTWvHb3a8nGJjLZmTk13", "email": "alice.smith@gmail.com", "name": "Alice", "signed_in": true, "plan": "premium_monthly", "on_trial": false, "firebase_id": "Yk2qP9XfQTWvHb3a8nGJjLZmTk13", "perm_id": "f6f29d1ca0c13901", "uuid": "e83b1d7e-9b22-4f59-9e2c-1c9b3eaa01a4" }}The code that does this, lifted from the shipping extension.
User block + payload assembly + send (We handler)
// In the We() event handler, after computing the active tab and current user state:const userBlock = { id: auth.currentUser?.uid ?? permId, email: auth.currentUser?.email, // present only if signed in name: settings.userNickname, signed_in: isSignedIn, plan: plan, // 'free' | 'premium_monthly' | 'premium_annual' on_trial: onTrial, firebase_id: auth.currentUser?.uid, perm_id: permId, // persistent install ID — see claim #2121 uuid: appUuid};event = this.Ms(pageBlock, event, userBlock, permId);// Mirror to PostHog with the URL and title added explicitly:try { const phEvent = Object.assign({}, event); phEvent.extension_version = MANIFEST_VERSION; phEvent.browser = 'chrome'; phEvent.is_prod = true; if (activeTab) { phEvent.current_tab_url = activeTab.url; phEvent.current_tab_title = activeTab.title; } if (destination === Destinations.BOTH || destination === Destinations.POSTHOG) { PostHog.sendEvent(event.name, permId, phEvent); }} catch (_) {}Ms() merges page/user/device/app into the event
// Ms() assembles the metis event by mutating a copy of the partial event:Ms(pageBlock, partialEvent, userBlock, permId) { const device = this.Ls(permId); // {name, version, type, manufacturer, model, os_name, os_version, install_id: permId} const appSession = this.Zt.getAppSession(); // 'app-sess-...' const tsMs = Date.now(); const app = { name: 'chrome_ext', version: MANIFEST_VERSION }; const event = Object.assign({}, partialEvent); event.app ||= app; event.app_session ||= appSession; event.device = device; event.client_timestamp_ms = tsMs; if (event.page == null) event.page = pageBlock; // pageBlock = { url: tab.url, name: tab.title } event.user = userBlock; return event;}Gs() does the PUT to metis
// Gs() is the actual outbound HTTP call:async Gs(event) { const res = await fetch('https://metis.teleparty.com/v1/record', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(event) }); if (res.status < 200 || res.status >= 300) throw new Error(res.statusText);}- metis.teleparty.com
Teleparty's own telemetry endpoint (path /v1/record). Receives the full URL + tab title + user/install ID for every party, video, and socket event.
- us.i.posthog.com
PostHog product analytics SaaS. Receives a mirror of the same events with current_tab_url/title attached. The PostHog write key is hardcoded in the extension.
Wraps fetch() in Teleparty's service worker so every PUT to metis.teleparty.com is logged to the DevTools console with the full event body, letting you see your own URL/title/email being shipped, in real time, without any extra tooling.
- Chrome with Developer mode enabled
- Teleparty (oocalimimngaihdkbihfgmpkcpnmlaoa) installed
// teleparty-event-sniffer.js// Paste this into the DevTools console of Teleparty's background service worker// (chrome://extensions → enable Developer mode → click the 'service worker' link// under "Netflix Party (Teleparty)"). Then start a watch party on Netflix /// Disney+ / etc. and watch the events scroll past.(function () { const origFetch = self.fetch.bind(self); self.fetch = async function (input, init) { try { const url = typeof input === 'string' ? input : (input && input.url) || ''; const isMetis = url.includes('metis.teleparty.com/v1/record'); const isPosthog = url.includes('us.i.posthog.com'); if ((isMetis || isPosthog) && init && typeof init.body === 'string') { let parsed; try { parsed = JSON.parse(init.body); } catch { parsed = init.body; } const tag = isMetis ? '[TELEPARTY → metis]' : '[TELEPARTY → posthog]'; console.groupCollapsed(`${tag} ${init.method || 'GET'} ${url}`); console.log('event name :', parsed && parsed.name); console.log('page :', parsed && parsed.page); console.log('user :', parsed && parsed.user); console.log('device :', parsed && parsed.device); console.log('full body :', parsed); console.groupEnd(); } } catch (_) {} return origFetch(input, init); }; console.log('[teleparty-event-sniffer] installed — start a watch party to see events');})();- 1Open chrome://extensions, enable Developer mode.
- 2Click 'service worker' under Teleparty.
- 3Paste the script in Console.
- 4Start/join a party on Netflix, Disney+, Hulu, or YouTube.
- 5Each PUT and PostHog mirror logs with full body.
Permanent Tracking ID Minted on Install, Sent With Every Event
On first run, Teleparty requests a fresh user ID from data3.netflixparty.com, saved as userId.
That ID goes into every event: metis.teleparty.com, data3.netflixparty.com/log-event, /log-experiment, PostHog, and the uninstall URL too.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You install Teleparty for the first time (or your previously stored userId is missing/invalid).
The extension calls home to data3.netflixparty.com/create-userId, gets back a fresh ID, and saves it permanently to local storage and into your uninstall URL.
From that point on, the same ID is sent on every analytics event so the operator can correlate everything you do across sessions and browser restarts as one user.
200 OK with response body "f6f29d1ca0c13901", a 16-character hex identifier. The extension immediately stores it as chrome.storage.local.userId. (On Edge the same call is made with `?browser=edge`.)
- Accept
- */*
Written on first run. userId is the identifier in every analytics event, persisting until uninstall or manual clear.
- Location
- chrome.storage.local
{ "date": "Mon Apr 14 2026 14:12:09 GMT+0000 (Coordinated Universal Time)", "userId": "f6f29d1ca0c13901", "recentlyUpdated": true, "recentlyUpdated3": true}- metis.teleparty.com / v1/recorduser.perm_id = "f6f29d1ca0c13901"
Sent in every event as `user.perm_id`, `user.id` (when not signed in), and `device.install_id`. See claim #2120 for the full event body.
- data3.netflixparty.com / log-event{"userId": "f6f29d1ca0c13901", "eventType": "socket_open", "sessionId": "sess-9c12-4a30"}
PUT request whose body is `{userId, eventType, sessionId}`, fired on various legacy/older event hooks.
- data3.netflixparty.com / log-experiment{"permId": "f6f29d1ca0c13901", "event": "experiment_viewed", "name": "new_invite_modal", "version": 2}
POST request body `{permId, event, name, version}`, used for A/B test bucketing on the same identifier.
- us.i.posthog.comdistinct_id = "f6f29d1ca0c13901"
Sent as the PostHog `distinct_id`, the field PostHog uses to collapse all events into a single user profile.
- https://www.teleparty.com/uninstallhttps://www.teleparty.com/uninstall?userId=f6f29d1ca0c13901&browser=chrome&version=5.5.5
Bound via setUninstallURL when the ID is minted. Uninstalling opens this URL with your userId baked in, tied to your tracked identifier.
The mint-on-install code and the validity check that re-mints on every startup.
Xe() — mint userId, store it, bake it into the uninstall URL
// Runs the first time the extension boots without a userId in storage.async function mintUserId() { const browser = navigator.userAgent.toLowerCase().includes('edg') ? 'edge' : 'chrome'; console.log('browser: ' + browser); const res = await fetch('https://data3.netflixparty.com/create-userId?browser=' + browser); const userId = await res.text(); // server returns a 16- or 36-char identifier as the response body const now = new Date(); if (isUserIdValid(userId)) { // Persist the ID forever (until uninstall + storage wipe). chrome.storage.local.set({ userId, recentlyUpdated: true, recentlyUpdated3: true, date: now.toString() }, () => console.log('Settings saved')); // Bake the same ID into the uninstall URL so the operator learns when // *this specific user* uninstalls. chrome.runtime.setUninstallURL( 'https://www.teleparty.com/uninstall' + '?userId=' + userId + '&browser=chrome' + '&version=' + encodeURIComponent(chrome.runtime.getManifest().version) ); }}isUserIdValid() — the entire 'validity' check
// The only sanity check on the server-issued ID is its length.function isUserIdValid(s) { return s.length === 16 || s.length === 36;}Re() — startup bootstrap, re-mints if missing/invalid
// Runs every time the background service worker starts.function bootstrapUserId() { try { storage.getAllItemsAsync().then(items => { if (items.userId && isUserIdValid(items.userId)) { // Refresh the uninstall URL with the existing ID + current version. chrome.runtime.setUninstallURL( 'https://www.teleparty.com/uninstall' + '?userId=' + items.userId + '&browser=chrome' + '&version=' + encodeURIComponent(MANIFEST_VERSION) ); } else { console.log('userId undefined/invalid in local storage -> now setting'); mintUserId(); // re-mint via /create-userId } }); } catch (_) {}}Same userId pushed to data3.netflixparty.com/log-event
// Older code-path: PUT a {userId, eventType, sessionId} record.async function logEvent(t) { try { const body = { userId: await this.getUserId(), // returns the persistent permId eventType: t.eventType, sessionId: t.sessionId }; console.log('event: ' + JSON.stringify(body)); await fetch('https://data3.netflixparty.com/log-event', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } catch (e) { console.log('log event error : ' + e); }}Same id (as permId) pushed to log-experiment for A/B bucketing
// A/B test bucketing keyed on the same persistent identifier.async function logExperiment(t) { try { const body = { permId: await this.getUserId(), event: t.eventType, name: t.experimentName, version: t.experimentVersion }; console.log('event: ' + JSON.stringify(body)); await fetch('https://data3.netflixparty.com/log-experiment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } catch (e) { console.log('log exp error : ' + e); }}- data3.netflixparty.com
Mints the ID at /create-userId on first run; receives it back at /log-event (field userId) and /log-experiment (field permId). Operated by Teleparty (formerly Netflix Party).
- metis.teleparty.com
Receives the same identifier as user.perm_id, user.id (when not signed in), and device.install_id on every event, see claim #2120.
- us.i.posthog.com
PostHog SaaS analytics. The same identifier is the PostHog distinct_id, so all of your events collapse into one PostHog profile.
- www.teleparty.com
Receives the identifier via the registered uninstall URL. Removing the extension opens teleparty.com/uninstall with userId, browser, version, informing the operator.
Dumps the persistent userId (and the matching uninstall URL) that Teleparty has stored locally, so you can verify with your own eyes that the same string is what the extension is sending out as perm_id / install_id / distinct_id.
- Chrome with Developer mode enabled
- Teleparty (oocalimimngaihdkbihfgmpkcpnmlaoa) installed
// teleparty-userid-dump.js// Paste into the DevTools console of Teleparty's background service worker// (chrome://extensions → enable Developer mode → click the 'service worker'// link under "Netflix Party (Teleparty)").(async function () { const items = await new Promise(res => chrome.storage.local.get(null, res)); console.group('[teleparty-userid-dump]'); console.log('userId in chrome.storage.local :', items.userId); console.log('recentlyUpdated :', items.recentlyUpdated); console.log('recentlyUpdated3 :', items.recentlyUpdated3); console.log('date first stored :', items.date); console.log('uninstall URL Chrome will open :', await new Promise(res => chrome.runtime.getUninstallURL ? res(chrome.runtime.getUninstallURL()) // older Chrome: surrogate by reconstructing : res('https://www.teleparty.com/uninstall?userId=' + items.userId))); console.groupEnd(); console.log('Tip: this exact userId is what Teleparty sends as perm_id / install_id /'); console.log(' distinct_id on every analytics event. Run the teleparty-event-sniffer'); console.log(' from claim #2120 alongside this to see it leaving the browser.');})();- 1Open chrome://extensions, enable Developer mode.
- 2Click 'service worker' under Teleparty.
- 3Paste the script in Console.
- 4Read the printed userId, date first stored, and uninstall URL, what Chrome visits, ID baked in, on removal.
Where it sends data
Destinations our analysis observed Teleparty contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- metis.teleparty.com
Teleparty sends data to metis.teleparty.com. 2 other extensions we have analysed send data here.