Prácticas acciones masivas con la API de Octo Browser


Alex Phillips
Customer Service Specialist
La API (Interfaz de Programación de Aplicaciones, por sus siglas en inglés) es una interfaz que permite a varias aplicaciones interactuar entre sí. En el caso de Octo Browser, la API le permite enviar solicitudes HTTP a los servidores de Octo y automatizar la gestión de los perfiles de navegador.
Anteriormente, explicamos en detalle qué es una API, cubrimos las formas de ejecutar scripts y explicamos la estructura de las solicitudes. Ahora, repasaremos varios ejemplos útiles que son de los más demandados por nuestros usuarios. Después de leer este artículo, aprenderá a recuperar y guardar nombres de perfiles, añadir extensiones de forma masiva, páginas de inicio y marcadores a los perfiles creados, y exportar datos de perfiles a una hoja de cálculo. A continuación encontrará instrucciones detalladas y código listo para usar.
Contenidos
Gestiona cualquier cantidad de cuentas sin bloqueos, rutinas ni gastos innecesarios.
¿Te gustaría probar Octo Browser con descuento?
Usa el código promocional OCTOBLOG para obtener un 30% de descuento en cualquier suscripción. Esta oferta solo es válida para nuevos usuarios.
Preparación
Descarga e instala VS Code.
Descarga e instala Node.js.
Crea una carpeta en una ubicación conveniente y ponle nombre, por ejemplo, octo_api.
Abre esta carpeta en VS Code.
Crea un archivo con extensión .js. Es mejor nombrarlo según la acción que realizará el código para evitar confusiones: por ejemplo, get_titles.js.
Abre la terminal y ejecuta el comando npm install axios para instalar una dependencia para Node.js.
Si VS Code muestra un error, abre Windows PowerShell como administrador, ingresa el comando Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned y confirma. Luego repite el paso anterior.
Inicia Octo Browser.
Dónde encontrar tu token de API
Para interactuar con Octo Browser a través de la API, necesitarás un token de API. Está disponible para usuarios con una Suscripción Base y superiores. El token de API se muestra en Octo Browser en la configuración de la cuenta principal bajo la pestaña "Adicional" (los demás miembros del equipo no pueden ver el token de API).

Límites de la API (rate limits)
Ten en cuenta que la API de Octo Browser tiene límites de solicitudes. Los fragmentos provistos utilizan la función check_limits, la cual comprueba las cabeceras de límite de velocidad de la API y se pausa automáticamente si el número de solicitudes restantes es bajo. Esto ayuda a evitar errores de tipo 429 Too Many Requests.
Al realizar solicitudes a la API pública, se consume 1 RPM y 1 RPH por solicitud. Al utilizar la API local, no se consumen RPM/RPH, excepto para POST Start Profile (1 RPM y 1 RPH) y POST One-time profile (4 RPM y 4 RPH).
El número exacto de perfiles que puedes iniciar, crear y eliminar bajo una suscripción específica depende completamente de tus scripts y su lógica. Puedes calcular de forma independiente el número requerido de solicitudes para tu flujo de trabajo en función de qué solicitudes afectan a los límites de RPH y RPM.
Algunas solicitudes de API pueden requerir un procesamiento más complejo, lo que significa que su ejecución puede costar más que una sola solicitud. Esto es necesario para equilibrar la carga del servidor y garantizar un rendimiento óptimo para todos los usuarios.
RPM significa solicitudes por minuto.
RPH significa solicitudes por hora.
Los valores de límite de solicitud dependen de tu suscripción.
Con la configuración completada, pasemos a los fragmentos de código útiles.
Obtener nombres de perfiles y guardarlos en un archivo .txt
Este fragmento es útil cuando necesitas recopilar rápidamente una lista de todos los perfiles creados en un solo lugar. Ayuda a simplificar el seguimiento de los perfiles, verificar su existencia real y realizar acciones posteriores con ellos.
Crea un archivo get_titles.js en la carpeta octo_api y pega el código del fragmento en él.
Reemplaza OCTO_API_TOKEN en el campo octo_token con tu token de API de Octo Browser.

Guarda el archivo.
Ingresa el comando node get_titles.js en la terminal y ejecuta el script.

