Exportar seguidores y seguidos de X (Twitter) a Excel, gratis y sin límite

Descarga los seguidores o los seguidos de cualquier cuenta de X en una hoja de cálculo: nombre, @usuario, ID, bio, verificado y si cada cuenta te sigue. Gratis, sin el límite de 150 cuentas y sin conectar tu cuenta a ninguna app.

Inicia sesión en x.com, abre un perfil y pulsa Seguidores o Siguiendo; luego ejecuta el script. Funciona con tus listas y con cualquier cuenta pública.

export-followers.js · 145 líneaspégalo en la consola (F12) de x.com
// Responsive Muse · X (Twitter) Followers & Following Exporter · v1.0 · 2026-10-31
// Saves the followers or the following list of any X account you can open (yours or a public
// one) to CSV or TXT: name, @handle, user ID, bio, verified, whether they follow you and the
// profile link. On your own Following list it can keep only the accounts that don't follow
// you back. X only keeps on screen the people you can see, so the script collects them while
// it scrolls. It makes no network requests of its own and never reads cookies, passwords or tokens.
(async () => {
  const TOOL = 'Exportar seguidores de X';
  const FORMAT = 'csv';        // 'csv' (Excel, Sheets) or 'txt' (one @handle per line)
  const ONLY = 'all';          // 'all' or 'not-following-back' (only accounts that don't follow you)
  const LIMIT = 2000;          // stop after this many accounts (0 = no limit; big accounts take a while)

  const page = location.pathname.match(/^\/([A-Za-z0-9_]{1,15})\/(followers|following|verified_followers)\/?$/);
  if (!/^(x|twitter)\.com$/.test(location.hostname) || !page) {
    alert('Abre primero una lista de seguidores o de seguidos en X (con sesión iniciada), por ejemplo:\nhttps://x.com/YourName/following');
    return;
  }
  // A small box on the page shows progress and asks before changing anything,
  // so you never need to read or edit the console.
  const many = (n, word) => `${n} ${n === 1 ? word : ({ canal: 'canales', 'canción': 'canciones', 'publicación': 'publicaciones', 'elemento guardado': 'elementos guardados' })[word] || word + 's'}`;   // 1 vídeo, 2 vídeos
  const panel = (() => {
    const box = document.createElement('div');
    box.style.cssText = 'display:block!important;position:fixed;right:16px;bottom:16px;z-index:2147483647;width:340px;max-width:calc(100vw - 32px);' +
      'background:#111318;color:#e6e8ee;font:14px/1.5 system-ui,sans-serif;border-radius:12px;padding:16px;' +
      'box-shadow:0 8px 30px rgba(0,0,0,.4);text-align:left';
    const head = document.createElement('strong');
    head.textContent = TOOL;
    const msg = document.createElement('p');
    msg.style.cssText = 'margin:8px 0 12px;white-space:pre-line';
    const row = document.createElement('div');
    row.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end';
    box.append(head, msg, row);
    document.body.append(box);
    const button = (label, primary, onClick) => {
      const b = document.createElement('button');
      b.textContent = label;
      b.style.cssText = 'font:600 14px system-ui,sans-serif;padding:8px 14px;border-radius:8px;cursor:pointer;' +
        (primary ? 'background:#0f766e;color:#fff;border:1px solid #0f766e' : 'background:none;color:#e6e8ee;border:1px solid #3a3f4b');
      b.onclick = onClick;
      row.append(b);
    };
    return {
      text(t) { msg.textContent = t; },
      ask(t, yes) {
        msg.textContent = t;
        row.replaceChildren();
        return new Promise(r => {
          button('Cancelar', false, () => { row.replaceChildren(); r(false); });
          button(yes, true, () => { row.replaceChildren(); r(true); });
        });
      },
      stoppable(onStop) { row.replaceChildren(); button('Parar', false, onStop); },
      actions(t, list) {
        msg.textContent = t;
        row.replaceChildren();
        list.forEach(([label, primary, run]) => button(label, primary, run));
      },
      finish(t) { this.actions(t, [['Cerrar', true, () => box.remove()]]); },
      close() { box.remove(); },
    };
  })();
  // Offer the result as a downloaded file or as clipboard text (a real click, so it always works)
  const deliver = (summary, body, file) => {
    const save = () => {
      const a = document.createElement('a');
      a.href = URL.createObjectURL(new Blob([/\.csv$/.test(file) ? '\ufeff' + body : body], { type: 'text/plain;charset=utf-8' }));
      a.download = file;
      a.click();
      setTimeout(() => URL.revokeObjectURL(a.href), 5000);
      panel.text(`${summary}\nGuardado como ${file} (mira tu carpeta de Descargas).`);
    };
    const copy = () => navigator.clipboard.writeText(body)
      .then(() => panel.text(`${summary}\nCopiado. Pégalo en una hoja de cálculo o en una nota.`))
      .catch(() => panel.text(`${summary}\nEl navegador ha bloqueado el portapapeles: usa Descargar.`));
    panel.actions(summary, [['Cerrar', false, () => panel.close()], ['Copiar', false, copy], ['Descargar', true, save]]);
  };
  const sleep = ms => new Promise(r => setTimeout(r, ms));
  const people = new Map();
  let total = 0;               // accounts seen, before the "not following back" filter

  // Read the accounts currently on screen
  const collect = () => {
    for (const cell of document.querySelectorAll('[data-testid="UserCell"]')) {
      // Skip the "Who to follow" suggestions in the sidebar
      if (cell.closest('aside') || !cell.closest('[data-testid="primaryColumn"]')) continue;
      // Profile links are plain "/handle" (links in the bio carry ?src= or point to /status)
      const prof = [...cell.querySelectorAll('a[href^="/"]')].find(a => /^\/[A-Za-z0-9_]{1,15}$/.test(a.getAttribute('href')) && a.textContent.trim());
      if (!prof) continue;
      const handle = prof.getAttribute('href').slice(1);
      if (people.has(handle)) continue;
      // The Follow / Following button carries the numeric user ID: "<id>-follow" or "<id>-unfollow"
      const btn = cell.querySelector('[data-testid$="-follow"], [data-testid$="-unfollow"]');
      // Bio: the visible text block outside the links (hidden ones with an id are screen-reader labels)
      const bio = [...cell.querySelectorAll('div[dir="auto"]')].find(d => !d.closest('a') && !d.id);
      const followsYou = !!cell.querySelector('[data-testid="userFollowIndicator"]');
      total++;
      people.set(handle, {
        name: prof.textContent.trim(),
        handle: '@' + handle,
        id: (btn?.dataset.testid.match(/^(\d+)-/) || [])[1] || '',
        bio: (bio?.innerText || '').replace(/\s+/g, ' ').trim(),
        verified: cell.querySelector('[data-testid="icon-verified"]') ? 'sí' : '',
        followsYou: followsYou ? 'sí' : '',
        youFollow: btn && /-unfollow$/.test(btn.dataset.testid) ? 'sí' : '',
        url: 'https://x.com/' + handle,
      });
    }
  };

  // 1. Scroll down step by step, collecting as we go. X loads the next accounts only when you reach
  //    the bottom and can take a few seconds, so we stop after ~10 s at the bottom with nothing new.
  let stop = false;
  panel.stoppable(() => { stop = true; });
  window.scrollTo(0, 0);
  await sleep(1000);
  let idle = 0;
  while (idle < 8 && !stop && !(LIMIT && people.size >= LIMIT)) {
    const before = people.size;
    collect();
    const atBottom = scrollY + innerHeight >= document.scrollingElement.scrollHeight - 50;
    window.scrollBy(0, innerHeight * 0.8);
    await sleep(1200);
    idle = people.size !== before ? 0 : atBottom ? idle + 1 : idle;
    panel.text(`Recogiendo cuentas… ${people.size}${LIMIT ? ' de un máximo de ' + LIMIT : ''}`);
  }
  let list = [...people.values()];
  if (LIMIT) list = list.slice(0, LIMIT);
  if (!list.length) {
    panel.finish('No se han encontrado cuentas. ¿La lista está vacía o es privada? Si no, puede que X haya cambiado su diseño: avísanos en responsive-muse.com.');
    return 0;
  }
  if (ONLY === 'not-following-back') list = list.filter(p => !p.followsYou);

  // 2. Build the file and offer it
  const cell = v => '"' + String(v).replace(/"/g, '""') + '"';
  const body = FORMAT === 'txt'
    ? list.map(p => p.handle).join('\n')
    : ['#,Nombre,Usuario,ID de usuario,Bio,Verificado,Te sigue,Le sigues,URL', ...list.map((p, i) =>
        [i + 1, p.name, p.handle, p.id, p.bio, p.verified, p.followsYou, p.youFollow, p.url].map(cell).join(','))].join('\n');
  const what = ONLY === 'not-following-back' ? `${many(list.length, 'cuenta')} que no te siguen (de ${total})` : many(list.length, 'cuenta');
  const file = `x-${page[1]}-${ONLY === 'not-following-back' ? 'not-following-back' : page[2]}.${FORMAT}`;
  deliver(`${stop ? 'Parado antes de terminar. ' : ''}Listo: ${what}.`, body, file);
  return list.length;
})();

🛡️ ¿Chrome te dice «No pegues código…»? Es normal. Escribe allow pasting en la consola (o permitir pegar si tu Chrome está en español), pulsa Intro y vuelve a pegar. Solo hace falta una vez.

¿Por qué avisa Chrome?

Chrome lo muestra la primera vez que alguien pega algo en la consola, para proteger de estafas en las que te piden pegar código desconocido. Hace bien en avisar, y por eso este script está entero a la vista: 145 líneas comentadas que no envían nada a ningún sitio ni tocan tus contraseñas o cookies. Por qué es seguro.

¿Prefieres no usar la consola? Arrastra el botón Exportar seguidores y seguidos a tu barra de marcadores y haz clic en él estando en x.com. Así no sale el aviso.

Marcador: arrastra el botón del medio a tu barra de marcadores, abre x.com y pulsa el marcador. Ejecuta el mismo script con las opciones de arriba.

Por qué es seguro ejecutarloComprobación automática superada 26 oct 2026 · probado en una cuenta real el 31 oct 2026
  • No envía nada a ningún sitio. Sin fetch, XHR ni scripts externos. La única actividad de red es la de x.com cargando tu lista.
  • No toca tu sesión. No lee cookies, contraseñas, tokens ni el almacenamiento del navegador.
  • Solo funciona en x.com. En cualquier otra web se detiene antes de hacer nada.
  • Corto y legible. 145 líneas comentadas, sin minificar ni ofuscar.
  • Muestra su progreso en la página. Un pequeño recuadro te dice qué está haciendo y al final te ofrece Descargar o Copiar.
  • El mismo código para todos. SHA-256 de la versión por defecto: 82279e9dde3023de…
Compruébalo tú mismo en 30 segundos
  1. Léelo: todas las líneas están arriba, con comentarios.
  2. Busca en el código fetch, XMLHttpRequest, cookie, localStorage o eval. No aparece ninguna.
  3. Abre la pestaña Red (Network) antes de ejecutarlo: solo verás peticiones a x.com.
¿Te ha funcionado?

Cómo usarlo

  1. Inicia sesión en x.com, abre el perfil y pulsa Seguidores o Siguiendo.
  2. Abre la consola: F12 o Ctrl+Mayús+J (Cmd+Opción+J en Mac). Si Chrome te lo pide, escribe permitir pegar y pulsa Enter.
  3. Copia el script, pégalo en la consola y pulsa Enter. Recorre la lista y te ofrece el archivo.

Qué obtienes

Una fila por cuenta: nombre, @usuario, el ID numérico (no cambia aunque cambien de nombre), bio, verificado, Te sigue y Le sigues. Elige TXT para una lista simple de @usuarios.

¿Por qué no una extensión o un exportador online?

Casi todos los exportadores de seguidores son extensiones que piden permiso para leer todas las webs que visitas, o webs que te piden conectar tu cuenta de X, y las versiones gratis se paran en 150 o 200 cuentas. Este script es corto, legible y solo funciona en x.com, en tu propia pestaña.

Preguntas frecuentes

¿Es gratis? Otros exportadores se paran en 150 o 200 cuentas.

Sí, sin límite y sin registro. El script se ejecuta en tu propia pestaña de X y lee la lista que ya ves, así que no hay servidor que pagar ni nada que desbloquear.

¿Tengo que darle mi contraseña de X o conectar una app?

No. No inicias sesión en ningún otro sitio ni autorizas ninguna app. El script no envía nada a ningún sitio: el archivo se crea en tu navegador.

¿Por qué salen menos cuentas que el número del perfil?

X no muestra las cuentas suspendidas, desactivadas o algunas restringidas, y en cuentas muy grandes solo deja recorrer una parte de los seguidores. El script exporta todo lo que X te enseña.

¿Puedo llevar la lista a Bluesky o Mastodon?

Expórtala en CSV: las columnas Usuario, Nombre y Bio son las que piden las herramientas de migración. Usa TXT para una lista simple de @usuarios.

Más herramientas para X

Limpiar

Exportar y hacer copia