Chat with us, powered by LiveChat
← Return to MyDisct Solver

Default Configuration Methods

The BBREClient class provides four default setter methods that let you change the client's default configuration after construction: setDefaultFingerprint(), setDefaultProxy(), setDefaultMode(), and setDefaultSensibility(). These methods modify the internal default values that the client uses as fallbacks when you call request(), createSession(), or any of the HTTP convenience methods (get(), post(), etc.) without explicitly specifying those parameters. The initial defaults are set through the constructor's config object, but the setter methods give you the ability to adjust them at any point during your application's lifecycle without creating a new client instance. This is particularly useful for applications that need to switch between different proxy servers based on geographic region, change the operating mode from passive to adaptive mid-workflow, adjust sensibility levels based on target site behavior, or update fingerprint profiles to rotate browser identities. Each setter method directly overwrites the corresponding internal property on the client instance, and the new value takes effect immediately for all subsequent requests that do not provide their own override. This page documents all four default setter methods with complete method signatures, parameter descriptions, detailed explanations of how defaults interact with per-request overrides, practical code examples for real-world scenarios, best practices, common issues, and links to related documentation.

How Default Values Work in BBREClient

When you create a BBREClient instance, the constructor sets initial defaults from the config object: mode defaults to "passive", sensibility defaults to "medium", fingerprint defaults to {}, and proxy defaults to null. Every time you call request() or createSession(), the client uses these defaults as fallback values for any parameter you do not explicitly provide. The four setter methods documented on this page let you change these defaults after construction. The new defaults apply to all future calls, but they never retroactively affect requests that are already in progress. Per-request parameters always take priority over defaults, so you can override any default on a case-by-case basis without changing the client configuration.

client.setDefaultFingerprint(fingerprint)

The setDefaultFingerprint() method sets the default browser fingerprint profile that the client uses for all subsequent requests and sessions. A fingerprint object describes the browser identity that the BBRE engine should emulate when processing your request, including properties like userAgent, platform, screen resolution, timezone, and language. When you call request() or createSession(), the client merges the default fingerprint with any per-request fingerprint you provide using the JavaScript spread operator. The per-request fingerprint properties take priority, so you can selectively override specific fields while keeping the rest of the default profile intact. If you pass a falsy value (such as null, undefined, or false) to this method, the default fingerprint resets to an empty object {}, which means the BBRE engine will use its own automatically generated fingerprint profile for subsequent requests.

Method Signature

JavaScript
client.setDefaultFingerprint(fingerprint);

Parameters

Parameter Type Required Description
fingerprint object Optional A fingerprint object containing browser identity properties such as userAgent, platform, screen, timezone, and language. If a falsy value is provided, the default fingerprint resets to an empty object {}.

Return Type

This method does not return a value. It modifies the internal defaultFingerprint property on the client instance directly.

Fingerprint Merge Behavior

Understanding how the default fingerprint merges with per-request fingerprints is critical for using this method effectively. When you call request() or createSession(), the client creates a merged fingerprint using the JavaScript spread operator: { ...this.defaultFingerprint, ...fingerprint }. This means the default fingerprint serves as the base layer, and any properties you specify in the per-request fingerprint object overwrite the corresponding default properties. Properties that exist only in the default fingerprint are preserved in the merged result. Properties that exist only in the per-request fingerprint are added to the merged result. If both objects define the same property, the per-request value wins.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultFingerprint({
  userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
  platform: "Win32",
  language: "en-US",
  timezone: "America/New_York"
});

const response = await client.request({
  url: "https://httpbin.org/headers",
  fingerprint: {
    timezone: "Europe/London",
    screen: { width: 1920, height: 1080 }
  }
});

console.log("Request sent with merged fingerprint:");
console.log("  userAgent: from default (Mozilla/5.0 ...)");
console.log("  platform: from default (Win32)");
console.log("  language: from default (en-US)");
console.log("  timezone: overridden to Europe/London");
console.log("  screen: added from per-request (1920x1080)");

Setting a Complete Fingerprint Profile

The most common use case is setting a complete fingerprint profile that represents a specific browser identity. This profile will be used as the base for all requests unless overridden.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultFingerprint({
  userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
  platform: "MacIntel",
  language: "en-US",
  timezone: "America/Los_Angeles",
  screen: { width: 2560, height: 1440 }
});

const response = await client.get("https://httpbin.org/headers");
console.log("Status:", response.statusCode);

