Multilogin Automation with Selenium: Complete Guide

Automate Multilogin browser profiles with Selenium WebDriver for scalable multi-account management. This guide covers Python, Node.js, and Java implementations.

πŸ’‘ Save 50% on Multilogin + Unlimited automation

Get 50% OFF β†’

🎯 What You’ll Learn

  • βœ… Multilogin Local API setup
  • βœ… Python + Selenium automation
  • βœ… Node.js + Puppeteer integration
  • βœ… Java + Selenium WebDriver
  • βœ… Profile management automation
  • βœ… Anti-detection best practices
  • βœ… Error handling & debugging

Prerequisites:

  • Multilogin installed (Solo/Team/Scale plan)
  • Basic programming knowledge
  • Python 3.7+ or Node.js 14+ or Java 11+

πŸ”§ Part 1: Setup & Configuration

Enable Multilogin Local API

1. Launch Multilogin Application

2. Enable API Access:

Settings β†’ Advanced β†’ Local API
βœ… Enable Local API Server
Port: 35000 (default)

3. Verify API is Running:

# Test API endpoint
curl http://localhost:35000/api/v1/profile/list

Expected response:

{
  "status": "success",
  "profiles": [...]
}

🐍 Part 2: Python + Selenium Automation

Installation

pip install selenium requests

Basic Profile Launcher

import requests
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

class MultiloginAutomation:
    def __init__(self):
        self.api_url = "http://localhost:35000/api/v1"
        
    def start_profile(self, profile_id):
        """Start Multilogin profile and return debugger address"""
        url = f"{self.api_url}/profile/start?profileId={profile_id}"
        
        try:
            response = requests.get(url)
            data = response.json()
            
            if data['status'] == 'OK':
                debugger_address = data['value']
                print(f"βœ… Profile started: {debugger_address}")
                return debugger_address
            else:
                print(f"❌ Error: {data}")
                return None
        except Exception as e:
            print(f"❌ API Error: {e}")
            return None
    
    def connect_selenium(self, debugger_address):
        """Connect Selenium to running profile"""
        chrome_options = Options()
        chrome_options.add_experimental_option(
            "debuggerAddress", 
            debugger_address
        )
        
        driver = webdriver.Chrome(options=chrome_options)
        return driver
    
    def stop_profile(self, profile_id):
        """Stop Multilogin profile"""
        url = f"{self.api_url}/profile/stop?profileId={profile_id}"
        
        try:
            response = requests.get(url)
            data = response.json()
            
            if data['status'] == 'OK':
                print(f"βœ… Profile stopped")
                return True
            else:
                print(f"❌ Error stopping profile: {data}")
                return False
        except Exception as e:
            print(f"❌ API Error: {e}")
            return False


# Usage Example
if __name__ == "__main__":
    # Initialize
    mlx = MultiloginAutomation()
    
    # Your profile ID from Multilogin app
    PROFILE_ID = "your-profile-uuid-here"
    
    # Start profile
    debugger = mlx.start_profile(PROFILE_ID)
    
    if debugger:
        # Connect Selenium
        driver = mlx.connect_selenium(debugger)
        
        # Your automation code here
        driver.get("https://example.com")
        print(f"Page title: {driver.title}")
        
        # Wait and close
        time.sleep(5)
        driver.quit()
        
        # Stop profile
        mlx.stop_profile(PROFILE_ID)

Advanced: Multi-Profile Automation