Los nombres de los perfiles se guardarán línea por línea en el archivo profiles_titles.txt en la misma carpeta que el script.
Fragmento para guardar nombres de perfiles en un archivo .txt
const fs = require('fs').promises; const axios = require('axios'); //This is the configuration. Specify your Octo API Token here const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_titles(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function write_file(profiles) { const titles = profiles.map(item => item.title).filter(Boolean); try { await fs.writeFile('./profiles_titles.txt', titles.join('\n'), 'utf-8'); } catch (error) { console.error(`ERROR: While trying writing the file some error occured:\n${error}`); } } (async () => { const total_pages = await get_total_pages(); const profiles = await get_all_profiles_titles(total_pages); await write_file(profiles); console.log('Finished. Check profiles_titles.txt file...'); })()
const fs = require('fs').promises; const axios = require('axios'); //This is the configuration. Specify your Octo API Token here const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_titles(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function write_file(profiles) { const titles = profiles.map(item => item.title).filter(Boolean); try { await fs.writeFile('./profiles_titles.txt', titles.join('\n'), 'utf-8'); } catch (error) { console.error(`ERROR: While trying writing the file some error occured:\n${error}`); } } (async () => { const total_pages = await get_total_pages(); const profiles = await get_all_profiles_titles(total_pages); await write_file(profiles); console.log('Finished. Check profiles_titles.txt file...'); })()
Agregar extensiones, páginas de inicio y marcadores a los perfiles
Este script de automatización te permite estandarizar rápidamente las configuraciones de los perfiles y evitar agregar manualmente los mismos parámetros.
Para agregar extensiones, necesitarás sus UUID:
Abre un perfil con la extensión instalada.
En la barra de direcciones del perfil, escribe chrome://extensions/ y presiona Enter.
En la extensión requerida, haz clic en el botón "Detalles".

Al final de la página, copia el UUID de la extensión junto con su versión.