Rotating Fingerprint Profiles

For applications that need to rotate browser identities across requests, you can call setDefaultFingerprint() before each batch of requests to switch the active profile. This is useful when scraping multiple websites that might correlate requests based on fingerprint consistency.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

const profiles = [
  {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    platform: "Win32",
    language: "en-US",
    timezone: "America/New_York"
  },
  {
    userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
    platform: "MacIntel",
    language: "en-GB",
    timezone: "Europe/London"
  },
  {
    userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    platform: "Linux x86_64",
    language: "de-DE",
    timezone: "Europe/Berlin"
  }
];

async function scrapeWithRotation(urls) {
  const results = [];

  for (let i = 0; i < urls.length; i++) {
    const profile = profiles[i % profiles.length];
    client.setDefaultFingerprint(profile);

    console.log("Using profile:", profile.platform, "-", profile.timezone);

    const response = await client.get(urls[i]);
    results.push({ url: urls[i], status: response.statusCode });
  }

  return results;
}

const urls = [
  "https://example.com/page1",
  "https://example.com/page2",
  "https://example.com/page3"
];

scrapeWithRotation(urls);

Resetting the Default Fingerprint

To clear the default fingerprint and let the BBRE engine generate its own fingerprint profile automatically, pass a falsy value to the method. The internal implementation uses fingerprint || {}, so passing null, undefined, false, 0, or an empty string all result in the default fingerprint being reset to an empty object.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  fingerprint: {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    platform: "Win32"
  }
});

const response1 = await client.get("https://httpbin.org/headers");
console.log("Request 1: used custom fingerprint from constructor");

client.setDefaultFingerprint(null);

const response2 = await client.get("https://httpbin.org/headers");
console.log("Request 2: BBRE engine generates its own fingerprint");

client.setDefaultProxy(proxy)

The setDefaultProxy() method sets the default proxy server URL that the client uses for all subsequent requests and sessions. The proxy value is a string containing the full proxy URL including the protocol, authentication credentials (if needed), host, and port. When you call request() or createSession() without specifying a proxy parameter, the client uses this default proxy. You can also pass null to explicitly indicate that no proxy should be used by default. Understanding the distinction between undefined and null in the proxy resolution logic is important: when a per-request proxy parameter is undefined (meaning it was not provided at all), the client falls back to the default proxy. When a per-request proxy parameter is explicitly set to null, it overrides the default and tells the BBRE engine to make the request without any proxy, regardless of what the default is set to.

Method Signature

JavaScript
client.setDefaultProxy(proxy);

Parameters

Parameter Type Required Description
proxy string | null Required The proxy server URL in the format protocol://user:pass@host:port or protocol://host:port. Pass null to explicitly clear the default proxy and make requests without a proxy.

Return Type

This method does not return a value. It modifies the internal defaultProxy property on the client instance directly.

Proxy Resolution Logic

The proxy resolution in request() and createSession() uses a specific pattern that distinguishes between "not provided" and "explicitly null". The internal logic is: const finalProxy = proxy !== undefined ? proxy : this.defaultProxy;. This means if you pass proxy: "http://myproxy:8080" in the request options, that proxy is used regardless of the default. If you pass proxy: null, no proxy is used regardless of the default. If you omit the proxy parameter entirely (so it is undefined), the default proxy is used. This three-state logic gives you full control over proxy behavior at both the client level and the individual request level.

Per-request proxy Default proxy Final proxy used
"http://proxy-a:8080" "http://proxy-b:8080" "http://proxy-a:8080" (per-request wins)
null "http://proxy-b:8080" null (no proxy, explicit override)
undefined (not provided) "http://proxy-b:8080" "http://proxy-b:8080" (default used)
undefined (not provided) null null (no proxy)

Setting a Default Proxy

The most straightforward use case is setting a proxy that all requests should route through. Once set, every call to request(), get(), post(), and other HTTP methods will use this proxy unless explicitly overridden.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultProxy("http://user:[email protected]:8080");

const response1 = await client.get("https://httpbin.org/ip");
console.log("Request 1 routed through proxy.example.com");

const response2 = await client.get("https://httpbin.org/headers");
console.log("Request 2 also routed through proxy.example.com");

Overriding the Default Proxy Per Request

Even with a default proxy set, you can override it for individual requests by passing a proxy parameter in the request options. You can also explicitly bypass the proxy by passing null.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultProxy("http://us-proxy.example.com:8080");

