Is Pixiv Downloader safe?

High risk

Pixiv Downloader sends download details and Google account identifiers to a Google Apps Script endpoint.

When a Pixiv download completes, the extension posts a log that includes your Google email and user ID, account status, and download count. It also sends details about the downloaded work, such as the page URL, title, Pixiv ID, type, view and bookmark counts, upload date, and tags, and stores failed logs locally for later retry.

ga_i_sa_iv6.0.4Chrome Web Store
72Risk
Who publishes it

ga_i_sa_i - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
ga_i_sa_i
Registered address
6−20−6 レモンホームⅢ, 相模原市南区大野大 2520331, 日本

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Pixiv downloads send account and work metadata to Apps Script

After a Pixiv PDF/EPUB download completes, Pixiv Downloader builds a record with account identifiers and work metadata, posted to a hardcoded Apps Script URL.

On failure it falls back to a beacon and stores the record in localStorage.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You finish a Pixiv PDF or EPUB download using the extension.

The code path is reached after the generated file is downloaded.

The extension did this

The extension posts your account identifiers and the downloaded work's metadata to a Google Apps Script URL.

If that send fails, the same record is stored locally and can be retried.

Fields assembled for the Apps Script record
  • Download timestamp
    2026/07/14 12:04:33.123 (illustrative)

    Ties the record to the exact time a download completed.

  • Your extension account
    reader@example.com, userId 38492017 (illustrative)

    Connects the download event to your account identity when the extension has it.

  • Your plan and counters
    free, downloadCount 12 (illustrative)

    Shows whether your account is marked as paid or free and how many downloads were counted.

  • Pixiv page URL
    https://www.pixiv.net/artworks/123456789 (illustrative)

    Identifies the exact Pixiv artwork or novel page associated with the download.

  • Pixiv work details
    Summer Illustration, id 123456789, illust2 (illustrative)

    Names what you downloaded and adds Pixiv's metadata for that work.

  • Popularity metrics
    viewCount 84521, bookmarkCount 3910 (illustrative)

    Adds view and bookmark counts for the downloaded work.

  • Pixiv tags
    illustration, original, landscape (illustrative)

    Carries the Pixiv tag list associated with the work you downloaded.

Captured request
POSThttps://script.google.com/macros/s/AKfycbyBn0ud1Ij37SkHkCvXeyX4Cqne8py5fjtC4M44eUkU-yTG7qh-AuOmZmNQbG2veoWY0g/exec

The source sends the request with mode no-cors and does not read a response.

Headers
Content-Type
application/json
What's stored on your device

If the first send fails, the extension keeps the same account-and-work record in your browser so it can be sent later.

Location
localStorage key 'failedGoogleScriptLogs'
Contents
{  "data": [    "date string",    "success flag",    "",    "user email",    "user id",    "vip or free",    "download count",    "account created timestamp",    "account canceled timestamp",    "",    "Pixiv upload date",    "Pixiv page URL",    "Pixiv title",    "Pixiv work id",    "Pixiv work type",    "view count",    "bookmark count",    "comma-joined Pixiv tags"  ],  "artTitle": "Summer Illustration (illustrative)",  "timestamp": "2026-07-14T12:04:33.123Z (illustrative)"}
The code that does this

Shipped code that gathers metadata, logs completion, and posts the record

Readable version

Readable equivalent of the Pixiv metadata fetch

assets/main.ts-bd71c890.js
const getPixivMetadata = async (url) => {  const match = url.match(/https:\/\/www\.pixiv\.net\/(artworks|novel)(?:\/show\.php\?id=|\/)(\d+)/);  if (!match) return null;  const workType = match[1] === "artworks" ? "illust" : "novel";  const workId = match[2];  const responseText = await (await fetch(`https://www.pixiv.net/ajax/${workType}/${workId}?lang=ja`)).text();  const parsed = new DOMParser().parseFromString(responseText, "text/html");  const body = JSON.parse(parsed.body.innerText).body;  return {    pageUrl: url,    ArtType: workType,    artTitle: body.illustTitle ?? body.title,    artId: body.illustId ?? body.id,    uploadDate: new Date(body.uploadDate),    ArtistName: body.userName,    ArtistId: body.userId,    seriesTitle: body.seriesNavData?.title ?? "",    seriesOrder: body.seriesNavData?.order != null ? String(body.seriesNavData.order) : "",    id: body.id,    illustType: body.illustType,    rowUrl: body.urls?.original ?? "",    pageCount: body.pageCount,    language: body.language,    content: body.content,    coverUrl: body.coverUrl,    imgUrl: Object.values(body.textEmbeddedImages || {}).map((image) => image?.urls?.["1200x1200"]),    viewCount: body.viewCount,    bookmarkCount: body.bookmarkCount,    tags: body.tags.tags  };};