Puedes encontrar métodos para obtener los UUID a través de la API en la documentación.
A continuación, debes editar el código:
Crea un archivo llamado adding_info.js en la carpeta octo_api.
Pega el fragmento de código en él.
Inserta tu token de API en el campo octo_token.
Agrega los UUID de las extensiones requeridas en el campo extensions.
Agrega el nombre (título del marcador) y la URL (dirección del sitio web) en el campo bookmarks.
Agrega las URL de los sitios web que deseas abrir automáticamente al iniciar el perfil en el campo start_pages.
Si solo necesitas agregar extensiones, marcadores o páginas de inicio, puedes eliminar los parámetros innecesarios del fragmento de código:
data: { extensions: ["nkbihfbeogaeaoehlefnkodbefgpgknn@12.17.2"], //remove if you don’t need to add extensions bookmarks: [ { name: "google", url: "https://google.com" }, { name: "facebook", url: "https://facebook.com" } ],//remove if you don’t need bookmarks start_pages: ["https://google.com", "https://facebook.com"] //remove if you don’t need start pages }data: { extensions: ["nkbihfbeogaeaoehlefnkodbefgpgknn@12.17.2"], //remove if you don’t need to add extensions bookmarks: [ { name: "google", url: "https://google.com" }, { name: "facebook", url: "https://facebook.com" } ],//remove if you don’t need bookmarks start_pages: ["https://google.com", "https://facebook.com"] //remove if you don’t need start pages }

Guarda el archivo adding_info.js.
Ejecútalo usando el comando node adding_info.js.
Las extensiones, marcadores y páginas de inicio requeridos se agregarán a todos tus perfiles. Si solo necesitas aplicar los cambios a perfiles específicos, consulta el ejemplo en la documentación.

Antes de ejecutar el script, asegúrate de que los perfiles que deseas actualizar no se estén ejecutando. Los perfiles en ejecución no se actualizarán.
Fragmento para agregar extensiones, páginas de inicio y marcadores a los perfiles
const axios = require('axios'); // This is the configuration. Specify your Octo API Token here; you can modify this for your personal needs const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", data: { "extensions": ["aapfglkgnhmiaabhiijkjpfmhllaodgp@4.2.3"], "bookmarks": [ { "name": "google", "url": "https://google.com" }, { "name": "facebook", "url": "https://facebook.com" } ], "start_pages": ["https://google.com", "https://facebook.com"] } } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_profiles() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_uuids(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function patch_all_profiles(profiles) { let updated = []; let not_updated = []; for (let profile of profiles) { try { const response = await OCTO_REMOTE_API.patch(`/profiles/${profile.uuid}`, config.data); await check_limits(response); updated.push(profile); console.log(`Successfully updated ${profile.uuid}`); } catch (error) { not_updated.push(profile); console.error(`ERROR: Can't patch profile ${profile.uuid}`); } } return [updated, not_updated]; } (async () => { const total = await get_total_profiles(); const profiles = await get_all_profiles_uuids(total); const [updated, not_updated] = await patch_all_profiles(profiles); console.log(`Finished process:\nUpdated: ${updated.length}\nNot updated: ${not_updated.length}`); })();
const axios = require('axios'); // This is the configuration. Specify your Octo API Token here; you can modify this for your personal needs const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", data: { "extensions": ["aapfglkgnhmiaabhiijkjpfmhllaodgp@4.2.3"], "bookmarks": [ { "name": "google", "url": "https://google.com" }, { "name": "facebook", "url": "https://facebook.com" } ], "start_pages": ["https://google.com", "https://facebook.com"] } } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_profiles() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_uuids(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function patch_all_profiles(profiles) { let updated = []; let not_updated = []; for (let profile of profiles) { try { const response = await OCTO_REMOTE_API.patch(`/profiles/${profile.uuid}`, config.data); await check_limits(response); updated.push(profile); console.log(`Successfully updated ${profile.uuid}`); } catch (error) { not_updated.push(profile); console.error(`ERROR: Can't patch profile ${profile.uuid}`); } } return [updated, not_updated]; } (async () => { const total = await get_total_profiles(); const profiles = await get_all_profiles_uuids(total); const [updated, not_updated] = await patch_all_profiles(profiles); console.log(`Finished process:\nUpdated: ${updated.length}\nNot updated: ${not_updated.length}`); })();
Exportar datos de perfiles a Google Sheets
Puedes exportar los datos de tus perfiles a una hoja de cálculo. Esto facilita estructurar la información, así como ordenarla y analizarla. Puedes exportar los siguientes parámetros de perfil:
UUID,
etiquetas (tags),
nombre,
descripción.
Crea un archivo llamado google_sheets.js en la carpeta octo_api.
Agrega el código del fragmento al archivo.
Completa el campo octo_token. El token de API se puede encontrar en Octo Browser.
Completa el campo table_id. Este es el ID de tu documento de Google Sheets.
Completa el campo table_sheet_name. Este es el nombre de la hoja dentro del documento de Google Sheets donde deseas exportar los datos.

Guarda los cambios.
Agrega los archivos credentials.json y token.json a la carpeta octo_api.
Ejecuta el script en la terminal de VS Code usando el comando node google_sheets.js.
Una vez que el script finalice, los datos se subirán a la hoja de cálculo especificada.

Fragmento para exportar datos de perfiles a Google Sheets
const axios = require('axios'); const { google } = require('googleapis'); const token = require('./token.json'); const credentials = require('./credentials.json'); const oAuth2Client = new google.auth.OAuth2( credentials.installed.client_id, credentials.installed.client_secret, credentials.installed.redirect_uris[0] ); oAuth2Client.setCredentials(token); //configuration //Paste your Octo API token, spreadsheet ID and sheet name here const config = { octo_token: "OCTO_API_TOKEN", table_id: "1pqnVysHymZSI5Lzx6odasdasAqE022fqUEf6q5LnOqAhylLSM", table_sheet_name: "Profiles", octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_data(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title,description,tags`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function fill_sheet_with_data(oAuth2Client, spreadsheet_id, list_name, data) { const sheets = google.sheets({ version: 'v4', auth: oAuth2Client }); try { const sheetInfo = await sheets.spreadsheets.get({ spreadsheetId: spreadsheet_id, }); const sheetExists = sheetInfo.data.sheets.some( sheet => sheet.properties.title === list_name ); if (!sheetExists) { await sheets.spreadsheets.batchUpdate({ spreadsheetId: spreadsheet_id, resource: { requests: [{ addSheet: { properties: { title: list_name } } }] } }); console.log(`Создан новый лист "${list_name}".`); } const headers = ['Uuid', 'Tags', 'Title', 'Description']; const values = data.map(item => [ item.uuid, Array.isArray(item.tags) ? item.tags.join(', ') : item.tags, item.title, item.description || '' ]); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A1:D1`, valueInputOption: 'RAW', resource: { values: [headers] } }); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A2:D${values.length + 1}`, valueInputOption: 'RAW', resource: { values: values } }); console.log(`Данные успешно записаны в лист "${list_name}".`); } catch (err) { console.error('Ошибка при обновлении таблицы:', err); throw err; } } (async () => { const total_pages = await get_total_pages(); const profiles_data = await get_all_profiles_data(total_pages); await fill_sheet_with_data(oAuth2Client, config.table_id, config.table_sheet_name, profiles_data); })()
const axios = require('axios'); const { google } = require('googleapis'); const token = require('./token.json'); const credentials = require('./credentials.json'); const oAuth2Client = new google.auth.OAuth2( credentials.installed.client_id, credentials.installed.client_secret, credentials.installed.redirect_uris[0] ); oAuth2Client.setCredentials(token); //configuration //Paste your Octo API token, spreadsheet ID and sheet name here const config = { octo_token: "OCTO_API_TOKEN", table_id: "1pqnVysHymZSI5Lzx6odasdasAqE022fqUEf6q5LnOqAhylLSM", table_sheet_name: "Profiles", octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_data(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title,description,tags`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function fill_sheet_with_data(oAuth2Client, spreadsheet_id, list_name, data) { const sheets = google.sheets({ version: 'v4', auth: oAuth2Client }); try { const sheetInfo = await sheets.spreadsheets.get({ spreadsheetId: spreadsheet_id, }); const sheetExists = sheetInfo.data.sheets.some( sheet => sheet.properties.title === list_name ); if (!sheetExists) { await sheets.spreadsheets.batchUpdate({ spreadsheetId: spreadsheet_id, resource: { requests: [{ addSheet: { properties: { title: list_name } } }] } }); console.log(`Создан новый лист "${list_name}".`); } const headers = ['Uuid', 'Tags', 'Title', 'Description']; const values = data.map(item => [ item.uuid, Array.isArray(item.tags) ? item.tags.join(', ') : item.tags, item.title, item.description || '' ]); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A1:D1`, valueInputOption: 'RAW', resource: { values: [headers] } }); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A2:D${values.length + 1}`, valueInputOption: 'RAW', resource: { values: values } }); console.log(`Данные успешно записаны в лист "${list_name}".`); } catch (err) { console.error('Ошибка при обновлении таблицы:', err); throw err; } } (async () => { const total_pages = await get_total_pages(); const profiles_data = await get_all_profiles_data(total_pages); await fill_sheet_with_data(oAuth2Client, config.table_id, config.table_sheet_name, profiles_data); })()
Cómo obtener el `table_id`
Abre o crea una hoja de cálculo en Google Sheets.
Copia el ID de la tabla. Esta es la parte de la URL que se encuentra entre
/d/y/edit.
Por ejemplo, si tu enlace se ve como https://docs.google.com/spreadsheets/d/1GoPnhkStxFFxDzGZjjXSyjT_H_msHlSDx51tBROewOA/edit, entonces el table_id es 1GoPnhkStxFFxDzGZjjXSyjT_H_msHlSDx51tBROewOA.
Cómo obtener el archivo credentials.json
Inicia sesión en una cuenta de Gmail personal (no de una organización).
Selecciona cualquier país europeo de la lista y haz clic en Aceptar y continuar.
Haz clic en Seleccionar un proyecto, luego en Nuevo proyecto.
Escribe cualquier nombre de proyecto y haz clic en Crear.
Ve a https://console.cloud.google.com/auth y haz clic en Comenzar.
Ingresa un nombre para la aplicación, tu correo electrónico y haz clic en Siguiente.
Selecciona Externo, ingresa tu correo otra vez, haz clic en Siguiente, marca la casilla de consentimiento, luego haz clic en Continuar y Finalizar.
En la sección Usuarios de prueba, haz clic en Agregar usuarios.

Agrega tu correo electrónico y haz clic en Guardar.
En la página https://console.cloud.google.com/auth/overview, haz clic en Crear cliente OAuth.

Selecciona Aplicación de escritorio de la lista, escribe cualquier nombre y haz clic en Crear.
Aparecerá una ventana emergente. Descarga el archivo JSON desde ella.

Busca Google Sheets API.
Selecciónala y haz clic en Habilitar.
Abre el archivo JSON que descargaste previamente.
Cambia el valor de
redirect_urisdehttp://localhosta"redirect_uris": ["urn:ietf:wg:oauth:2.0:oob","http://localhost"].
Realiza el cambio con cuidado para no eliminar accidentalmente los corchetes.

Guarda el archivo.
Renómbralo a credentials.json.
Agrégalo a la carpeta octo_api.
Cómo obtener el archivo token.json
Crea un archivo llamado get_token.js en la carpeta octo_api y ábrelo en VS Code.
Ejecuta el comando Npm install googleapis en la terminal de VS Code.
Pega el script de generación de tokens en get_token.js y guarda el archivo.
Ejecuta el script usando node get_token.js.
Verás un enlace que dice Abre este URL en el navegador:
https://accounts.google.com/o/oauth2/…Abre el enlace.
Inicia sesión en tu cuenta de Google.
Acepta las pantallas de consentimiento.
En la página final, Google mostrará un código de autorización.

Pega el código en la terminal y presiona Enter.
El script creará un archivo token.json en la carpeta donde lo ejecutaste.

Script de generación de tokens
const fs = require('fs'); const readline = require('readline'); const { google } = require('googleapis'); const CREDENTIALS_PATH = 'credentials.json'; const TOKEN_PATH = 'token.json'; const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH)); const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); const SCOPES = ['https://www.googleapis.com/auth/spreadsheets']; const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, }); console.log('Open this URL in browser:', authUrl); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('Enter the code from that page here: ', (code) => { rl.close(); oAuth2Client.getToken(code, (err, token) => { if (err) return console.error('Error retrieving access token', err); fs.writeFileSync(TOKEN_PATH, JSON.stringify(token)); console.log('Token stored to', TOKEN_PATH); }); });
const fs = require('fs'); const readline = require('readline'); const { google } = require('googleapis'); const CREDENTIALS_PATH = 'credentials.json'; const TOKEN_PATH = 'token.json'; const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH)); const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); const SCOPES = ['https://www.googleapis.com/auth/spreadsheets']; const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, }); console.log('Open this URL in browser:', authUrl); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('Enter the code from that page here: ', (code) => { rl.close(); oAuth2Client.getToken(code, (err, token) => { if (err) return console.error('Error retrieving access token', err); fs.writeFileSync(TOKEN_PATH, JSON.stringify(token)); console.log('Token stored to', TOKEN_PATH); }); });
Conclusión
Usamos el lenguaje de programación Node.js en los ejemplos de este artículo. Puedes encontrar fragmentos útiles en otros lenguajes en nuestra documentación.

