# Manage profiles

In this tutorial you will walk through the full lifecycle of a browser profile: search a fingerprint, create a profile, start and stop it, update settings (add a proxy), duplicate via export/import, and finally delete it when no longer needed.

# Prerequisites

  • Completion of the Quickstart guide
  • Kameleo app running locally (http://localhost:5050)

# 1. Search a fingerprint

Pick a recent desktop Chrome fingerprint (adjust filters as needed). Limit filtering at first, then narrow.

fps = client.fingerprint.search_fingerprints(device_type="desktop", browser_product="chrome")
fingerprint = fps[0]
print("Selected fingerprint", fingerprint.id)
const fps = await client.fingerprint.searchFingerprints("desktop", undefined, "chrome");
const fingerprint = fps[0];
console.log("Selected fingerprint", fingerprint.id);
var fps = await client.Fingerprint.SearchFingerprintsAsync(deviceType: "desktop", browserProduct: "chrome");
var fingerprint = fps[0];
Console.WriteLine($"Selected fingerprint {fingerprint.Id}");

# 2. Create the profile

Below is a full example showing every configurable field of the profile creation request. In real use you often only set fingerprintId, name, maybe language, proxy, and a couple of masking overrides. Leave any field out to accept the default inferred from the fingerprint.

from kameleo.local_api_client.models import (
  CreateProfileRequest,
  TimezoneChoice, GeolocationChoice, ProxyChoice, Server, WebRtcChoice,
  ScreenChoice, ScreenSize, HardwareConcurrencyChoice, DeviceMemoryChoice,
  WebglMetaChoice, WebglMetaValue, TimezoneSpoofingType, GeolocationSpoofingType,
  WebRtcSpoofingType, ScreenSpoofingType, HardwareConcurrencySpoofingType,
  DeviceMemorySpoofingType, ProxyConnectionType, WebglMetaSpoofingType
)

# Full request showcasing every available field. Omit fields you don't need.
create_req = CreateProfileRequest(
  fingerprint_id=fingerprint.id,
  name='acct-main',
  folder_id=None,
  tags=['marketing', 'facebook'],
  canvas='intelligent',
  webgl='noise',
  webgl_meta=WebglMetaChoice(
    value=WebglMetaSpoofingType.MANUAL,
    extra=WebglMetaValue(vendor='Intel Inc.', renderer='Intel Iris OpenGL Engine')
  ),
  audio='noise',
  timezone=TimezoneChoice(value=TimezoneSpoofingType.MANUAL, extra='Europe/Berlin'),
  geolocation=GeolocationChoice(value=GeolocationSpoofingType.MANUAL, extra={'latitude': 52.52, 'longitude': 13.405, 'accuracy': 10}),
  proxy=ProxyChoice(value=ProxyConnectionType.HTTP, extra=Server(host='proxy.example', port=8000, id='user', secret='pass')),
  web_rtc=WebRtcChoice(value=WebRtcSpoofingType.MANUAL, extra={'localIp': '10.0.0.5', 'publicIp': '93.184.216.34'}),
  fonts='automatic',
  screen=ScreenChoice(value=ScreenSpoofingType.MANUAL, extra=ScreenSize(width=1920, height=1080)),
  hardware_concurrency=HardwareConcurrencyChoice(value=HardwareConcurrencySpoofingType.MANUAL, extra=8),
  device_memory=DeviceMemoryChoice(value=DeviceMemorySpoofingType.MANUAL, extra=8),
  language='en-US',
  start_page='https://whoer.net/',
  password_manager='enabled',
  extensions=['/absolute/path/extension.crx'],
  notes='I used this profile for affiliate marketing.',
  storage='local'
)

profile = client.profile.create_profile(create_req)
print('Created profile', profile.id)
// Full request showcasing every available field. Omit fields you don't need.
const profile = await client.profile.createProfile({
    fingerprintId: fingerprint.id,
    name: "acct-main",
    folderId: null,
    tags: ["marketing", "facebook"],
    canvas: "intelligent",
    webgl: "noise",
    webglMeta: { value: "manual", extra: { vendor: "Intel Inc.", renderer: "Intel Iris OpenGL Engine" } },
    audio: "noise",
    timezone: { value: "manual", extra: "Europe/Berlin" },
    geolocation: { value: "manual", extra: { latitude: 52.52, longitude: 13.405, accuracy: 10 } },
    proxy: { value: "http", extra: { host: "proxy.example", port: 8000, id: "user", secret: "pass" } },
    webRtc: { value: "manual", extra: { localIp: "10.0.0.5", publicIp: "93.184.216.34" } },
    fonts: "automatic",
    screen: { value: "manual", extra: { width: 1920, height: 1080 } },
    hardwareConcurrency: { value: "manual", extra: 8 },
    deviceMemory: { value: "manual", extra: 8 },
    language: "en-US",
    startPage: "https://whoer.net/",
    passwordManager: "enabled",
    extensions: ["/absolute/path/extension.crx"],
    notes: "I used this profile for affiliate marketing.",
    storage: "local",
});
console.log("Created profile", profile.id);
using Kameleo.LocalApiClient.Model;

// Full request showcasing every available field. Omit fields you don't need.
var create = new CreateProfileRequest(
  fingerprintId: fingerprint.Id,
  name: "acct-main",
  folderId: null,
  tags: new List<string> { "marketing", "facebook" },
  canvas: CanvasSpoofingType.Intelligent,
  webgl: WebglSpoofingType.Noise,
  webglMeta: new WebglMetaChoice(WebglMetaSpoofingType.Manual, new WebglMetaValue(vendor: "Intel Inc.", renderer: "Intel Iris OpenGL Engine")),
  audio: AudioSpoofingType.Noise,
  timezone: new TimezoneChoice(TimezoneSpoofingType.Manual, extra: "Europe/Berlin"),
  geolocation: new GeolocationChoice(GeolocationSpoofingType.Manual, new GeolocationValue(latitude: 52.52, longitude: 13.405, accuracy: 10)),
  proxy: new ProxyChoice(ProxyConnectionType.Http, new Server(host: "proxy.example", port: 8000, id: "user", secret: "pass")),
  webRtc: new WebRtcChoice(WebRtcSpoofingType.Manual, new WebRtcSpoofingOptions(publicIp: "93.184.216.34")),
  fonts: FontSpoofingType.Automatic,
  screen: new ScreenChoice(ScreenSpoofingType.Manual, new ScreenSize(width: 1920, height: 1080)),
  hardwareConcurrency: new HardwareConcurrencyChoice(HardwareConcurrencySpoofingType.Manual, extra: 8),
  deviceMemory: new DeviceMemoryChoice(DeviceMemorySpoofingType.Manual, extra: 8),
  language: "en-US",
  startPage: "https://whoer.net/",
  passwordManager: PasswordManagerType.Enabled,
  extensions: new List<string> { "/absolute/path/extension.crx" },
  notes: "I used this profile for affiliate marketing.",
  storage: ProfileStorageLocation.Local
);

var profile = await client.Profile.CreateProfileAsync(create);
Console.WriteLine($"Created profile {profile.Id}");

# 3. Start and stop the profile

Only start when you are ready to automate. Stop to free resources; the session state (cookies, storage) is preserved.

client.profile.start_profile(profile.id)
print('Profile started')

client.profile.stop_profile(profile.id)
print('Profile stopped')
await client.profile.startProfile(profile.id);
console.log("Profile started");

await client.profile.stopProfile(profile.id);
console.log("Profile stopped");
await client.Profile.StartProfileAsync(profile.Id);
Console.WriteLine("Profile started");

await client.Profile.StopProfileAsync(profile.Id);
Console.WriteLine("Profile stopped");

# 4. List profiles with filtering by lifetime state

After creating and starting profiles, you may want to list them and filter by their current lifetime state. Profiles can be in one of several states: created, starting, running, terminating, terminated, locked, loading, or unknown. Filtering by state helps monitor which profiles are active and manage resources efficiently.

from kameleo.local_api_client.models import ProfileLifetimeState

# List all profiles
all_profiles = client.profile.list_profiles()
print(f'Total profiles: {len(all_profiles)}')

# Filter for running profiles only
running_profiles = client.profile.list_profiles(ProfileLifetimeState.RUNNING)
print(f'Running profiles: {len(running_profiles)}')
for p in running_profiles:
    print(f'  - {p.name} (ID: {p.id})')

# Filter for terminated profiles (stopped but previously started)
terminated_profiles = client.profile.list_profiles(ProfileLifetimeState.TERMINATED)
print(f'Terminated profiles: {len(terminated_profiles)}')
for p in terminated_profiles:
    print(f'  - {p.name} (ID: {p.id})')

# Filter for newly created profiles (never started)
created_profiles = client.profile.list_profiles(ProfileLifetimeState.CREATED)
print(f'Created profiles: {len(created_profiles)}')
// List all profiles
const allProfiles = await client.profile.listProfiles();
console.log(`Total profiles: ${allProfiles.length}`);

// Filter for running profiles only
const runningProfiles = await client.profile.listProfiles("running");
console.log(`Running profiles: ${runningProfiles.length}`);
runningProfiles.forEach((p) => console.log(`  - ${p.name} (ID: ${p.id})`));

// Filter for terminated profiles (stopped but previously started)
const terminatedProfiles = await client.profile.listProfiles("terminated");
console.log(`Terminated profiles: ${terminatedProfiles.length}`);
terminatedProfiles.forEach((p) => console.log(`  - ${p.name} (ID: ${p.id})`));

// Filter for newly created profiles (never started)
const createdProfiles = await client.profile.listProfiles("created");
console.log(`Created profiles: ${createdProfiles.length}`);
using Kameleo.LocalApiClient.Model;

// List all profiles
var allProfiles = await client.Profile.ListProfilesAsync();
Console.WriteLine($"Total profiles: {allProfiles.Count}");

// Filter for running profiles only
var runningProfiles = await client.Profile.ListProfilesAsync(ProfileLifetimeState.Running);
Console.WriteLine($"Running profiles: {runningProfiles.Count}");
foreach (var p in runningProfiles)
{
    Console.WriteLine($"  - {p.Name} (ID: {p.Id})");
}

// Filter for terminated profiles (stopped but previously started)
var terminatedProfiles = await client.Profile.ListProfilesAsync(ProfileLifetimeState.Terminated);
Console.WriteLine($"Terminated profiles: {terminatedProfiles.Count}");
foreach (var p in terminatedProfiles)
{
    Console.WriteLine($"  - {p.Name} (ID: {p.Id})");
}

// Filter for newly created profiles (never started)
var createdProfiles = await client.Profile.ListProfilesAsync(ProfileLifetimeState.Created);
Console.WriteLine($"Created profiles: {createdProfiles.Count}");

# 5. Update settings (add a proxy)

Updates require the profile to be in a stopped (terminated) state. Attempting to change network settings while running can fail or cause instability.

from kameleo.local_api_client.models import ProxyChoice, Server, UpdateProfileRequest, ProxyConnectionType

update_req = UpdateProfileRequest(proxy=ProxyChoice(value=ProxyConnectionType.HTTP, extra=Server(host='proxy.example', port=8000, id='u', secret='p')))
updated = client.profile.update_profile(profile.id, update_profile_request=update_req)
print('Proxy set to', updated.proxy.value)
const updated = await client.profile.updateProfile(profile.id, {
    proxy: { value: "http", extra: { host: "proxy.example", port: 8000, id: "u", secret: "p" } },
});
console.log("Proxy set to", updated.proxy?.value);
var update = new UpdateProfileRequest(proxy: new ProxyChoice(ProxyConnectionType.Http, new Server(host: "proxy.example", port: 8000, id: "u", secret: "p")));
var updated = await client.Profile.UpdateProfileAsync(profile.Id, update);
Console.WriteLine($"Proxy set to {updated.Proxy?.Value}");

# 6. Export and import (backup / restore)

Export creates a portable archive (.kameleo) of the existing profile (including settings, storage, cookies, history, bookmarks, extensions). Importing restores that exact profile (same profile ID). Import will fail if a profile with the same ID is already loaded.

# Ensure the profile is stopped, then back it up
from kameleo.local_api_client.models import ExportProfileRequest, ImportProfileRequest

client.profile.stop_profile(profile.id)
client.profile.export_profile(profile.id, export_profile_request=ExportProfileRequest(path='./acct-main.kameleo'))
print('Exported profile to acct-main.kameleo')

# Remove (or move to another machine) before restoring; import will fail if same ID is still loaded
client.profile.delete_profile(profile.id)
restored = client.profile.import_profile(import_profile_request=ImportProfileRequest(path='./acct-main.kameleo'))
print('Restored profile with SAME ID', restored.id)
// Stop, export, delete, then import to restore
await client.profile.stopProfile(profile.id);
await client.profile.exportProfile(profile.id, { path: "./acct-main.kameleo" });
console.log("Exported profile to acct-main.kameleo");

await client.profile.deleteProfile(profile.id);
const restored = await client.profile.importProfile({ path: "./acct-main.kameleo" });
console.log("Restored profile with SAME ID", restored.id);
await client.Profile.StopProfileAsync(profile.Id);
await client.Profile.ExportProfileAsync(profile.Id, new ExportProfileRequest(path: "./acct-main.kameleo"));
Console.WriteLine("Exported profile to acct-main.kameleo");

await client.Profile.DeleteProfileAsync(profile.Id);
var restored = await client.Profile.ImportProfileAsync(new ImportProfileRequest(path: "./acct-main.kameleo"));
Console.WriteLine($"Restored profile with SAME ID {restored.Id}");

# 7. Upgrade the profile (browser/kernel)

Keep profiles current to benefit from the latest browser security patches, modern TLS/cipher suites, and widely distributed real‑world fingerprints. Upgrading refreshes the profile's fingerprint to the newest matching browser version and (if applicable) switches to a better fitting kernel. The profile ID, local storage (cookies, history, etc.), and your settings remain; only the browser fingerprinting surface changes.

# Ensure the profile is stopped before upgrading
client.profile.stop_profile(profile.id)

upgraded = client.profile.upgrade_profile_kernel(profile.id)
print('Upgraded browser to', upgraded.fingerprint.browser.product, upgraded.fingerprint.browser.version)
// Make sure the profile is stopped first
await client.profile.stopProfile(profile.id);

const upgraded = await client.profile.upgradeProfileKernel(profile.id);
if (upgraded) {
    console.log("Upgraded browser to", upgraded.fingerprint.browser.product, upgraded.fingerprint.browser.version);
} else {
    console.log("Profile already up to date");
}
// Stop the profile if it is running
await client.Profile.StopProfileAsync(profile.Id);

profile = await client.Profile.UpgradeProfileKernelAsync(profile.Id);
Console.WriteLine($"Browser after upgrade: {profile.Fingerprint.Browser.Product} {profile.Fingerprint.Browser.VarVersion}");

# 8. Delete when finished

Remove profiles you no longer need to keep the workspace clean and reclaim disk space (profiles store cached browsing data, extensions, cookies, history, and other artifacts).

client.profile.delete_profile(profile.id)
print('Deleted original profile')
await client.profile.deleteProfile(profile.id);
console.log("Deleted original profile");
await client.Profile.DeleteProfileAsync(profile.Id);
Console.WriteLine("Deleted original profile");