const response1 = await client.get("https://httpbin.org/ip");
console.log("Used default US proxy");

const response2 = await client.request({
  url: "https://httpbin.org/ip",
  proxy: "http://eu-proxy.example.com:8080"
});
console.log("Used EU proxy override for this request");

const response3 = await client.request({
  url: "https://httpbin.org/ip",
  proxy: null
});
console.log("No proxy used for this request (explicit null override)");

Multi-Region Proxy Rotation

For applications that need to distribute requests across different geographic regions, you can rotate the default proxy between batches of requests. This pattern is useful for geo-targeted scraping, load distribution across proxy providers, and avoiding rate limits on specific proxy IP addresses.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

const regionProxies = {
  us: "http://user:[email protected]:8080",
  eu: "http://user:[email protected]:8080",
  asia: "http://user:[email protected]:8080"
};

async function scrapeByRegion(regionUrls) {
  const results = {};

  for (const [region, urls] of Object.entries(regionUrls)) {
    client.setDefaultProxy(regionProxies[region]);
    console.log("Switched to", region, "proxy");

    results[region] = [];

    for (const url of urls) {
      const response = await client.get(url);
      results[region].push({ url, status: response.statusCode });
    }
  }

  return results;
}

const regionUrls = {
  us: ["https://us-store.example.com/products", "https://us-store.example.com/prices"],
  eu: ["https://eu-store.example.com/products", "https://eu-store.example.com/prices"],
  asia: ["https://asia-store.example.com/products", "https://asia-store.example.com/prices"]
};

scrapeByRegion(regionUrls);

Clearing the Default Proxy

To remove the default proxy and make all subsequent requests go directly through the BBRE engine without a proxy, pass null to the method.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  proxy: "http://user:[email protected]:8080"
});

const response1 = await client.get("https://httpbin.org/ip");
console.log("Request 1: routed through constructor proxy");

client.setDefaultProxy(null);

const response2 = await client.get("https://httpbin.org/ip");
console.log("Request 2: no proxy, direct BBRE engine connection");

client.setDefaultMode(mode)

The setDefaultMode() method sets the default operating mode that the client uses for all subsequent requests and sessions. The BBRE engine supports two operating modes: "passive" and "adaptive". Passive mode handles requests through a lightweight HTTP pipeline without launching a full browser instance, making it faster and cheaper for simple data retrieval tasks. Adaptive mode launches a full browser environment with JavaScript execution, DOM rendering, and anti-detection measures, making it suitable for websites that require browser-level interaction or have sophisticated bot detection. When you call request() or createSession() without specifying a mode parameter, the client uses this default mode. The constructor sets the initial default mode to "passive" unless you specify a different value in the config object. Calling setDefaultMode() lets you switch the default mode at any point during your application's lifecycle, which is particularly useful for workflows that start with passive mode for initial data gathering and then switch to adaptive mode when they encounter pages that require full browser rendering.

Method Signature

JavaScript
client.setDefaultMode(mode);

Parameters

Parameter Type Required Description
mode string Required The operating mode to use as the default. Must be either "passive" for lightweight HTTP requests or "adaptive" for full browser-based requests. Passing an invalid value will cause the BBRE API to return an INVALID_MODE error when the next request is submitted.

Return Type

This method does not return a value. It modifies the internal defaultMode property on the client instance directly.

Mode Comparison

The following table summarizes the key differences between passive and adaptive modes to help you decide which mode to set as your default.

Characteristic Passive Mode Adaptive Mode
Browser instance No browser launched Full browser environment
JavaScript execution Not supported Full JavaScript support
Browser actions (click, fill, navigate) Not available Fully supported
Speed Faster (no browser overhead) Slower (browser startup and rendering)
Cost per request Lower Higher
Anti-detection level Basic HTTP-level Full browser-level fingerprinting
Best for APIs, static pages, simple data retrieval SPAs, JavaScript-heavy sites, form interactions

Switching from Passive to Adaptive Mid-Workflow

A common pattern is to start with passive mode for initial page discovery and data gathering, then switch to adaptive mode when you encounter pages that require full browser rendering. This approach minimizes cost by using the cheaper passive mode wherever possible.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  mode: "passive"
});