En este artículo, hemos cubierto:
cómo preparar tu entorno;
cómo obtener tu token de API de Octo Browser;
cómo recuperar el UUID de una extensión;
cómo obtener nombres de perfiles utilizando la API y guardarlos en un archivo de texto;
cómo agregar de forma masiva extensiones, marcadores y páginas de inicio a perfiles existentes;
cómo exportar datos de perfiles a Google Sheets.
Si aún tienes preguntas o no pudiste encontrar el ejemplo que necesitas en la documentación, no dudes en contactar a nuestro Servicio de Atención al Cliente en Telegram, mediante el chat de nuestro sitio web o el widget integrado en el navegador.
Gestiona cualquier cantidad de cuentas sin bloqueos, rutinas ni gastos innecesarios.
¿Te gustaría probar Octo Browser con descuento?
Usa el código promocional OCTOBLOG para obtener un 30% de descuento en cualquier suscripción. Esta oferta solo es válida para nuevos usuarios.
Preparación
Descarga e instala VS Code.
Descarga e instala Node.js.
Crea una carpeta en una ubicación conveniente y ponle nombre, por ejemplo, octo_api.
Abre esta carpeta en VS Code.
Crea un archivo con extensión .js. Es mejor nombrarlo según la acción que realizará el código para evitar confusiones: por ejemplo, get_titles.js.
Abre la terminal y ejecuta el comando npm install axios para instalar una dependencia para Node.js.
Si VS Code muestra un error, abre Windows PowerShell como administrador, ingresa el comando Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned y confirma. Luego repite el paso anterior.
Inicia Octo Browser.
Dónde encontrar tu token de API
Para interactuar con Octo Browser a través de la API, necesitarás un token de API. Está disponible para usuarios con una Suscripción Base y superiores. El token de API se muestra en Octo Browser en la configuración de la cuenta principal bajo la pestaña "Adicional" (los demás miembros del equipo no pueden ver el token de API).