import concurrent.futures
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class MultiProfileAutomation(MultiloginAutomation):
    
    def automate_single_profile(self, profile_config):
        """Automate single profile with error handling"""
        profile_id = profile_config['id']
        tasks = profile_config['tasks']
        
        try:
            # Start profile
            debugger = self.start_profile(profile_id)
            if not debugger:
                return {"profile": profile_id, "status": "failed", "reason": "Failed to start"}
            
            # Connect Selenium
            driver = self.connect_selenium(debugger)
            
            # Execute tasks
            results = []
            for task in tasks:
                try:
                    result = self.execute_task(driver, task)
                    results.append(result)
                except Exception as e:
                    results.append({"task": task, "error": str(e)})
            
            # Cleanup
            driver.quit()
            time.sleep(2)
            self.stop_profile(profile_id)
            
            return {
                "profile": profile_id, 
                "status": "success", 
                "results": results
            }
            
        except Exception as e:
            return {
                "profile": profile_id, 
                "status": "error", 
                "reason": str(e)
            }
    
    def execute_task(self, driver, task):
        """Execute specific task"""
        task_type = task['type']
        
        if task_type == "navigate":
            driver.get(task['url'])
            return {"action": "navigate", "url": task['url'], "title": driver.title}
        
        elif task_type == "click":
            element = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, task['selector']))
            )
            element.click()
            return {"action": "click", "selector": task['selector']}
        
        elif task_type == "fill":
            element = driver.find_element(By.CSS_SELECTOR, task['selector'])
            element.clear()
            element.send_keys(task['value'])
            return {"action": "fill", "selector": task['selector']}
        
        elif task_type == "screenshot":
            filename = f"screenshot_{int(time.time())}.png"
            driver.save_screenshot(filename)
            return {"action": "screenshot", "file": filename}
        
        else:
            return {"action": "unknown", "task": task}
    
    def run_parallel_automation(self, profiles, max_workers=3):
        """Run automation on multiple profiles in parallel"""
        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            futures = [
                executor.submit(self.automate_single_profile, profile) 
                for profile in profiles
            ]
            
            results = []
            for future in concurrent.futures.as_completed(futures):
                result = future.result()
                results.append(result)
                print(f"Completed: {result['profile']} - {result['status']}")
            
            return results


# Usage Example: Parallel Automation
if __name__ == "__main__":
    mlx_multi = MultiProfileAutomation()
    
    # Define profiles and tasks
    profiles = [
        {
            "id": "profile-uuid-1",
            "tasks": [
                {"type": "navigate", "url": "https://example.com"},
                {"type": "fill", "selector": "#username", "value": "user1"},
                {"type": "fill", "selector": "#password", "value": "pass1"},
                {"type": "click", "selector": "#login-button"},
                {"type": "screenshot"}
            ]
        },
        {
            "id": "profile-uuid-2",
            "tasks": [
                {"type": "navigate", "url": "https://example.com"},
                {"type": "fill", "selector": "#search", "value": "automation"},
                {"type": "click", "selector": "#search-button"}
            ]
        }
    ]
    
    # Run automation (max 3 profiles at once)
    results = mlx_multi.run_parallel_automation(profiles, max_workers=3)
    
    # Print results
    for result in results:
        print(f"\nProfile: {result['profile']}")
        print(f"Status: {result['status']}")
        if 'results' in result:
            for task_result in result['results']:
                print(f"  - {task_result}")

🟒 Part 3: Node.js + Puppeteer Automation

Installation

npm install puppeteer-core axios

Basic Setup

const puppeteer = require('puppeteer-core');
const axios = require('axios');

class MultiloginAutomation {
  constructor() {
    this.apiUrl = 'http://localhost:35000/api/v1';
  }

  async startProfile(profileId) {
    try {
      const response = await axios.get(
        `${this.apiUrl}/profile/start?profileId=${profileId}`
      );
      
      if (response.data.status === 'OK') {
        const wsEndpoint = response.data.value;
        console.log(`βœ… Profile started: ${wsEndpoint}`);
        return wsEndpoint;
      } else {
        console.log(`❌ Error: ${response.data}`);
        return null;
      }
    } catch (error) {
      console.log(`❌ API Error: ${error.message}`);
      return null;
    }
  }

  async connectPuppeteer(wsEndpoint) {
    const browser = await puppeteer.connect({
      browserWSEndpoint: wsEndpoint,
      defaultViewport: null
    });
    return browser;
  }

  async stopProfile(profileId) {
    try {
      const response = await axios.get(
        `${this.apiUrl}/profile/stop?profileId=${profileId}`
      );
      
      if (response.data.status === 'OK') {
        console.log(`βœ… Profile stopped`);
        return true;
      } else {
        console.log(`❌ Error: ${response.data}`);
        return false;
      }
    } catch (error) {
      console.log(`❌ API Error: ${error.message}`);
      return false;
    }
  }
}