async function smartScrape(url) {
  const passiveResponse = await client.get(url);

  if (passiveResponse.body && passiveResponse.body.includes("Enable JavaScript")) {
    console.log("Page requires JavaScript, switching to adaptive mode");
    client.setDefaultMode("adaptive");

    const adaptiveResponse = await client.get(url);
    console.log("Adaptive response status:", adaptiveResponse.statusCode);
    return adaptiveResponse;
  }

  console.log("Passive mode worked, status:", passiveResponse.statusCode);
  return passiveResponse;
}

smartScrape("https://example.com/dynamic-page");

Environment-Based Mode Configuration

In development and testing environments, you might want to use passive mode for speed, while production environments use adaptive mode for reliability against bot detection. Set the default mode based on an environment variable after constructing the client.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: process.env.BBRE_API_KEY });

const environment = process.env.NODE_ENV || "development";

if (environment === "production") {
  client.setDefaultMode("adaptive");
  console.log("Production mode: using adaptive for maximum reliability");
} else {
  client.setDefaultMode("passive");
  console.log("Development mode: using passive for faster iteration");
}

const response = await client.get("https://example.com/data");
console.log("Response status:", response.statusCode);

Per-Request Mode Override

Even with a default mode set, you can override it for individual requests by passing a mode parameter in the request options. This is useful when most of your requests work fine in passive mode but a few specific URLs require adaptive mode.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  mode: "passive"
});

const apiResponse = await client.get("https://api.example.com/data");
console.log("API call used passive mode (default)");

const spaResponse = await client.request({
  url: "https://spa.example.com/dashboard",
  mode: "adaptive"
});
console.log("SPA page used adaptive mode (per-request override)");

const anotherApiResponse = await client.get("https://api.example.com/users");
console.log("Another API call used passive mode (default still active)");

client.setDefaultSensibility(sensibility)

The setDefaultSensibility() method sets the default sensibility level that the client uses for all subsequent requests and sessions. Sensibility controls how carefully the BBRE engine processes your request in terms of anti-detection measures, timing patterns, and behavioral simulation. The three available levels are "low", "medium", and "high". Low sensibility processes requests quickly with minimal anti-detection overhead, making it suitable for targets with no or basic bot protection. Medium sensibility applies a balanced set of anti-detection measures and is the default level set by the constructor. High sensibility applies the most thorough anti-detection measures including realistic timing delays, human-like behavioral patterns, and advanced fingerprint consistency checks, making it suitable for heavily protected websites but at the cost of slower request processing. When you call request() or createSession() without specifying a sensibility parameter, the client uses this default sensibility level. Calling setDefaultSensibility() lets you adjust the protection level dynamically based on the target website's detection capabilities.

Method Signature

JavaScript
client.setDefaultSensibility(sensibility);

Parameters

Parameter Type Required Description
sensibility string Required The sensibility level to use as the default. Must be one of "low", "medium", or "high". Passing an invalid value will cause the BBRE API to return an INVALID_SENSIBILITY error when the next request is submitted.

Return Type

This method does not return a value. It modifies the internal defaultSensibility property on the client instance directly.

Sensibility Level Comparison

The following table summarizes the trade-offs between the three sensibility levels to help you choose the right default for your use case.

Characteristic Low Medium High
Processing speed Fastest Moderate Slowest
Anti-detection measures Minimal Balanced Maximum
Timing simulation No artificial delays Basic timing patterns Human-like timing with randomization
Fingerprint consistency Basic Standard checks Advanced consistency validation
Cost per request Lowest Standard Highest
Best for Friendly APIs, internal tools, testing Most websites, general scraping Heavily protected sites, Cloudflare, Akamai

Adjusting Sensibility Based on Target

Different target websites have different levels of bot protection. You can adjust the default sensibility based on which website you are targeting to optimize the balance between speed and detection avoidance.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

const siteSensibility = {
  "api.example.com": "low",
  "shop.example.com": "medium",
  "bank.example.com": "high"
};

async function scrapeMultipleSites(siteUrls) {
  const results = {};

  for (const [site, urls] of Object.entries(siteUrls)) {
    const level = siteSensibility[site] || "medium";
    client.setDefaultSensibility(level);
    console.log("Set sensibility to", level, "for", site);

    results[site] = [];

    for (const url of urls) {
      const response = await client.get(url);
      results[site].push({ url, status: response.statusCode });
    }
  }

  return results;
}