Límites de la API (rate limits)
Ten en cuenta que la API de Octo Browser tiene límites de solicitudes. Los fragmentos provistos utilizan la función check_limits, la cual comprueba las cabeceras de límite de velocidad de la API y se pausa automáticamente si el número de solicitudes restantes es bajo. Esto ayuda a evitar errores de tipo 429 Too Many Requests.
Al realizar solicitudes a la API pública, se consume 1 RPM y 1 RPH por solicitud. Al utilizar la API local, no se consumen RPM/RPH, excepto para POST Start Profile (1 RPM y 1 RPH) y POST One-time profile (4 RPM y 4 RPH).
El número exacto de perfiles que puedes iniciar, crear y eliminar bajo una suscripción específica depende completamente de tus scripts y su lógica. Puedes calcular de forma independiente el número requerido de solicitudes para tu flujo de trabajo en función de qué solicitudes afectan a los límites de RPH y RPM.
Algunas solicitudes de API pueden requerir un procesamiento más complejo, lo que significa que su ejecución puede costar más que una sola solicitud. Esto es necesario para equilibrar la carga del servidor y garantizar un rendimiento óptimo para todos los usuarios.
RPM significa solicitudes por minuto.
RPH significa solicitudes por hora.
Los valores de límite de solicitud dependen de tu suscripción.
Con la configuración completada, pasemos a los fragmentos de código útiles.
Obtener nombres de perfiles y guardarlos en un archivo .txt
Este fragmento es útil cuando necesitas recopilar rápidamente una lista de todos los perfiles creados en un solo lugar. Ayuda a simplificar el seguimiento de los perfiles, verificar su existencia real y realizar acciones posteriores con ellos.
Crea un archivo get_titles.js en la carpeta octo_api y pega el código del fragmento en él.
Reemplaza OCTO_API_TOKEN en el campo octo_token con tu token de API de Octo Browser.