Readable equivalent of the completion logger

assets/main.ts-bd71c890.js
class DownloadLogger {  constructor(startMessage = "") {    this.discord = new SpreadsheetReporter();    const match = startMessage.match(/▼downloadCount\.value\s*:\s*(\d+)/);    this.downloadCount = match ? parseInt(match[1], 10) : null;  }  async logComplete(meta, user) {    await this.discord.stack("▼Complete Jobs!");    await this.discord.post_spreadsheet(meta, user, this.downloadCount);  }  async logError(error, meta, user) {    await this.discord.error(error, { meta: meta || {}, user, downloadCount: this.downloadCount });  }}

Readable equivalent of the Apps Script reporter

assets/main.ts-bd71c890.js
post_spreadsheet = async (meta, user, downloadCount, success = true) => {  const endpoint = "https://script.google.com/macros/s/AKfycbyBn0ud1Ij37SkHkCvXeyX4Cqne8py5fjtC4M44eUkU-yTG7qh-AuOmZmNQbG2veoWY0g/exec";  const account = user || { email: "unknown", userId: "unknown", isVIP: false, created: null, canceled: null };  const record = [    await this.date(),    success,    "",    account.email,    account.userId,    account.isVIP ? "vip" : "free",    downloadCount,    account.created,    account.canceled,    "",    meta.uploadDate,    meta.pageUrl,    meta.artTitle,    meta.id,    meta.illustType ? meta.ArtType + meta.illustType : meta.ArtType,    meta.viewCount,    meta.bookmarkCount,    meta.tags.map((tag) => tag.tag).join(", ")  ];  try {    await fetch(endpoint, {      method: "POST",      mode: "no-cors",      headers: { "Content-Type": "application/json" },      body: JSON.stringify(record)    });    return true;  } catch {    if (navigator.sendBeacon) {      try {        const blob = new Blob([JSON.stringify(record)], { type: "application/json" });        if (navigator.sendBeacon(endpoint, blob)) return true;      } catch (error) {        console.error("Beaconエラー:", error);      }    }    try {      const failed = JSON.parse(localStorage.getItem("failedGoogleScriptLogs") || "[]");      failed.push({ timestamp: new Date().toISOString(), data: record, artTitle: meta.artTitle });      if (failed.length > 100) failed.shift();      localStorage.setItem("failedGoogleScriptLogs", JSON.stringify(failed));    } catch {      console.error("Beaconエラー:");    }    return true;  }};

Readable equivalent of the injected button handler

assets/main.ts-bd71c890.js
downloadButton.addEventListener("click", async (event) => {  event.preventDefault();  event.stopPropagation();  const fullUrl = href.startsWith("http") ? href : `https://www.pixiv.net${href}`;  if (downloadQueue) downloadQueue.orderWork(fullUrl);});
External hosts in this evidence path
    • www.pixiv.net

    The content script fetches Pixiv's own artwork or novel metadata from /ajax/{type}/{id}?lang=ja.

    • script.google.com

    Receives the account-and-work record through a hardcoded Google Apps Script endpoint.

Where it sends data

Destinations our analysis observed Pixiv Downloader 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.

  • script.google.com/macros/s/AKfycbyBn0ud1Ij37SkHkCvXeyX4Cqne8py5fjtC4M44eUkU-yTG7qh-AuOmZmNQbG2veoWY0g/exec

    Pixiv Downloader sends data to script.google.com/macros/s/AKfycbyBn0ud1Ij37SkHkCvXeyX4Cqne8py5fjtC4M44eUkU-yTG7qh-AuOmZmNQbG2veoWY0g/exec. No other extension we have analysed sends data here.

Updated 30 September 2026obpplliepllmongaimfbkbdembbmkfne