const siteUrls = {
  "api.example.com": ["https://api.example.com/v1/products", "https://api.example.com/v1/categories"],
  "shop.example.com": ["https://shop.example.com/deals", "https://shop.example.com/new-arrivals"],
  "bank.example.com": ["https://bank.example.com/rates", "https://bank.example.com/exchange"]
};

scrapeMultipleSites(siteUrls);

Escalating Sensibility on Failure

A practical pattern is to start with low sensibility for speed and automatically escalate to higher levels when requests fail due to bot detection. This approach gives you the best performance for easy targets while still handling protected sites.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

async function requestWithEscalation(url) {
  const levels = ["low", "medium", "high"];

  for (const level of levels) {
    client.setDefaultSensibility(level);
    console.log("Trying with sensibility:", level);

    try {
      const response = await client.get(url);

      if (response.statusCode === 200) {
        console.log("Success with sensibility:", level);
        return response;
      }

      if (response.statusCode === 403 || response.statusCode === 429) {
        console.log("Blocked with sensibility:", level, "- escalating");
        continue;
      }

      return response;
    } catch (error) {
      console.log("Error with sensibility:", level, "-", error.message);
      continue;
    }
  }

  console.log("All sensibility levels failed for:", url);
  return null;
}

requestWithEscalation("https://protected-site.example.com/data");

Per-Request Sensibility Override

Like all default settings, the sensibility can be overridden on a per-request basis. This is useful when most of your requests target sites with moderate protection but a few specific endpoints require higher sensibility.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  sensibility: "medium"
});

const regularPage = await client.get("https://example.com/products");
console.log("Regular page: used medium sensibility (default)");

const protectedPage = await client.request({
  url: "https://example.com/checkout",
  sensibility: "high"
});
console.log("Checkout page: used high sensibility (per-request override)");

const apiEndpoint = await client.request({
  url: "https://api.example.com/v1/data",
  sensibility: "low"
});
console.log("API endpoint: used low sensibility (per-request override)");

Combining Multiple Defaults

In real-world applications, you rarely change just one default at a time. The four setter methods work together to define a complete request profile. You can call them in any order, and each one independently modifies its corresponding default value. The following examples demonstrate common patterns for configuring multiple defaults together.

Full Configuration After Construction

Sometimes you want to create the client with just the API key and configure all defaults separately, perhaps because the configuration values come from different sources (environment variables, config files, database records).

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: process.env.BBRE_API_KEY });

client.setDefaultMode(process.env.BBRE_MODE || "passive");
client.setDefaultSensibility(process.env.BBRE_SENSIBILITY || "medium");
client.setDefaultProxy(process.env.BBRE_PROXY || null);
client.setDefaultFingerprint({
  userAgent: process.env.BBRE_USER_AGENT || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
  platform: process.env.BBRE_PLATFORM || "Win32",
  language: process.env.BBRE_LANGUAGE || "en-US"
});

console.log("Client configured from environment variables");

const response = await client.get("https://example.com/data");
console.log("Response status:", response.statusCode);

Target-Specific Configuration Profiles

When your application scrapes multiple websites with different protection levels, you can define configuration profiles for each target and apply them by calling all four setter methods together before processing each target's URLs.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

const targetProfiles = {
  "simple-api": {
    mode: "passive",
    sensibility: "low",
    proxy: null,
    fingerprint: {}
  },
  "ecommerce-site": {
    mode: "passive",
    sensibility: "medium",
    proxy: "http://user:[email protected]:8080",
    fingerprint: {
      userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      platform: "Win32",
      language: "en-US"
    }
  },
  "protected-portal": {
    mode: "adaptive",
    sensibility: "high",
    proxy: "http://user:[email protected]:8080",
    fingerprint: {
      userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      platform: "MacIntel",
      language: "en-US",
      timezone: "America/New_York",
      screen: { width: 1920, height: 1080 }
    }
  }
};

function applyProfile(profileName) {
  const profile = targetProfiles[profileName];
  if (!profile) {
    console.log("Unknown profile:", profileName);
    return;
  }

  client.setDefaultMode(profile.mode);
  client.setDefaultSensibility(profile.sensibility);
  client.setDefaultProxy(profile.proxy);
  client.setDefaultFingerprint(profile.fingerprint);

  console.log("Applied profile:", profileName);
}

async function scrapeWithProfiles(tasks) {
  const results = [];

  for (const task of tasks) {
    applyProfile(task.profile);

    for (const url of task.urls) {
      const response = await client.get(url);
      results.push({ profile: task.profile, url, status: response.statusCode });
    }
  }

  return results;
}

