Is Read to Me safe?
Read to Me captures DOM snapshots of every page you visit and sends them to Sentry, and transmits page URLs to Amplitude on each user interaction.
The extension's content script runs on all websites and uses an embedded rrweb library to record full-page DOM snapshots and mutations, which are compressed and sent to Sentry's session replay endpoint. With maskAllText disabled, visible text on any visited page may be included in these recordings. Additionally, the extension sends the current page URL, language, and a persistent user identifier to Amplitude each time the user opens or interacts with its text-to-speech widget.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Read to Me sends page URLs to Amplitude on widget use
Read to Me's widget POSTs to Amplitude when opened on visited pages, recording the page URL, a persistent user ID, browser language, extension version, and event type.
The endpoint also receives the request's network IP address.
You open or interact with the Read to Me widget on a visited web page.
Observed events included widget openings after using the toolbar action on several websites.
The extension sends an analytics event to Amplitude with the page URL and a persistent extension user ID.
The same request also includes browser language, extension version, event type, and platform.
| Content-Type | application/json |
| Field | Value | Why it matters | |
|---|---|---|---|
Current page URL | https://en.wikipedia.org/wiki/Canary | Shows the exact page where you used the widget, which can reveal the site and page topic. | |
Extension user ID | 94fe7879-beff-4e0e-8ec8-3313f6bc8058 | Lets repeated widget events from the same browser be linked together over time. | |
Browser language | en-US | Adds regional context to the event and helps distinguish users or environments. | |
Extension version | 3.0.1 | Shows which installed version generated the event. | |
Widget event type | widget_opened | Shows what you did with the widget, such as opening it or pressing play. | |
Network IP address | 203.0.113.24 (illustrative) | The remote analytics endpoint receives the network address used to make the request. |
The sender builds an Amplitude event with window.location.href
var E = function() {
var t, r = (t = v().mark((function t(r, e) {
var n, o, i;
return v().wrap((function(t) {
for (;;) switch (t.prev = t.next) {
case 0:
return t.prev = 0, t.next = 3, p();
case 3:
return o = t.sent, i = {
api_key: "21a85efdb6d6e94bf888af0f5325c2b2",
events: [{
user_id: o,
event_type: r,
event_properties: g({
extension_version: chrome.runtime.getManifest().version,
time: (new Date).toISOString(),
url: r !== x.EXTENSION_INSTALLED ? window.location.href : null,
platform: "Chrome Extension",
country: navigator.language || (null === (n = navigator.languages) || void 0 === n ? void 0 : n[0])
}, e)
}]
}, t.next = 7, fetch("https://api2.amplitude.com/2/httpapi", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(i)
});
case 7:
t.sent.ok, t.next = 14;
break;
case 11:
t.prev = 11, t.t0 = t.catch(0);
case 14:
case "end":
return t.stop()
}
}), t, null, [
[0, 11]
])
})), function() {
var r = this,
e = arguments;
return new Promise((function(n, o) {
var i = t.apply(r, e);
function a(t) {
w(i, n, o, a, c, "next", t)
}
function c(t) {
w(i, n, o, a, c, "throw", t)
}
a(void 0)
}))
});
return function(t, e) {
return r.apply(this, arguments)
}
}(),
x = {
WIDGET_OPENED: "widget_opened",
WIDGET_CLOSED: "widget_closed",
EXTENSION_INSTALLED: "extension_installed",
TTS_PLAY_CLICKED: "tts_play_clicked",
WIDGET_OPEN_ERROR: "widget_open_error",
VOICES_LOAD_ERROR: "voices_load_error",
PDF_READER_OPENED: "pdf_reader_opened"
};function p() {
return y.apply(this, arguments)
}
function y() {
return (t = c().mark((function t() {
var r, e;
return c().wrap((function(t) {
for (;;) switch (t.prev = t.next) {
case 0:
return t.prev = 0, t.next = 3, s(["clientId"]);
case 3:
if (!(r = t.sent).clientId) {
t.next = 6;
break
}
return t.abrupt("return", r.clientId);
case 6:
return e = o(), t.next = 9, f({
clientId: e
});
case 9:
return t.abrupt("return", e);
case 13:
return t.prev = 13, t.t0 = t.catch(0), t.abrupt("return", o());
case 17:
case "end":
return t.stop()
}
}), t, null, [
[0, 13]
])
})), y = function() {
var r = this,
e = arguments;
return new Promise((function(n, o) {
var i = t.apply(r, e);
function a(t) {
u(i, n, o, a, c, "next", t)
}
function c(t) {
u(i, n, o, a, c, "throw", t)
}
a(void 0)
}))
}).apply(this, arguments);
var t
}- api2.amplitude.com
Amplitude HTTP API endpoint that receives widget analytics events containing the current page URL, persistent extension user ID, language, event type, and extension version.