Kameleo 4.0 introduces a modernised Local API that’s simpler to use, easier to integrate, and aligned with REST best practices. Most changes were driven by long‑standing community requests and developer feedback collected since the 3.x series. Kameleo’s user base has grown from hobby‑level scrapers to large automation teams. To make day‑to‑day scripting less verbose and to expose advanced fingerprint features in a consistent way, we refactored several core concepts and rationalised a few legacy endpoints.
New terminology: Base Profiles ➜ Fingerprints
All BaseProfile*
entities and related endpoints are now called Fingerprint*
. “Fingerprint” more accurately describes the bundle of browser and device attributes you select before creating a virtual profile.
Impact on SDK / API calls:
-
GET /base-profiles
→GET /fingerprints
-
DTOs and properties such as
BaseProfile
orBaseProfileId
are renamed accordingly.
Android mobile app sunset
Kameleo 4.0 no longer supports launching native mobile profiles on physical Android devices through the Kameleo Mobile App. Instead, emulate mobile fingerprints on desktop via Chroma. Consequently, the launcher
field has been removed from the Profile
schema.
Fingerprint search by browser version
You can now filter the /fingerprints
list by the exact major browser version through the browserVersion
parameter, which is ideal when a target site flags mismatched user‑agent versions. The GUI will add the same filter in an upcoming minor release.
Universal language support
Starting in Kameleo 4.0, any Fingerprint can be launched in any of our 120 supported languages, so the language
filter has been removed from the /fingerprint
API endpoint. Choose a fingerprint, pick your locale, and Kameleo handles all the regional quirks behind the scenes.
HTTP verb corrections
Endpoint | Old verb | New verb | Reason |
---|---|---|---|
/profiles/{id}/start |
GET | POST | Action causes a state change, so it shouldn’t be idempotent. |
/profiles/{id}/duplicate |
GET | POST | Consistency with other create‑style operations. |
/profiles/{id}/upgrade |
GET | POST | Operation alters the internal profile state, so it shouldn’t be idempotent. |
Data location & app settings hierarchy
The default location for all data folders (Workspace, Logs, Kernels, etc) has been changed:
-
Windows:
%LOCALAPPDATA%\Programs\Kameleo
→%APPDATA%\Kameleo
- macOS:
~/Library/Application Support/Kameleo
(unchanged)
Starting in Kameleo 4.0, multiple configuration sources are supported, so Kameleo now merges appsettings.json
from the executable's directory, and the data folder. For example, on Windows, it reads the config file from both %APPDATA%\Programs\Kameleo\appsettings.json
(the default installation directory) and %APPDATA%\Kameleo\appsettings.json
(the new data folder).
Unified profile state
PersistenceState
has been removed. All status information (running, stopped, upgrading, etc.) is now encoded in a single enum: ProfileLifetimeState
.
Create profiles with one‑line calls
Every field in the CreateProfileRequest
body is optional. If you omit a property, Kameleo automatically chooses the most realistic value based on the selected fingerprint, proxy, and OS.
Local API Client changes
We ship Client packages to wrap our API to make development easier for you. The above mentioned changes effect the Local API Clients as well. Here we list the most important changes that will help you to understand how to migrate to Kameleo 4.0.0
Create profile
Old code
-
const chromeBaseProfileList = await client.searchBaseProfiles({ deviceType: 'desktop', osFamily: 'windows', browserProduct: 'chrome', language: 'es-es', }); const createProfileRequest = BuilderForCreateProfile .forBaseProfile(chromeBaseProfileList[0].id) .setName('create profile example') .setRecommededDefaults() .build(); const profile = await client.createProfile({ body: createProfileRequest, });
-
var baseProfileList = await client.SearchBaseProfilesAsync("desktop", "windows", "chrome", "es-es"); var createProfileRequest = BuilderForCreateProfile .ForBaseProfile(selectedBaseProfile.Id) .SetName("create profile example") .SetRecommendedDefaults() .Build(); var profile = await client.CreateProfileAsync(createProfileRequest);
-
base_profiles = client.search_base_profiles( device_type='desktop', browser_product='chrome', language='es-es' ) create_profile_request = BuilderForCreateProfile \ .for_base_profile(base_profiles[0].id) \ .set_name('create profile example') \ .set_recommeded_defaults() .build() profile = client.create_profile(body=create_profile_request)
New code
-
const fingerprints = await client.fingerprint.searchFingerprints("desktop", "windows", "chrome", "134"); const createProfileRequest = { fingerprintId: fingerprints[0].id, name: "create profile example", language: "es-es", }; const profile = await client.profile.createProfile(createProfileRequest);
-
var fingerprints = await client.Fingerprint.SearchFingerprintsAsync("desktop", "windows", "chrome", "134"); var createProfileRequest = new CreateProfileRequest(fingerprints[0]) { Name = "create profile example", Language = "es-es", };
-
fingerprints = client.fingerprint.search_fingerprints( device_type='desktop', browser_product='chrome', browser_version='134', ) create_profile_request = CreateProfileRequest( fingerprint_id=fingerprints[0].id, language="es-es", name='create profile example',) profile = client.profile.create_profile(create_profile_request)
Set proxy
Old code
-
var createProfileRequest = BuilderForCreateProfile .ForBaseProfile(baseProfileList[0].Id) .SetName("start with proxy example") .SetRecommendedDefaults() .SetProxy("socks5", new Server(proxyHost, proxyPort, proxyUsername, proxyPassword)) .Build(); var profile = await client.CreateProfileAsync(createProfileRequest);
-
const createProfileRequest = BuilderForCreateProfile .forBaseProfile(chromeBaseProfileList[0].id) .setName('start with proxy example') .setRecommendedDefaults() .setProxy( 'socks5', { host: process.env.PROXY_HOST || '', port: Number(process.env.PROXY_PORT) || 1080, id: process.env.PROXY_USERNAME || '', secret: process.env.PROXY_PASSWORD || '', }, ) .build(); const profile = await client.createProfile({ body: createProfileRequest, });
-
create_profile_request = BuilderForCreateProfile \ .for_base_profile(base_profiles[0].id) \ .set_name('start with proxy example') \ .set_recommended_defaults() \ .set_proxy('socks5', Server(host=PROXY_HOST, port=PROXY_PORT, id=PROXY_USERNAME, secret=PROXY_PASSWORD)) \ .build() profile = client.create_profile(body=create_profile_request)
New code
-
const createProfileRequest = { fingerprintId: fingerprints[0].id, name: "start with proxy example", proxy: { value: "socks5", extra: { host: "", port: 1080, id: "", secret: "", }, }, }; const profile = await client.profile.createProfile(createProfileRequest);
-
var createProfileRequest = new CreateProfileRequest(fingerprints[0].Id) { Name = "start with proxy example", Proxy = new (ProxyConnectionType.Socks5, new Server(proxyHost, proxyPort, proxyUsername, proxyPassword)) }; var profile = await client.Profile.CreateProfileAsync(createProfileRequest);
-
create_profile_request = CreateProfileRequest( fingerprint_id=fingerprints[0].id, name='start with proxy example', proxy=ProxyChoice( value='socks5', extra=Server(host=PROXY_HOST, port=PROXY_PORT, id=PROXY_USERNAME, secret=PROXY_PASSWORD) )) profile = client.profile.create_profile(create_profile_request)
Basic operations
Old code
-
// Start the profile await client.startProfile(profile.id); // Stop the profile await client.stopProfile(profile.id); // Delete original profile // Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo await client.deleteProfile(profile.id);
-
// Start the profile await client.StartProfileAsync(profile.Id); // Stop the profile await client.StopProfileAsync(profile.Id); // Delete original profile // Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo await client.DeleteProfileAsync(profile.Id);
-
# Start the browser profile client.start_profile(profile.id) # Stop the browser by stopping the Kameleo profile client.stop_profile(profile.id) # Delete original profile # Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo client.delete_profile(profile.id)
New code
-
// Start the profile await client.profile.startProfile(profile.id); // Stop the profile await client.profile.stopProfile(profile.id); // Delete original profile // Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo await client.profile.deleteProfile(profile.id);
-
// Start the profile await client.Profile.StartProfileAsync(profile.Id); // Stop the profile await client.Profile.StopProfileAsync(profile.Id); // Delete original profile // Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo await client.Profile.DeleteProfileAsync(profile.Id);
-
# Start the duplicated browser profile client.profile.start_profile(profile.id) # Stop the browser by stopping the Kameleo profile client.profile.stop_profile(profile.id) # Delete original profile # Profiles need to be deleted explicitly becase they are persisted so they are available after restarting Kameleo client.profile.delete_profile(profile.id)