const tasks = [
  {
    profile: "simple-api",
    urls: ["https://api.example.com/v1/products", "https://api.example.com/v1/categories"]
  },
  {
    profile: "ecommerce-site",
    urls: ["https://shop.example.com/deals", "https://shop.example.com/bestsellers"]
  },
  {
    profile: "protected-portal",
    urls: ["https://portal.example.com/dashboard"]
  }
];

scrapeWithProfiles(tasks);

Defaults with Session Creation

The default values also apply when creating sessions with createSession(). The session inherits the default mode, sensibility, fingerprint, and proxy from the client unless you override them in the session options. This means you can configure the client once and create multiple sessions that all share the same configuration.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultMode("adaptive");
client.setDefaultSensibility("high");
client.setDefaultProxy("http://user:[email protected]:8080");
client.setDefaultFingerprint({
  userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
  platform: "Win32",
  language: "en-US"
});

const session1 = await client.createSession();
console.log("Session 1 created with all defaults (adaptive, high, proxy, fingerprint)");

const session2 = await client.createSession({
  mode: "passive",
  sensibility: "low"
});
console.log("Session 2 created with mode/sensibility overrides but default proxy/fingerprint");

await client.closeSession(session1.sessionId);
await client.closeSession(session2.sessionId);

Dynamic Reconfiguration During Long-Running Tasks

For long-running scraping tasks that process thousands of URLs, you might need to reconfigure the client mid-execution based on observed results. For example, if you notice an increase in blocked requests, you can escalate the sensibility and switch to a different proxy without stopping the process.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({
  apiKey: "YOUR_API_KEY",
  mode: "passive",
  sensibility: "low"
});

async function adaptiveScrape(urls) {
  let successCount = 0;
  let failCount = 0;
  const results = [];

  for (let i = 0; i < urls.length; i++) {
    const response = await client.get(urls[i]);
    results.push({ url: urls[i], status: response.statusCode });

    if (response.statusCode === 200) {
      successCount++;
    } else {
      failCount++;
    }

    if ((i + 1) % 50 === 0) {
      const recentResults = results.slice(-50);
      const recentFailRate = recentResults.filter(r => r.status !== 200).length / 50;

      if (recentFailRate > 0.3) {
        console.log("High failure rate detected:", (recentFailRate * 100).toFixed(1) + "%");
        console.log("Escalating configuration...");

        client.setDefaultSensibility("high");
        client.setDefaultMode("adaptive");
        client.setDefaultProxy("http://user:[email protected]:8080");

        console.log("Switched to adaptive mode with high sensibility and premium proxy");
      }
    }
  }

  console.log("Completed:", urls.length, "URLs");
  console.log("Success:", successCount, "Failed:", failCount);
  return results;
}

Constructor Config vs. Setter Methods

You can set default values in two ways: through the constructor config object when creating the client, or by calling the setter methods after construction. Both approaches produce the same result. The constructor approach is convenient when you know all your configuration values upfront. The setter approach is useful when configuration values are loaded asynchronously, come from different sources, or need to change during the application's lifecycle. You can also combine both approaches by setting initial values in the constructor and adjusting them later with the setters.

Equivalent Configuration Examples

The following two code blocks produce identical client configurations. The first uses the constructor config object, and the second uses the setter methods.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const clientA = new BBREClient({
  apiKey: "YOUR_API_KEY",
  mode: "adaptive",
  sensibility: "high",
  proxy: "http://user:[email protected]:8080",
  fingerprint: {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    platform: "Win32"
  }
});
JavaScript
const BBREClient = require("mydisctsolver-bbre");

const clientB = new BBREClient({ apiKey: "YOUR_API_KEY" });

clientB.setDefaultMode("adaptive");
clientB.setDefaultSensibility("high");
clientB.setDefaultProxy("http://user:[email protected]:8080");
clientB.setDefaultFingerprint({
  userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
  platform: "Win32"
});

Async Configuration Loading

When your configuration values come from an asynchronous source such as a database, remote config service, or encrypted vault, you need to create the client first and then apply the configuration after the async operation completes. The setter methods are designed for exactly this scenario.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

