Automation examples
Each example starts a profile through the local API, connects to its DevTools endpoint, opens a page and stops the profile. Set BLEND_API and BLEND_TOKEN from Settings → API. Only automate sites you own or are allowed to test.
Node.js + Playwright
examples/node-playwright.mjs
/* global fetch */
// Blend + Playwright (Chromium over CDP). Usage: BLEND_TOKEN=... node node-playwright.mjs
import { chromium } from 'playwright-core';
const API = process.env.BLEND_API ?? 'http://127.0.0.1:3001/api/v1';
const TOKEN = process.env.BLEND_TOKEN;
const TARGET = process.env.TARGET_URL ?? 'https://example.com';
if (!TOKEN) throw new Error('Set BLEND_TOKEN (Settings → API in the Blend app, or `pnpm blend token`)');
async function api(method, path, body) {
const r = await fetch(API + path, { method, headers: { authorization: `Bearer ${TOKEN}`, 'content-type': 'application/json' }, body: body && JSON.stringify(body) });
const j = await r.json();
if (!r.ok) throw new Error(`${method} ${path}: ${j.error}`);
return j;
}
const profile = await api('POST', '/profiles', { name: `example-playwright-${Date.now()}` });
try {
await api('POST', `/profiles/${profile.id}/start`, { headless: process.env.HEADLESS === '1' });
const { browserURL } = await api('GET', `/profiles/${profile.id}/debug`);
const browser = await chromium.connectOverCDP(browserURL);
const context = browser.contexts()[0]; // the profile's own context (cookies, storage, fingerprint)
const page = await context.newPage();
await page.goto(TARGET);
console.log(JSON.stringify({ framework: 'playwright', title: await page.title(), heading: await page.locator('h1').first().innerText() }));
await page.close();
await browser.close(); // closes the CDP connection only; the profile keeps running until /stop
} finally {
await api('POST', `/profiles/${profile.id}/stop`).catch(() => {});
await api('DELETE', `/profiles/${profile.id}`).catch(() => {});
}
Node.js + Puppeteer
examples/node-puppeteer.mjs
/* global fetch, navigator */
// Blend + Puppeteer: start a profile and drive it. Usage: BLEND_TOKEN=... node node-puppeteer.mjs
import puppeteer from 'puppeteer-core';
const API = process.env.BLEND_API ?? 'http://127.0.0.1:3001/api/v1';
const TOKEN = process.env.BLEND_TOKEN;
const TARGET = process.env.TARGET_URL ?? 'https://example.com';
if (!TOKEN) throw new Error('Set BLEND_TOKEN (Settings → API in the Blend app, or `pnpm blend token`)');
async function api(method, path, body) {
const r = await fetch(API + path, { method, headers: { authorization: `Bearer ${TOKEN}`, 'content-type': 'application/json' }, body: body && JSON.stringify(body) });
const j = await r.json();
if (!r.ok) throw new Error(`${method} ${path}: ${j.error}`);
return j;
}
const profile = await api('POST', '/profiles', { name: `example-puppeteer-${Date.now()}` });
try {
await api('POST', `/profiles/${profile.id}/start`, { headless: process.env.HEADLESS === '1' });
const { wsEndpoint } = await api('GET', `/profiles/${profile.id}/debug`);
const browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint, defaultViewport: null });
const page = await browser.newPage();
await page.goto(TARGET, { waitUntil: 'domcontentloaded' });
console.log(JSON.stringify({ framework: 'puppeteer', title: await page.title(), userAgent: await page.evaluate(() => navigator.userAgent) }));
await browser.disconnect();
} finally {
await api('POST', `/profiles/${profile.id}/stop`).catch(() => {});
await api('DELETE', `/profiles/${profile.id}`).catch(() => {});
}
Python + Playwright
examples/python_playwright.py
"""Blend + Playwright for Python. Usage: BLEND_TOKEN=... python python_playwright.py"""
import json
import os
import time
import requests
from playwright.sync_api import sync_playwright
API = os.environ.get("BLEND_API", "http://127.0.0.1:3001/api/v1")
TOKEN = os.environ.get("BLEND_TOKEN") or exit("Set BLEND_TOKEN (Settings -> API in the Blend app)")
TARGET = os.environ.get("TARGET_URL", "https://example.com")
def api(method, path, body=None):
r = requests.request(method, API + path, json=body, headers={"Authorization": f"Bearer {TOKEN}"}, timeout=120)
data = r.json()
if not r.ok:
raise RuntimeError(f"{method} {path}: {data.get('error')}")
return data
profile = api("POST", "/profiles", {"name": f"example-py-playwright-{int(time.time())}"})
try:
api("POST", f"/profiles/{profile['id']}/start", {"headless": os.environ.get("HEADLESS") == "1"})
debug = api("GET", f"/profiles/{profile['id']}/debug")
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(debug["browserURL"])
page = browser.contexts[0].new_page()
page.goto(TARGET)
print(json.dumps({"framework": "python-playwright", "title": page.title()}))
page.close()
browser.close()
finally:
for method, path in (("POST", "/stop"), ("DELETE", "")):
try:
api(method, f"/profiles/{profile['id']}{path}")
except Exception:
pass
Python + Selenium
examples/python_selenium.py
"""Blend + Selenium (attach to a running profile). Usage: BLEND_TOKEN=... python python_selenium.py"""
import json
import os
import time
import requests
from selenium import webdriver
API = os.environ.get("BLEND_API", "http://127.0.0.1:3001/api/v1")
TOKEN = os.environ.get("BLEND_TOKEN") or exit("Set BLEND_TOKEN (Settings -> API in the Blend app)")
TARGET = os.environ.get("TARGET_URL", "https://example.com")
def api(method, path, body=None):
r = requests.request(method, API + path, json=body, headers={"Authorization": f"Bearer {TOKEN}"}, timeout=120)
data = r.json()
if not r.ok:
raise RuntimeError(f"{method} {path}: {data.get('error')}")
return data
profile = api("POST", "/profiles", {"name": f"example-selenium-{int(time.time())}"})
try:
api("POST", f"/profiles/{profile['id']}/start", {"headless": os.environ.get("HEADLESS") == "1"})
debug = api("GET", f"/profiles/{profile['id']}/debug")
version = requests.get(debug["browserURL"] + "/json/version", timeout=10).json()["Browser"].split("/")[1]
options = webdriver.ChromeOptions()
options.debugger_address = debug["debuggerAddress"] # attach; do not launch a new browser
options.browser_version = version # Selenium Manager fetches the matching ChromeDriver
if os.environ.get("CHROMEDRIVER"):
service = webdriver.ChromeService(executable_path=os.environ["CHROMEDRIVER"])
else:
service = webdriver.ChromeService()
driver = webdriver.Chrome(options=options, service=service)
driver.get(TARGET)
print(json.dumps({"framework": "selenium", "title": driver.title}))
driver.quit() # with debuggerAddress this ends the WebDriver session; Blend stops the browser below
finally:
for method, path in (("POST", "/stop"), ("DELETE", "")):
try:
api(method, f"/profiles/{profile['id']}{path}")
except Exception:
pass