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
π Related Resources
- Complete Multilogin Setup Tutorial β
- Best Proxy Providers β
- Team Collaboration Guide β
- 50% Discount Code β