Is Video to MP3 safe?
Video to MP3 sends a built-in API token with format and download requests to yt-gateway.vercel.app.
When you use the extension to convert or download media, its scripts contact yt-gateway.vercel.app for formats, downloads, progress, files, password status, and delivery statistics. Those requests include a hardcoded X-API-Token header embedded in the extension code.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Hardcoded API token sent to conversion gateway
The extension embeds a gateway API token used in conversion requests.
Opening the popup on a YouTube video or continuing a download sends your video URL, a persistent install ID, and the bundled X-API-Token header to yt-gateway.vercel.app.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You choose a video and ask the extension to fetch formats or start a download.
The extension sends requests to its conversion gateway with a bundled API token in the headers.
- Gateway API tokenX-API-Token: <redacted>
A service credential is shipped inside every installed copy and is sent back to the conversion service.
- Selected video URLhttps://www.youtube.com/watch?v=dQw4w9WgXcQ
The conversion gateway receives the video link you chose for format lookup or download processing.
- Installation IDX-User-ID: 4f5b7c0a-3a4d-4a9b-9a14-6e928c7e4b21
A persistent browser-generated ID lets the service associate repeated conversion requests from the same installation.
- Extension brandX-Brand: youtube-to-mp3
The gateway can tell which extension product sent the request.
- X-API-Token
- <redacted>
- Content-Type
- application/json
- X-API-Token
- <redacted>
- Content-Type
- application/json
The shipped scripts embed the token and reuse it in gateway headers
popup.js defaults and format request
popup.jsconst defaults = { serverUrl: "https://yt-gateway.vercel.app", apiToken: "<redacted>"};let gatewayUrl = defaults.serverUrl;let apiToken = defaults.apiToken;let userId = "";chrome.storage.sync.get({ ...defaults, userId: "", audioFormat: "mp3", audioQuality: "320"}, settings => { gatewayUrl = settings.serverUrl; apiToken = settings.apiToken; if (settings.userId) { userId = settings.userId; } else { userId = crypto.randomUUID(); chrome.storage.sync.set({ userId }); }});async function fetchFormats() { if (!selectedVideoUrl) return; let formats = readFormatCache(selectedVideoUrl); if (!formats) { const response = await fetch(`${gatewayUrl}/formats`, { method: "POST", headers: gatewayHeaders(), body: JSON.stringify({ url: selectedVideoUrl }) }); if (response.status === 403) throw new Error(t("invalidToken")); if (response.status === 429) throw new Error(t("serverBusy")); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || t("fetchFailed")); } formats = await response.json(); writeFormatCache(selectedVideoUrl, formats); } renderFormats(formats);}function gatewayHeaders() { return { "Content-Type": "application/json", "X-API-Token": apiToken, "X-User-ID": userId, "X-Brand": "youtube-to-mp3" };}download.js download, progress, file, status, and stats requests
download.jsconst defaults = { serverUrl: "https://yt-gateway.vercel.app", apiToken: "<redacted>"};let gatewayUrl = defaults.serverUrl;let apiToken = defaults.apiToken;let userId = "";async function startDownload() { const payload = { url: videoUrl, format_id: formatId, ext: extension, audio_quality: audioQuality }; if (startTime != null) payload.start = parseFloat(startTime); if (endTime != null) payload.end = parseFloat(endTime); const response = await fetch(`${gatewayUrl}/download`, { method: "POST", headers: gatewayHeaders(), body: JSON.stringify(payload) }); if (!response.ok) throw new Error(t("serverError")); const task = await response.json(); const workerUrl = task.worker_url || gatewayUrl; pollProgress(task.task_id, workerUrl);}async function pollProgress(taskId, workerUrl) { const response = await fetch(`${workerUrl}/progress/${taskId}`, { headers: { "X-API-Token": apiToken, "X-User-ID": userId } }); const progress = await response.json(); if (progress.status === "done") { chrome.downloads.download({ url: `${workerUrl}/file/${taskId}`, headers: [ { name: "X-API-Token", value: apiToken }, { name: "X-User-ID", value: userId }, { name: "X-Brand", value: "youtube-to-mp3" } ] }); }}function gatewayHeaders() { return { "Content-Type": "application/json", "X-API-Token": apiToken, "X-User-ID": userId, "X-Brand": "youtube-to-mp3" };}function sendPaywallStatus(status) { fetch(`${gatewayUrl}/pw-status`, { method: "POST", headers: gatewayHeaders(), body: JSON.stringify(status) }).catch(() => {});}function sendDeliveryStats(workerUrl, taskId, bytes, seconds) { fetch(`${workerUrl}/internal/delivery-stats`, { method: "POST", headers: gatewayHeaders(), body: JSON.stringify({ task_id: taskId, bytes, seconds }) }).catch(() => {});}options.js writes the same default token to settings
options.jsconst defaults = { serverUrl: "https://yt-gateway.vercel.app", apiToken: "<redacted>"};ready(() => { localizeSettingsPage(); populateLanguageSelect(); chrome.storage.sync.get(defaults, settings => { serverUrlInput.value = settings.serverUrl; apiTokenInput.value = settings.apiToken; });});saveButton.addEventListener("click", () => { const serverUrl = serverUrlInput.value.trim().replace(/\/+$/, ""); const apiToken = apiTokenInput.value.trim(); const language = languageSelect.value; saveLanguage(language); chrome.storage.sync.set({ serverUrl, apiToken }, () => { savedNotice.classList.add("visible"); setTimeout(() => { savedNotice.classList.remove("visible"); if (language !== currentLanguage()) location.reload(); }, 1500); });});- yt-gateway.vercel.app
Receives the format, download, progress, file, paywall-status, and delivery-stat requests for the conversion workflow.
- app.yt-video-to-mp3.club
Configured as the paywall SDK origin in the same extension bundle; not the endpoint carrying the X-API-Token header.
Where it sends data
Destinations our analysis observed Video to MP3 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.
- yt-gateway.vercel.app
Video to MP3 sends data to yt-gateway.vercel.app. No other extension we have analysed sends data here.