async function loadAndApplyConfig() {
  const config = await fetchConfigFromDatabase();

  if (config.mode) {
    client.setDefaultMode(config.mode);
  }

  if (config.sensibility) {
    client.setDefaultSensibility(config.sensibility);
  }

  if (config.proxy) {
    client.setDefaultProxy(config.proxy);
  }

  if (config.fingerprint) {
    client.setDefaultFingerprint(config.fingerprint);
  }

  console.log("Configuration loaded and applied");
}

async function fetchConfigFromDatabase() {
  return {
    mode: "adaptive",
    sensibility: "high",
    proxy: "http://user:[email protected]:8080",
    fingerprint: {
      userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
      platform: "Win32"
    }
  };
}

loadAndApplyConfig();

Override Priority Chain

When the client processes a request, it resolves each configuration value through a priority chain. Understanding this chain is essential for predicting which value will be used in any given scenario. The priority order from highest to lowest is: per-request parameter, then client default (set via constructor or setter method), then built-in fallback. The following table shows the complete resolution chain for each configuration value.

Configuration Priority 1 (Highest) Priority 2 Priority 3 (Lowest)
Mode Per-request mode parameter this.defaultMode (setter or constructor) "passive" (built-in fallback)
Sensibility Per-request sensibility parameter this.defaultSensibility (setter or constructor) "medium" (built-in fallback)
Proxy Per-request proxy parameter (including null) this.defaultProxy (setter or constructor) null (no proxy)
Fingerprint Merged: { ...defaultFingerprint, ...requestFingerprint } this.defaultFingerprint (setter or constructor) {} (engine auto-generates)
Fingerprint Uses Merge, Not Override

Notice that fingerprint behaves differently from the other three configuration values. Mode, sensibility, and proxy use a simple override pattern where the per-request value completely replaces the default. Fingerprint uses a merge pattern where the default fingerprint and per-request fingerprint are combined using the spread operator. This means per-request fingerprint properties override matching default properties, but non-overlapping default properties are preserved. If you want to completely replace the default fingerprint for a specific request, you need to include all desired properties in the per-request fingerprint object.

Priority Chain in Action

The following example demonstrates how the priority chain resolves each configuration value across three different requests using the same client.

JavaScript
const BBREClient = require("mydisctsolver-bbre");

const client = new BBREClient({ apiKey: "YOUR_API_KEY" });

client.setDefaultMode("passive");
client.setDefaultSensibility("medium");
client.setDefaultProxy("http://default-proxy:8080");
client.setDefaultFingerprint({ userAgent: "DefaultUA", platform: "Win32" });

const response1 = await client.get("https://example.com/page1");
console.log("Request 1 uses: passive, medium, default-proxy, {userAgent: DefaultUA, platform: Win32}");

const response2 = await client.request({
  url: "https://example.com/page2",
  mode: "adaptive",
  sensibility: "high"
});
console.log("Request 2 uses: adaptive (override), high (override), default-proxy, {userAgent: DefaultUA, platform: Win32}");

const response3 = await client.request({
  url: "https://example.com/page3",
  proxy: null,
  fingerprint: { userAgent: "CustomUA", timezone: "Europe/Berlin" }
});
console.log("Request 3 uses: passive, medium, no proxy (null override), {userAgent: CustomUA, platform: Win32, timezone: Europe/Berlin}");

Best Practices

Set Defaults in the Constructor When Values Are Known Upfront

If you know your configuration values at the time of client creation, pass them through the constructor config object instead of calling setter methods separately. This makes your code more readable and ensures the client is fully configured from the first request. Reserve the setter methods for scenarios where configuration values are loaded asynchronously, change during runtime, or come from different sources that are not all available at construction time.

Use Per-Request Overrides for One-Off Exceptions

When most of your requests share the same configuration but a few need different settings, set the common configuration as defaults and use per-request overrides for the exceptions. Avoid calling setter methods back and forth between individual requests, as this makes the code harder to follow and introduces the risk of forgetting to reset the default after a special request. The per-request override pattern is cleaner because it does not modify the client state.

Start with Low Sensibility and Escalate as Needed

Set the default sensibility to "low" or "medium" and only escalate to "high" when you encounter bot detection issues. Higher sensibility levels add processing overhead and increase request latency. Many websites work perfectly fine with low sensibility, so starting high wastes resources. Implement an escalation pattern that automatically increases sensibility when requests fail with 403 or 429 status codes, and you will get the best balance between speed and reliability.

Rotate Fingerprints and Proxies Together