// Usage Example
(async () => {
  const mlx = new MultiloginAutomation();
  const PROFILE_ID = 'your-profile-uuid-here';
  
  // Start profile
  const wsEndpoint = await mlx.startProfile(PROFILE_ID);
  
  if (wsEndpoint) {
    // Connect Puppeteer
    const browser = await mlx.connectPuppeteer(wsEndpoint);
    const page = await browser.newPage();
    
    // Your automation here
    await page.goto('https://example.com');
    console.log('Page title:', await page.title());
    
    // Wait and close
    await page.waitForTimeout(5000);
    await browser.disconnect();
    
    // Stop profile
    await mlx.stopProfile(PROFILE_ID);
  }
})();

Advanced: E-commerce Scraper

async function scrapeProductData(mlx, profileId, productUrl) {
  try {
    // Start profile
    const wsEndpoint = await mlx.startProfile(profileId);
    if (!wsEndpoint) return null;
    
    // Connect browser
    const browser = await mlx.connectPuppeteer(wsEndpoint);
    const page = await browser.newPage();
    
    // Navigate to product
    await page.goto(productUrl, { waitUntil: 'networkidle2' });
    
    // Extract data
    const productData = await page.evaluate(() => {
      return {
        title: document.querySelector('h1')?.innerText,
        price: document.querySelector('.price')?.innerText,
        rating: document.querySelector('.rating')?.innerText,
        availability: document.querySelector('.availability')?.innerText,
        images: Array.from(document.querySelectorAll('.product-image img'))
          .map(img => img.src)
      };
    });
    
    // Screenshot
    await page.screenshot({ path: `product_${Date.now()}.png` });
    
    // Cleanup
    await browser.disconnect();
    await mlx.stopProfile(profileId);
    
    return productData;
    
  } catch (error) {
    console.error('Scraping error:', error);
    return null;
  }
}

// Usage
(async () => {
  const mlx = new MultiloginAutomation();
  
  const products = [
    'https://example.com/product-1',
    'https://example.com/product-2',
    'https://example.com/product-3'
  ];
  
  const PROFILE_ID = 'your-profile-uuid';
  
  for (const url of products) {
    const data = await scrapeProductData(mlx, PROFILE_ID, url);
    console.log('Product data:', data);
    
    // Wait between requests
    await new Promise(resolve => setTimeout(resolve, 3000));
  }
})();

β˜• Part 4: Java + Selenium WebDriver

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.15.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>