Guarda el archivo.
Ingresa el comando node get_titles.js en la terminal y ejecuta el script.

Los nombres de los perfiles se guardarán línea por línea en el archivo profiles_titles.txt en la misma carpeta que el script.
Fragmento para guardar nombres de perfiles en un archivo .txt
const fs = require('fs').promises; const axios = require('axios'); //This is the configuration. Specify your Octo API Token here const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_titles(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function write_file(profiles) { const titles = profiles.map(item => item.title).filter(Boolean); try { await fs.writeFile('./profiles_titles.txt', titles.join('\n'), 'utf-8'); } catch (error) { console.error(`ERROR: While trying writing the file some error occured:\n${error}`); } } (async () => { const total_pages = await get_total_pages(); const profiles = await get_all_profiles_titles(total_pages); await write_file(profiles); console.log('Finished. Check profiles_titles.txt file...'); })()
Agregar extensiones, páginas de inicio y marcadores a los perfiles
Este script de automatización te permite estandarizar rápidamente las configuraciones de los perfiles y evitar agregar manualmente los mismos parámetros.
Para agregar extensiones, necesitarás sus UUID:
Abre un perfil con la extensión instalada.
En la barra de direcciones del perfil, escribe chrome://extensions/ y presiona Enter.
En la extensión requerida, haz clic en el botón "Detalles".

Al final de la página, copia el UUID de la extensión junto con su versión.