When rotating browser identities to avoid detection, always change the fingerprint and proxy together. A consistent fingerprint coming from different IP addresses, or different fingerprints coming from the same IP address, can trigger bot detection systems that correlate these signals. Create complete profiles that pair a specific fingerprint with a specific proxy and rotate the entire profile as a unit using the setter methods.

Use Passive Mode as Default, Adaptive Mode for Specific Targets

Keep "passive" as your default mode and switch to "adaptive" only for targets that require full browser rendering. Passive mode is significantly faster and cheaper than adaptive mode. If you set adaptive as the default, every request pays the browser startup cost even when it is not needed. Use per-request overrides or change the default temporarily when processing a batch of URLs that require adaptive mode, then switch back to passive when done.

Be Aware of Shared State in Concurrent Applications

The setter methods modify the client instance directly, and the changes affect all subsequent requests made through that instance. In applications that process multiple tasks concurrently (for example, using Promise.all()), calling a setter method in one task can unexpectedly affect requests in another task that shares the same client instance. To avoid this, either use per-request overrides instead of setter methods in concurrent code, or create separate client instances for each concurrent task.

Common Issues

Default Proxy Not Being Used When proxy Is Explicitly Set to null

Symptom: You set a default proxy with setDefaultProxy(), but some requests are not using it even though you did not intend to override it.
Cause: The proxy resolution logic distinguishes between undefined (not provided) and null (explicitly no proxy). If you pass proxy: null in your request options, even accidentally, the default proxy is bypassed. This can happen when you destructure an options object that has a proxy property set to null.
Solution: Only include the proxy property in your request options when you intentionally want to override the default. If you are building request options dynamically, omit the proxy key entirely (do not set it to null) when you want the default to apply. Use delete options.proxy or conditional object spreading to exclude the property.

setDefaultFingerprint() Does Not Affect Already-Running Requests

Symptom: You call setDefaultFingerprint() but requests that were initiated before the call still use the old fingerprint.
Cause: The fingerprint merge happens at the moment request() or createSession() is called, not when the BBRE engine processes the request. If you fire multiple async requests and then change the default fingerprint, requests that were already initiated have already captured the old default value.
Solution: Call setDefaultFingerprint() before initiating the requests that should use the new fingerprint. If you need to change fingerprints between requests in a concurrent batch, use per-request fingerprint overrides instead of changing the default.

INVALID_MODE Error After Calling setDefaultMode() with a Typo

Symptom: After calling setDefaultMode(), all subsequent requests fail with an INVALID_MODE error from the BBRE API.
Cause: The setDefaultMode() method does not validate the mode value locally. It simply stores whatever string you pass. The validation happens on the server side when the request is submitted. If you pass a typo like "pasive" or "Adaptive" (case-sensitive), the error only appears when the next request is made.
Solution: Always use lowercase strings: "passive" or "adaptive". Consider defining constants in your application to avoid typos: const MODES = { PASSIVE: "passive", ADAPTIVE: "adaptive" };. The same applies to setDefaultSensibility() with values "low", "medium", and "high".

Fingerprint Properties Not Being Overridden as Expected

Symptom: You pass a per-request fingerprint to override specific properties, but the default fingerprint properties still appear in the merged result.
Cause: The spread operator merge ({ ...defaultFingerprint, ...requestFingerprint }) only performs a shallow merge. If your default fingerprint has a nested object like screen: { width: 1920, height: 1080 } and your per-request fingerprint has screen: { width: 1366 }, the entire screen object is replaced, not deep-merged. The resulting fingerprint will have screen: { width: 1366 } without the height property.
Solution: When overriding nested fingerprint properties, include all the nested properties you want to keep in the per-request fingerprint object. For example: fingerprint: { screen: { width: 1366, height: 768 } }. The shallow merge behavior is by design and matches standard JavaScript spread operator semantics.

Concurrent Requests Getting Wrong Defaults Due to Shared Client State

Symptom: In a concurrent workflow using Promise.all(), some requests use unexpected default values because another concurrent task changed the defaults mid-execution.
Cause: The setter methods modify the client instance state immediately, and all concurrent requests share the same client instance. If Task A calls setDefaultMode("adaptive") while Task B's requests are being initiated, Task B's requests may unexpectedly use adaptive mode.
Solution: Avoid calling setter methods in concurrent code paths. Instead, use per-request overrides to specify configuration for each request independently. Alternatively, create separate BBREClient instances for each concurrent task so they do not share state.