Java Implementation

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class MultiloginAutomation {
    private static final String API_URL = "http://localhost:35000/api/v1";
    
    public static String startProfile(String profileId) throws Exception {
        String url = API_URL + "/profile/start?profileId=" + profileId;
        
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(url);
            String response = EntityUtils.toString(
                httpClient.execute(request).getEntity()
            );
            
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.get("status").getAsString().equals("OK")) {
                String debuggerAddress = json.get("value").getAsString();
                System.out.println("βœ… Profile started: " + debuggerAddress);
                return debuggerAddress;
            } else {
                System.out.println("❌ Error: " + json);
                return null;
            }
        }
    }
    
    public static WebDriver connectSelenium(String debuggerAddress) {
        ChromeOptions options = new ChromeOptions();
        options.setExperimentalOption("debuggerAddress", debuggerAddress);
        
        WebDriver driver = new ChromeDriver(options);
        return driver;
    }
    
    public static boolean stopProfile(String profileId) throws Exception {
        String url = API_URL + "/profile/stop?profileId=" + profileId;
        
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(url);
            String response = EntityUtils.toString(
                httpClient.execute(request).getEntity()
            );
            
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.get("status").getAsString().equals("OK")) {
                System.out.println("βœ… Profile stopped");
                return true;
            } else {
                System.out.println("❌ Error: " + json);
                return false;
            }
        }
    }
    
    public static void main(String[] args) {
        String PROFILE_ID = "your-profile-uuid-here";
        
        try {
            // Start profile
            String debugger = startProfile(PROFILE_ID);
            
            if (debugger != null) {
                // Connect Selenium
                WebDriver driver = connectSelenium(debugger);
                
                // Your automation
                driver.get("https://example.com");
                System.out.println("Page title: " + driver.getTitle());
                
                // Wait and close
                Thread.sleep(5000);
                driver.quit();
                
                // Stop profile
                stopProfile(PROFILE_ID);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

πŸ›‘οΈ Anti-Detection Best Practices

1. Randomize Timing

import random
import time

def human_delay(min_seconds=1, max_seconds=5):
    """Random delay to mimic human behavior"""
    delay = random.uniform(min_seconds, max_seconds)
    time.sleep(delay)

# Usage
driver.find_element(By.ID, "username").send_keys("myuser")
human_delay(2, 4)  # Wait 2-4 seconds
driver.find_element(By.ID, "password").send_keys("mypass")
human_delay(1, 3)
driver.find_element(By.ID, "submit").click()

2. Scroll Like Human

from selenium.webdriver.common.keys import Keys

def human_scroll(driver, scrolls=3):
    """Scroll page gradually"""
    body = driver.find_element(By.TAG_NAME, "body")
    
    for i in range(scrolls):
        # Random scroll amount
        scroll_amount = random.randint(300, 800)
        driver.execute_script(f"window.scrollBy(0, {scroll_amount})")
        
        # Random pause
        time.sleep(random.uniform(0.5, 2.0))

3. Handle CAPTCHA

def wait_for_captcha_solve(driver, max_wait=120):
    """Wait for manual CAPTCHA solving"""
    print("⏳ Waiting for CAPTCHA to be solved...")
    
    start_time = time.time()
    while time.time() - start_time < max_wait:
        try:
            # Check if CAPTCHA iframe disappeared
            driver.find_element(By.CSS_SELECTOR, "iframe[src*='captcha']")
            time.sleep(2)
        except:
            print("βœ… CAPTCHA solved!")
            return True
    
    print("❌ CAPTCHA timeout")
    return False

4. Error Recovery

def safe_click(driver, selector, max_attempts=3):
    """Click with retry logic"""
    for attempt in range(max_attempts):
        try:
            element = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, selector))
            )
            element.click()
            return True
        except Exception as e:
            print(f"Attempt {attempt + 1} failed: {e}")
            if attempt < max_attempts - 1:
                time.sleep(2)
            else:
                return False

πŸ“Š Performance Optimization

Profile Management

class ProfilePool:
    def __init__(self, profile_ids, max_concurrent=5):
        self.profile_ids = profile_ids
        self.max_concurrent = max_concurrent
        self.active_profiles = {}
        self.mlx = MultiloginAutomation()
    
    def get_available_profile(self):
        """Get least used profile"""
        if len(self.active_profiles) >= self.max_concurrent:
            # Wait for a profile to free up
            time.sleep(5)
            return self.get_available_profile()
        
        # Find profile with lowest usage
        usage_counts = {pid: 0 for pid in self.profile_ids}
        for pid in self.active_profiles:
            usage_counts[pid] = usage_counts.get(pid, 0) + 1
        
        profile_id = min(usage_counts, key=usage_counts.get)
        return profile_id
    
    def start(self, profile_id):
        """Start profile and track"""
        debugger = self.mlx.start_profile(profile_id)
        if debugger:
            self.active_profiles[profile_id] = debugger
            return debugger
        return None
    
    def stop(self, profile_id):
        """Stop profile and remove from tracking"""
        if profile_id in self.active_profiles:
            self.mlx.stop_profile(profile_id)
            del self.active_profiles[profile_id]

❓ Troubleshooting

🚨 "Connection refused" error
Solution:
  • Ensure Multilogin app is running
  • Check Local API is enabled (Settings β†’ Advanced)
  • Verify port 35000 is not blocked by firewall
  • Try restarting Multilogin application
🚨 Profile starts but Selenium won't connect
Solution:
  • Wait 3-5 seconds after profile starts
  • Check ChromeDriver version matches Chrome in profile
  • Verify debugger address format (should be "127.0.0.1:PORT")
  • Use correct connection method for your language


Start Automating with Multilogin

Save 50% with code SAAS50

Get 50% OFF Now β†’