Puedes encontrar métodos para obtener los UUID a través de la API en la documentación.
A continuación, debes editar el código:
Crea un archivo llamado adding_info.js en la carpeta octo_api.
Pega el fragmento de código en él.
Inserta tu token de API en el campo octo_token.
Agrega los UUID de las extensiones requeridas en el campo extensions.
Agrega el nombre (título del marcador) y la URL (dirección del sitio web) en el campo bookmarks.
Agrega las URL de los sitios web que deseas abrir automáticamente al iniciar el perfil en el campo start_pages.
Si solo necesitas agregar extensiones, marcadores o páginas de inicio, puedes eliminar los parámetros innecesarios del fragmento de código:
data: { extensions: ["nkbihfbeogaeaoehlefnkodbefgpgknn@12.17.2"], //remove if you don’t need to add extensions bookmarks: [ { name: "google", url: "https://google.com" }, { name: "facebook", url: "https://facebook.com" } ],//remove if you don’t need bookmarks start_pages: ["https://google.com", "https://facebook.com"] //remove if you don’t need start pages }

Guarda el archivo adding_info.js.
Ejecútalo usando el comando node adding_info.js.
Las extensiones, marcadores y páginas de inicio requeridos se agregarán a todos tus perfiles. Si solo necesitas aplicar los cambios a perfiles específicos, consulta el ejemplo en la documentación.

Antes de ejecutar el script, asegúrate de que los perfiles que deseas actualizar no se estén ejecutando. Los perfiles en ejecución no se actualizarán.
Fragmento para agregar extensiones, páginas de inicio y marcadores a los perfiles
const axios = require('axios'); // This is the configuration. Specify your Octo API Token here; you can modify this for your personal needs const config = { octo_token: "OCTO_API_TOKEN", // Specify your API token here octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", data: { "extensions": ["aapfglkgnhmiaabhiijkjpfmhllaodgp@4.2.3"], "bookmarks": [ { "name": "google", "url": "https://google.com" }, { "name": "facebook", "url": "https://facebook.com" } ], "start_pages": ["https://google.com", "https://facebook.com"] } } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function get_total_profiles() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_uuids(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function patch_all_profiles(profiles) { let updated = []; let not_updated = []; for (let profile of profiles) { try { const response = await OCTO_REMOTE_API.patch(`/profiles/${profile.uuid}`, config.data); await check_limits(response); updated.push(profile); console.log(`Successfully updated ${profile.uuid}`); } catch (error) { not_updated.push(profile); console.error(`ERROR: Can't patch profile ${profile.uuid}`); } } return [updated, not_updated]; } (async () => { const total = await get_total_profiles(); const profiles = await get_all_profiles_uuids(total); const [updated, not_updated] = await patch_all_profiles(profiles); console.log(`Finished process:\nUpdated: ${updated.length}\nNot updated: ${not_updated.length}`); })();
Exportar datos de perfiles a Google Sheets
Puedes exportar los datos de tus perfiles a una hoja de cálculo. Esto facilita estructurar la información, así como ordenarla y analizarla. Puedes exportar los siguientes parámetros de perfil:
UUID,
etiquetas (tags),
nombre,
descripción.
Crea un archivo llamado google_sheets.js en la carpeta octo_api.
Agrega el código del fragmento al archivo.
Completa el campo octo_token. El token de API se puede encontrar en Octo Browser.
Completa el campo table_id. Este es el ID de tu documento de Google Sheets.
Completa el campo table_sheet_name. Este es el nombre de la hoja dentro del documento de Google Sheets donde deseas exportar los datos.

Guarda los cambios.
Agrega los archivos credentials.json y token.json a la carpeta octo_api.
Ejecuta el script en la terminal de VS Code usando el comando node google_sheets.js.
Una vez que el script finalice, los datos se subirán a la hoja de cálculo especificada.

Fragmento para exportar datos de perfiles a Google Sheets
const axios = require('axios'); const { google } = require('googleapis'); const token = require('./token.json'); const credentials = require('./credentials.json'); const oAuth2Client = new google.auth.OAuth2( credentials.installed.client_id, credentials.installed.client_secret, credentials.installed.redirect_uris[0] ); oAuth2Client.setCredentials(token); //configuration //Paste your Octo API token, spreadsheet ID and sheet name here const config = { octo_token: "OCTO_API_TOKEN", table_id: "1pqnVysHymZSI5Lzx6odasdasAqE022fqUEf6q5LnOqAhylLSM", table_sheet_name: "Profiles", octo_api_base_url: "https://app.octobrowser.net/api/v2/automation/", } const OCTO_REMOTE_API = axios.create({ baseURL: config.octo_api_base_url, timeout: 10000, headers: { 'X-Octo-Api-Token': config.octo_token, 'Content-Type': "application/json" } }); //functions async function check_limits(response) { function parse_int_safe(value) { const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; } const ratelimit_header = response.headers.ratelimit; if (!ratelimit_header) { console.warn('No ratelimit header found!'); return; } const limit_entries = ratelimit_header.split(',').map(entry => entry.trim()); for (const entry of limit_entries) { const name_match = entry.match(/^([^;]+)/); const r_match = entry.match(/;r=(\d+)/); const t_match = entry.match(/;t=(\d+)/); if (!r_match || !t_match) { console.warn(`Invalid ratelimit format: ${entry}`); continue; } const limit_name = name_match ? name_match[1] : 'unknown_limit'; const remaining_quantity = parse_int_safe(r_match[1]); const window_seconds = parse_int_safe(t_match[1]); if (remaining_quantity < 5) { const wait_time = window_seconds + 1; console.log(`Waiting ${wait_time} seconds due to ${limit_name} limit`); await sleep(wait_time); } } } async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms * 1000)); } async function get_total_pages() { const response = await OCTO_REMOTE_API.get('/profiles?page=0&page_len=10'); const total_profiles = response.data.total_count; const total_pages = Math.ceil(response.data.total_count / 100); console.log(`Total Profiles: ${total_profiles}\nTotal Pages: ${total_pages}`); await check_limits(response); return total_pages; } async function get_all_profiles_data(total_pages) { let profiles = []; for (let i = 0; i < total_pages; i++) { let response = await OCTO_REMOTE_API.get(`/profiles?page=${i}&page_len=100&fields=title,description,tags`); await check_limits(response); profiles.push(...response.data.data); } return profiles; } async function fill_sheet_with_data(oAuth2Client, spreadsheet_id, list_name, data) { const sheets = google.sheets({ version: 'v4', auth: oAuth2Client }); try { const sheetInfo = await sheets.spreadsheets.get({ spreadsheetId: spreadsheet_id, }); const sheetExists = sheetInfo.data.sheets.some( sheet => sheet.properties.title === list_name ); if (!sheetExists) { await sheets.spreadsheets.batchUpdate({ spreadsheetId: spreadsheet_id, resource: { requests: [{ addSheet: { properties: { title: list_name } } }] } }); console.log(`Создан новый лист "${list_name}".`); } const headers = ['Uuid', 'Tags', 'Title', 'Description']; const values = data.map(item => [ item.uuid, Array.isArray(item.tags) ? item.tags.join(', ') : item.tags, item.title, item.description || '' ]); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A1:D1`, valueInputOption: 'RAW', resource: { values: [headers] } }); await sheets.spreadsheets.values.update({ spreadsheetId: spreadsheet_id, range: `${list_name}!A2:D${values.length + 1}`, valueInputOption: 'RAW', resource: { values: values } }); console.log(`Данные успешно записаны в лист "${list_name}".`); } catch (err) { console.error('Ошибка при обновлении таблицы:', err); throw err; } } (async () => { const total_pages = await get_total_pages(); const profiles_data = await get_all_profiles_data(total_pages); await fill_sheet_with_data(oAuth2Client, config.table_id, config.table_sheet_name, profiles_data); })()
Cómo obtener el `table_id`
Abre o crea una hoja de cálculo en Google Sheets.
Copia el ID de la tabla. Esta es la parte de la URL que se encuentra entre
/d/y/edit.
Por ejemplo, si tu enlace se ve como https://docs.google.com/spreadsheets/d/1GoPnhkStxFFxDzGZjjXSyjT_H_msHlSDx51tBROewOA/edit, entonces el table_id es 1GoPnhkStxFFxDzGZjjXSyjT_H_msHlSDx51tBROewOA.
Cómo obtener el archivo credentials.json
Inicia sesión en una cuenta de Gmail personal (no de una organización).
Selecciona cualquier país europeo de la lista y haz clic en Aceptar y continuar.
Haz clic en Seleccionar un proyecto, luego en Nuevo proyecto.
Escribe cualquier nombre de proyecto y haz clic en Crear.
Ve a https://console.cloud.google.com/auth y haz clic en Comenzar.
Ingresa un nombre para la aplicación, tu correo electrónico y haz clic en Siguiente.
Selecciona Externo, ingresa tu correo otra vez, haz clic en Siguiente, marca la casilla de consentimiento, luego haz clic en Continuar y Finalizar.
En la sección Usuarios de prueba, haz clic en Agregar usuarios.

Agrega tu correo electrónico y haz clic en Guardar.
En la página https://console.cloud.google.com/auth/overview, haz clic en Crear cliente OAuth.

Selecciona Aplicación de escritorio de la lista, escribe cualquier nombre y haz clic en Crear.
Aparecerá una ventana emergente. Descarga el archivo JSON desde ella.

Busca Google Sheets API.
Selecciónala y haz clic en Habilitar.
Abre el archivo JSON que descargaste previamente.
Cambia el valor de
redirect_urisdehttp://localhosta"redirect_uris": ["urn:ietf:wg:oauth:2.0:oob","http://localhost"].
Realiza el cambio con cuidado para no eliminar accidentalmente los corchetes.

Guarda el archivo.
Renómbralo a credentials.json.
Agrégalo a la carpeta octo_api.
Cómo obtener el archivo token.json
Crea un archivo llamado get_token.js en la carpeta octo_api y ábrelo en VS Code.
Ejecuta el comando Npm install googleapis en la terminal de VS Code.
Pega el script de generación de tokens en get_token.js y guarda el archivo.
Ejecuta el script usando node get_token.js.
Verás un enlace que dice Abre este URL en el navegador:
https://accounts.google.com/o/oauth2/…Abre el enlace.
Inicia sesión en tu cuenta de Google.
Acepta las pantallas de consentimiento.
En la página final, Google mostrará un código de autorización.

Pega el código en la terminal y presiona Enter.
El script creará un archivo token.json en la carpeta donde lo ejecutaste.

Script de generación de tokens
const fs = require('fs'); const readline = require('readline'); const { google } = require('googleapis'); const CREDENTIALS_PATH = 'credentials.json'; const TOKEN_PATH = 'token.json'; const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH)); const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); const SCOPES = ['https://www.googleapis.com/auth/spreadsheets']; const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, }); console.log('Open this URL in browser:', authUrl); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('Enter the code from that page here: ', (code) => { rl.close(); oAuth2Client.getToken(code, (err, token) => { if (err) return console.error('Error retrieving access token', err); fs.writeFileSync(TOKEN_PATH, JSON.stringify(token)); console.log('Token stored to', TOKEN_PATH); }); });
Conclusión
Usamos el lenguaje de programación Node.js en los ejemplos de este artículo. Puedes encontrar fragmentos útiles en otros lenguajes en nuestra documentación.

En este artículo, hemos cubierto:
cómo preparar tu entorno;
cómo obtener tu token de API de Octo Browser;
cómo recuperar el UUID de una extensión;
cómo obtener nombres de perfiles utilizando la API y guardarlos en un archivo de texto;
cómo agregar de forma masiva extensiones, marcadores y páginas de inicio a perfiles existentes;
cómo exportar datos de perfiles a Google Sheets.
Si aún tienes preguntas o no pudiste encontrar el ejemplo que necesitas en la documentación, no dudes en contactar a nuestro Servicio de Atención al Cliente en Telegram, mediante el chat de nuestro sitio web o el widget integrado en el navegador.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.

Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.

Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.
Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.