Getting Started
To start using Ateratti, all you need is the browser executable and an API key. The browser can be launched without any additional command line arguments, which essentially runs Chromium without much to it. To actually benefit from Ateratti fingerprinting capabilities, you need to provide the --ateratti-key argument to the executable. This argument contains information about the fingerprint that is going to be emulated:
./chrome --ateratti-key="W7xUeqJ1t…4QuxV9Hum5A=="To use Ateratti with browser automation libraries, you also often need a chromedriver executable, as otherwise the library could use a preinstalled one, which will have webdriver leaks. We distribute our own version of chromedriver next to the main browser binary, so you should always use if your library supports it.
One-off fingerprints
The simplest way to launch the browser is to do that with a temporary profile, as it doesn't require having a fingerprint in the database:
import chrome from "selenium-webdriver/chrome";
import { Builder } from "selenium-webdriver";
const response = await fetch("https://api.ateratti.com/launch", {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
}),
});
const data = await response.json();
const aterattiKey = data["aterattiKey"];
const options = new chrome.Options();
options.setChromeBinaryPath("/path/to/chrome");
options.addArguments("--ateratti-key=" + aterattiKey);
options.addArguments("--user-data-dir=" + "/path/to/profile");
const service = new chrome.ServiceBuilder("/path/to/chromedriver");
const driver = new Builder()
.setChromeService(service)
.setChromeOptions(options)
.forBrowser("chrome")
.build();
await driver.get("https://docs.ateratti.com/");In this example, we make a POST request to the /launch endpoint, which is documented in the API Reference, and provide nothing but a preset field in the request body. By doing it this way, the server will randomly generate a fingerprint that can be used to launch the browser, but won't be stored in the database, which might be very useful if you don't want to keep track of fingerprints.
The --ateratti-key is necessary to launch the browser with fingerprint spoofing, and it mainly consists of two parts, which are encrypted but can be seen in the response body:
- Static fingerprint data, e.g., operating system, CPU, RAM, WebGL, etc.
- Dynamic geo-specific data: IP address, country, languages and timezone
As you are probably going to use Ateratti with proxies, the above example doesn't really work for us, as the resulting fingerprint will contain geo-specific data determined from the IP address used to make the request (i.e., your IP). There are multiple options to change that:
- Send the launch request through a proxy, so the server would use its IP for geo lookup
- Tell the server to use a different IP address for the lookup
- Specify geo-specific fields in the request
- Override the geo-specific fields when launching the browser
1. Proxying the launch request
As the server's default strategy is to use the IP of the request sender for generating geo-specific fields, the easiest way to make it use the proxy IP is to proxy the request itself:
npm i node-fetchnpm i https-proxy-agentimport fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";
const response = await fetch("https://api.ateratti.com/launch", {
agent: new HttpsProxyAgent("http://user:pass@host:1234"),
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
}),
});
const data = await response.json();
console.log(data);2. Specify the IP manually
If you know the IP address of the proxy beforehand, then it makes more sense to not use it for the launch request, but instead provide its IP in the query parameters:
const response = await fetch(
"https://api.ateratti.com/launch?ip=11.22.33.44",
{
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
}),
},
);
const data = await response.json();
console.log(data);This way the server will use the provided IP address in case it needs to perform a geo lookup. This could also be useful in case you are launching the browser on a machine you don't want to expose API keys to. You could transfer the proxy IP (or make a request through it) to your server where the API keys are stored, and make the launch request from it.
3. Specify geo-specific fields
In case you don't want the geo lookup to be performed on our side, you could always provide the needed fields yourself:
const response = await fetch("https://api.ateratti.com/launch", {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
"country_code": "GB",
"languages": "en-GB,en-US,en",
"tz": "Europe/London",
"ip4": "11.22.33.44",
"ip6": "11:22::33:44"
}),
});
const data = await response.json();
console.log(data);In general, you should not touch geo-specific fields yourself, as the lookup only adds about 1.2-1.5ms to the response time. The only reason to not utilize it is if you have a very specific use case, e.g., you verified that your own geo lookup mechanism is more precise, and it makes a difference on the target website.
4. Override geo-specific fields
The last option to override geolocation and IP data is to pass it to the browser as command line arguments. There are exactly 5 flags to do that:
This might be useful if you want to start the browser as soon as possible, but the proxy IP is unknown at the time of making the launch request. In such case, you could make two requests, one for the browser launch, and one for the lookup, and then use data from both to start the browser. Here's an example for how to override the fields:
import chrome from "selenium-webdriver/chrome";
import { Builder } from "selenium-webdriver";
const geoData = {
countryCode: "GB",
languages: "en-GB,en-US,en",
tz: "Europe/London",
ip4: "11.22.33.44",
ip6: "11:22::33:44",
};
const response = await fetch("https://api.ateratti.com/launch", {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
}),
});
const data = await response.json();
const aterattiKey = data["aterattiKey"];
const options = new chrome.Options();
options.setChromeBinaryPath("/path/to/chrome");
options.addArguments("--ateratti-key=" + aterattiKey);
options.addArguments("--user-data-dir=" + "/path/to/profile");
options.addArguments("--override-country=" + geoData.countryCode);
options.addArguments("--override-languages=" + geoData.languages);
options.addArguments("--override-tz=" + geoData.tz);
options.addArguments("--override-ip4=" + geoData.ip4);
options.addArguments("--override-ip6=" + geoData.ip6);
const service = new chrome.ServiceBuilder("/path/to/chromedriver");
const driver = new Builder()
.setChromeService(service)
.setChromeOptions(options)
.forBrowser("chrome")
.build();
await driver.get("https://docs.ateratti.com/");We have a separate geo lookup endpoint to support such use in case it fits you.
Database fingerprints
Ateratti is designed in a way that lets you store fingerprints yourself and never interact with our database, but, if you decide that you would benefit from utilizing it, we provide a basic CRUD API for managing fingerprints.
As /fp endpoints are described in detail in the API Reference, we will only look at them in the context of launching the browser. Here's a simple example on how to create a fingerprint and launch the browser with it:
import chrome from "selenium-webdriver/chrome";
import { Builder } from "selenium-webdriver";
const fpResponse = await fetch("https://api.ateratti.com/fp", {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
}),
});
// Contains the newly created fingerprint
const fp = await fpResponse.json();
console.log(fp);
const id = fp["id"];
const launchResponse = await fetch(`https://api.ateratti.com/launch?id=${id}`, {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
});
const data = await launchResponse.json();
const aterattiKey = data["aterattiKey"];
const options = new chrome.Options();
options.setChromeBinaryPath("/path/to/chrome");
options.addArguments("--ateratti-key=" + aterattiKey);
options.addArguments("--user-data-dir=" + "/path/to/profile");
const service = new chrome.ServiceBuilder("/path/to/chromedriver");
const driver = new Builder()
.setChromeService(service)
.setChromeOptions(options)
.forBrowser("chrome")
.build();
await driver.get("https://docs.ateratti.com/");Combine fingerprints
It is possible to use a fingerprint in the database and add overrides to it in the launch request. When the server constructs the final object that is going to be used by the browser, it follows three simple principles:
- Data provided in the request takes precedence over any other source
- Data in the database takes precedence over geo lookup
- IP lookup is only used if geo-specific fields weren't provided by the two previous sources
This means you have multiple options for how to combine the resulting fingerprint, and the most common use case for that is if you don't want to keep any fingerprint-related logic on the machine performing browser automation. For example, you could reuse the same set of database fingerprints, but slightly modify them depending on your user's plan and preferences. As you are probably storing this information server-side, it makes more sense to provide the overrides in the request rather than when launching the browser.
Consider the following example:
const fpResponse = await fetch("https://api.ateratti.com/fp", {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
preset: "windows",
screen: {
w: 1920,
h: 1080,
},
}),
});
const fp = await fpResponse.json();
const id = fp["id"];
const launchResponse = await fetch(`https://api.ateratti.com/launch?id=${id}`, {
method: "POST",
headers: { "X-Api-Key": "Your API key" },
body: JSON.stringify({
screen: {
w: 2560,
h: 1440,
},
languages: "en-US,en",
}),
});
const data = await launchResponse.json();
console.log(data);In the example, when the server will be constructing the final fingerprint in the launch phase, it will:
- Fetch the fingerprint from the database
- Apply
screenandlanguagesoverrides from the request - Perform a geo lookup to fill all fields except for
languagesas they were already provided
The resulting object will have a screen resolution of 2560x1440, default en-US,en languages, and everything else filled randomly or based on the result of an IP lookup.