# Evomi Documentation > Documentation for Evomi's residential, mobile, datacenter and static residential proxies, the Scraper API, the Scraping Browser and the Public API. Source: https://docs.evomi.com/ --- ## Proxy Instructions Source: https://docs.evomi.com/proxy-instructions/ Evomi provides different types of proxies to meet your diverse needs. Each proxy type offers unique advantages depending on your use case. Every proxy request has a minimum billed traffic consumption of 512 bytes. If you send many high-frequency requests that only consume a few bytes each, each request still deducts at least 512 bytes from your balance. 512 bytes is 0.000000512 GB, so the floor is that fraction of whichever per-GB rate you are on: about $0.00000015 per request on datacenter, about $0.00000025 on Core Residential, about $0.000002 on mobile. ## Getting Started 1. Choose your proxy type based on your needs 2. Generate your proxy credentials from the dashboard 3. Configure your application or tool Check the sidebar for detailed documentation on each proxy type and integration guides. --- ## Datacenter Proxies Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/ Evomi offers high-performance datacenter proxies with blazing fast speeds and robust reliability. Our proxies feature: - Average response times of 0.45 seconds - 99.95% connection success rate - Support for HTTP, HTTPS, and SOCKS5 protocols - Automatic IP rotation and sticky session options - Scalability to handle thousands of requests per second Every datacenter proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At $0.30 per GB on the 1,000 GB plan, that minimum is about $0.0000001536 per request, or $0.0000002304 at the $0.45 pay-as-you-go rate. Ideal for search engine monitoring, AI training, data extraction, and more, Evomi datacenter proxies provide enterprise-grade performance at competitive prices. --- ## Datacenter Proxies Geo Targetting Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/geo-targetting/ Geo-targeting allows you to specify the geographic location of the proxy you want to use. This can be essential for tasks like localized testing, content access, and more. Below, we'll explain the key geo-targeting parameters and show you how to use them effectively. ## Geo-targeting Parameters - **_country-**: Specify the country of the proxy. - **_continent-**: Specify the continent of the proxy. ## Choosing the Geo Source The parameters above are resolved by a geolocation database, and you can choose which one. By default Evomi uses `ipapi` ([ip-api.com](https://ip-api.com)). Adding `_geosource-` selects a different source, which works for Datacenter proxies too: ```bash curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-NL_geosource-maxmind https://ip.evomi.com/s ``` Sources disagree about individual IPs, so a filter that matches plenty of them on one source may match fewer — or none — on another. See [Geo Source Selection](/proxy-instructions/residential-proxies/expert-settings/geosource/) for the available sources and their matching behaviour. --- ## Datacenter Proxies Continent Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/geo-targetting/continent/ You can target a specific continent by using the `continent` parameter in the proxy URL. The available continents are `africa`, `asia`, `europe`, `north.america`, `oceania`, and `south.america`. ```bash curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_continent-asia https://ip.evomi.com/s curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_continent-europe https://ip.evomi.com/s curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_continent-africa https://ip.evomi.com/s ``` Note that you can find all available continents in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Datacenter Proxies Country Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/geo-targetting/country/ You can target a specific country by using the `country` parameter in your proxy string. This parameter allows you to connect to a proxy located in a specific country. You can specify any country in the ISO format (e.g., `US` for the United States, `CA` for Canada, etc.). For some countries the full name is supported aswell (e.g., `UnitedStates` for the United States, `Canada` for Canada, etc.). ```bash curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-US https://ip.evomi.com/s ``` Note that you can find all available countries in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Datacenter Proxies How to Connect Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/how-to-connect/ This page is a basic guide with information about Datacenter Proxies and how to connect to them. ## Endpoint and Port Information To connect to the evomi proxy system with Datacenter proxies, you need to use the following endpoint and port configurations: | Proxy Protocol | Datacenter Endpoint | Datacenter Port | |--------------|---------------|--------| | HTTP Proxy | dcp.evomi.com | 2000 | | HTTPs Proxy | dcp.evomi-proxy.com | 2001 | | SOCKS5 Proxy | dcp.evomi.com | 2002 | The HTTPs endpoint uses the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject `dcp.evomi.com` on port 2001. The difference between the HTTP and HTTPs proxies is that the HTTPs proxy is encrypted, while the HTTP proxy is not. The SOCKS5 proxy is a more advanced protocol that can handle more types of traffic. The HTTP protocol works for HTTPs requests, you do not need to use the HTTPs protocol for HTTPs requests. The HTTPs protocol is only necessary if you need to connect to a secure HTTPs server and is generally slower than the HTTP protocol. Please note that depending on your protocol, you will need to use the correct port number. Every datacenter proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At $0.30 per GB on the 1,000 GB plan, that minimum is about $0.0000001536 per request, or $0.0000002304 at the $0.45 pay-as-you-go rate. ## Basic Connection Instructions Here are step-by-step instructions to help you connect using different programming languages and tools. ### CURL To connect using CURL, use the following command format: ```bash curl -x http://dcp.evomi.com:2000 -U testuser:testpassword https://ip.evomi.com/s ``` ### Python To connect using Python with the `requests` library, follow these steps: 1. Install the `requests` library if you haven't already: ```bash pip install requests ``` 2. Use the following script: ```python import requests proxy_pass = "testpassword" proxy_url = f"http://testuser:{proxy_pass}@dcp.evomi.com:2000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` ### JavaScript To connect using JavaScript, particularly with Node.js, follow these steps: 1. Install the `node-fetch` and `http-proxy-agent` packages if you haven't already: ```bash npm install node-fetch http-proxy-agent ``` 2. Use the following script: ```javascript const fetch = require('node-fetch'); const HttpsProxyAgent = require('https-proxy-agent'); const proxyPass = 'testpassword'; const proxyUrl = `http://testuser:${proxyPass}@dcp.evomi.com:2000`; const agent = new HttpsProxyAgent(proxyUrl); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log(body)) .catch(err => console.error('Error:', err)); ``` Note that you can get an easy to use interface to configure this for you, using our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Datacenter Proxies Proxy Sessions Source: https://docs.evomi.com/proxy-instructions/datacenter-proxies/proxy-sessions/ By default, each request to our proxy server generates a new IP address. However, you can use session parameters to maintain the same IP address for multiple requests. This feature is useful for tasks that require a stable connection, such as web resources with session-based authentication. ![Sticky Session Demo](/images/session-demo.gif) ## Session Parameters We offer three types of session parameters: `session`, `hardsession`, and `lockedsession`. All ensure you keep the same IP for all following requests, but they have different behaviors and use cases. ### The `session` parameter - A unique alphanumeric string, 6 to 10 characters long. - Optimized for the highest overall success rate. - May change IP address in favor of stability. - Works with the `lifetime` parameter. ### The `hardsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Prioritizes IP address longevity. - Keeps the same IP for as long as possible. - Does not work with the `lifetime` parameter. ### The `lockedsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Locks traffic to one specific IP. - Never switches to a replacement IP if that peer disconnects. - If the bound IP goes offline, the request fails instead of assigning a new IP. - Does not work with the `lifetime` parameter. When specifying any geo or ISP targeting parameters, the first request will use the specified parameters, and all subsequent requests will use the same IP address. If you change the targeting while maintaining the same session, the IP address will remain the same. ### The `lifetime` parameter - Specifies the duration for the `session` in minutes. - If not specified, the session will last for 30 minutes by default. - Maximum value is 1440 minutes, which is 24 hours. - After the specified time, the IP address will change. - Does not apply to `hardsession` or `lockedsession`. If you specify a higher `lifetime` value than the maximum allowed, the request will return a 412 error code. ## Examples Below are examples of using Sticky sessions in different programming languages: CURL, Python, and JavaScript. #### CURL ```bash curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-US_session-sgn34f3e_lifetime-10 https://ip.evomi.com/s curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-US_hardsession-JS9nsq2n https://ip.evomi.com/s curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-US_lockedsession-k8m2pQ1x https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_session-sgn34f3e_lifetime-10" proxy_url = f"http://testuser:{proxy_pass}@dcp.evomi.com:2000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_hardsession-JS9nsq2n" proxy_url = f"http://testuser:{proxy_pass}@dcp.evomi.com:2000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_lockedsession-k8m2pQ1x" proxy_url = f"http://testuser:{proxy_pass}@dcp.evomi.com:2000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_country-US_session-sgn34f3e_lifetime-10'; const proxyUrl = `http://testuser:${proxyPass}@dcp.evomi.com:2000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass2 = 'testpassword_country-US_hardsession-JS9nsq2n'; const proxyUrl2 = `http://testuser:${proxyPass2}@dcp.evomi.com:2000`; fetch(url, { proxy: proxyUrl2 }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass3 = 'testpassword_country-US_lockedsession-k8m2pQ1x'; const proxyUrl3 = `http://testuser:${proxyPass3}@dcp.evomi.com:2000`; fetch(url, { proxy: proxyUrl3 }) .then(res => res.text()) .then(body => console.log(body)); ``` Note our easy to use Proxy Generator can generate all this for you. --- ## Integration Guides Source: https://docs.evomi.com/proxy-instructions/integration-guides/ These guides will help you integrate Evomi proxies with your favorite tools and frameworks. Choose the integration that matches your use case. ## Quick Start 1. Choose your tool or platform from the list 2. Get your proxy credentials from the dashboard 3. Follow the step-by-step guide for your specific tool 4. Test your connection All guides include code examples, configuration options, and troubleshooting tips. --- ## Android Source: https://docs.evomi.com/proxy-instructions/integration-guides/android/ This guide covers how to set up and use Evomi proxies on Android devices. Since Evomi proxies require username/password authentication and Android's built-in proxy settings do not support this, a dedicated proxy client app is required. ## Prerequisites - An active Evomi proxy subscription - Your Evomi proxy credentials (username and password) - An Android device running Android 5.0 or later ## Method 1: Using Postern (Recommended) [Postern](https://play.google.com/store/apps/details?id=com.puxin.postern) is a proxy client available on the Google Play Store that supports HTTP, HTTPS, SOCKS5, and Shadowsocks. It routes traffic system-wide through a local VPN service and allows per-app routing rules. ### Setup Steps 1. Install **Postern** from the Google Play Store. 2. Open Postern and tap **Add Proxy Server**. 3. Configure the proxy: - **Proxy Name**: Enter a label (e.g., `Evomi Residential`) - **Proxy Type**: Select `SOCKS5` (or `HTTP/HTTPS` depending on your needs) - **Host**: `rp.evomi.com` - **Port**: `1002` for SOCKS5 (or `1000` for HTTP) - **Username**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 4. Tap **Save** to store the profile. 5. Back on the main screen, tap **Add Rule** to create a routing rule: - **Rule Name**: e.g. `Route All` - **Match Pattern**: Select **All** to route all traffic - **Action**: Select your Evomi proxy 6. Enable the VPN connection by tapping the **Start** button. Accept the Android VPN permission prompt. 7. Verify your connection by opening a browser and navigating to `https://ip.evomi.com/s`. The displayed IP should be the proxy IP, not your real one. Postern lets you create multiple proxy profiles and rules. You can route specific apps through the proxy while others use a direct connection. ## Method 2: Using Drony [Drony](https://play.google.com/store/apps/details?id=org.sandroproxy.drony) is a free proxy client that supports HTTP, HTTPS, SOCKS4, and SOCKS5 with per-app filtering. It also uses Android's VPN interface to route traffic without requiring root. ### Setup Steps 1. Install **Drony** from the Google Play Store. 2. Open Drony. You will see the **Log** tab by default. 3. Swipe to the **Settings** tab. 4. Tap **Wi-Fi** (or the name of your current network). 5. Under your network name, configure: - **Proxy type**: Select `Manual` - **Hostname**: `rp.evomi.com` - **Port**: `1000` (HTTP) or `1002` (SOCKS5) - **Proxy type**: `HTTP` or `SOCKS5` to match the port - **Username**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 6. Use the **Filter** tab to control which apps route through the proxy. - Default value: Select the default action for apps (e.g., **Direct** for no proxy, or **Local proxy chain** to route through the proxy) - Tap individual apps to override the default behavior 7. Go back to the **Log** tab and tap the **ON/OFF** toggle at the bottom to activate. Accept the VPN permission prompt. 8. Verify by visiting `https://ip.evomi.com/s` in your browser. ## Why Not the Built-In Wi-Fi Proxy? Android has a manual Wi-Fi proxy setting under **Settings** > **Network & internet** > **Wi-Fi**, but it has critical limitations that make it unsuitable for Evomi proxies: - **No username/password authentication** for HTTP proxies in most Android versions. Since Evomi proxies always require authentication, the connection will fail silently in most apps. - Settings only apply to the specific Wi-Fi network, not mobile data. - SOCKS5 support is inconsistent across manufacturers. The built-in Wi-Fi proxy setting does **not** work reliably with Evomi proxies because it cannot handle authentication. Use a dedicated proxy app (Postern or Drony) instead. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Verifying Your Connection After enabling the proxy, open a browser and navigate to: ``` https://ip.evomi.com/s ``` The displayed IP address should be the proxy server's IP, not your device's real IP. If you see your own IP, the proxy is not active for that app. ## Tips and Troubleshooting - **Credentials**: Double-check hostname, port, username, and the full password string (including `_session-` and `_mode-` parameters). - **App-specific proxy settings**: Some apps (e.g., Firefox) have their own internal proxy settings that may override the VPN-based proxy. Check the app's network settings. - **Force-stop apps**: If an app does not pick up the new proxy settings, force-stop it from Android Settings and relaunch. - **Battery usage**: VPN-based proxy apps may increase battery consumption. Disable the proxy when not in use. - **SOCKS5 recommended**: For system-wide proxying through apps like Postern or Drony, SOCKS5 is generally more reliable than HTTP since it handles all traffic types. - **Toggle Airplane mode**: If you experience connectivity issues after changing proxy settings, toggling Airplane mode on and off can help reset the network stack. --- ## Beautiful Soup Source: https://docs.evomi.com/proxy-instructions/integration-guides/beautiful-soup/ This guide explains how to use Evomi proxies with Beautiful Soup and the `requests` library for web scraping in Python. Beautiful Soup handles HTML parsing while `requests` manages the HTTP connections through the proxy. ## Prerequisites - Python 3.8+ installed - Your Evomi proxy credentials (username and password) ## Installation ```bash pip install beautifulsoup4 requests ``` For SOCKS5 proxy support, also install the SOCKS extra: ```bash pip install "requests[socks]" ``` ## Configuration ```python import requests from bs4 import BeautifulSoup proxy_host = "rp.evomi.com" proxy_port = "1000" proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies, timeout=15) response.raise_for_status() soup = BeautifulSoup(response.content, "html.parser") print("Proxy IP:", soup.get_text().strip()) ``` Replace `your_username` and `your_password` with your actual Evomi credentials (keep the `_session-anychars_mode-speed` parameters). ## Explanation 1. **Proxy URL**: The format `http://user:pass@host:port` embeds authentication directly in the URL. The `requests` library extracts the credentials and sends them as a `Proxy-Authorization` header. 2. **`proxies` dictionary**: Maps both `http` and `https` traffic through the same proxy. Even though the proxy URL uses `http://`, it correctly tunnels HTTPS traffic via the HTTP `CONNECT` method. 3. **`timeout`**: Always set a timeout to prevent the script from hanging if the proxy is unreachable. 4. **`raise_for_status()`**: Raises an exception for 4xx/5xx status codes, making error handling cleaner. ## Scraping HTML Elements A more realistic example that fetches and parses actual page content: ```python import requests from bs4 import BeautifulSoup proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@rp.evomi.com:1000" proxies = {"http": proxy_url, "https": proxy_url} headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" } response = requests.get( "https://example.com", proxies=proxies, headers=headers, timeout=15, ) response.raise_for_status() soup = BeautifulSoup(response.content, "html.parser") for heading in soup.find_all("h1"): print(heading.get_text(strip=True)) ``` Always set a realistic `User-Agent` header. Many websites block requests that use Python's default User-Agent string. ## Using SOCKS5 After installing `requests[socks]`, change the proxy URL scheme to `socks5h://`: ```python proxy_url = f"socks5h://{proxy_user}:{proxy_pass}@rp.evomi.com:1002" proxies = {"http": proxy_url, "https": proxy_url} ``` The `socks5h://` scheme routes DNS resolution through the proxy, which prevents DNS leaks. Use `socks5://` if you want local DNS resolution instead. ## Using Sessions For multiple requests, use a `requests.Session` to reuse the underlying TCP connection: ```python session = requests.Session() session.proxies.update({"http": proxy_url, "https": proxy_url}) session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }) response = session.get("https://example.com", timeout=15) soup = BeautifulSoup(response.content, "html.parser") ``` ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Credentials**: Double-check your username, password, host, and port. The password format is `your_password_session-anychars_mode-speed`. - **Special characters in credentials**: If your password contains special characters (`@`, `:`, etc.), URL-encode them using `urllib.parse.quote()`. - **SSL errors**: If you encounter SSL certificate errors from the target site (not the proxy), ensure your system's CA certificates are up to date. Avoid `verify=False` in production. - **Error handling**: Wrap requests in `try/except` blocks to catch `requests.exceptions.ProxyError`, `requests.exceptions.Timeout`, and `requests.exceptions.HTTPError`. - **Rate limiting**: Be respectful of target websites. Add delays between requests with `time.sleep()` and respect `robots.txt`. - **Dynamic content**: Beautiful Soup and `requests` only fetch the initial HTML. They do not execute JavaScript. For JS-heavy sites, use Selenium or Playwright instead. --- ## Chrome and Firefox Source: https://docs.evomi.com/proxy-instructions/integration-guides/browser/ This guide explains how to configure Evomi proxies in Google Chrome and Mozilla Firefox, both manually through system settings and using browser extensions for easier management. ## Chrome ### Manual Configuration Chrome uses your operating system's proxy settings. Changing them affects all browsers and applications that rely on system proxy configuration. **Windows:** 1. Open Chrome and go to **Settings** > **System** > **Open your computer's proxy settings**. 2. Under **Manual proxy setup**, toggle **Use a proxy server** to **On**. 3. Enter the Evomi proxy address and port: - **Address**: `rp.evomi.com` - **Port**: `1000` 4. Click **Save**. 5. When you visit a website, Chrome will prompt you for your proxy username and password. **macOS:** 1. Open Chrome and go to **Settings** > **System** > **Open your computer's proxy settings**. 2. In the Network preferences panel, select your active connection and click **Advanced**. 3. Go to the **Proxies** tab. 4. Check **Web Proxy (HTTP)** and enter: - **Server**: `rp.evomi.com` - **Port**: `1000` 5. Check **Proxy server requires password** and enter your Evomi credentials. 6. Click **OK** > **Apply**. System-level proxy settings do not natively support SOCKS5 in Chrome. For SOCKS5 or more granular control, use a browser extension. ### Using ZeroOmega (Recommended Extension) [ZeroOmega](https://chromewebstore.google.com/detail/proxy-switchyomega-3-zero/pfnededegaaopdmhkdmcofjmoldfiped) (also known as "Proxy SwitchyOmega 3") is the maintained successor to the original SwitchyOmega extension. It supports HTTP, HTTPS, SOCKS4, and SOCKS5 proxies with authentication. 1. Install **ZeroOmega** from the [Chrome Web Store](https://chromewebstore.google.com/detail/proxy-switchyomega-3-zero/pfnededegaaopdmhkdmcofjmoldfiped). 2. Click the ZeroOmega icon in the toolbar and select **Options**. 3. In the left sidebar, click **New Profile** and name it `Evomi`. 4. Configure the profile: - **Protocol**: Select `HTTP`, `HTTPS`, or `SOCKS5` - **Server**: `rp.evomi.com`, or `rp.evomi-proxy.com` if you selected `HTTPS` - **Port**: `1000` (HTTP), `1001` (HTTPS), or `1002` (SOCKS5) 5. Under the lock icon or **Authentication**, enter your Evomi username and password. 6. Click **Apply changes** in the left sidebar. 7. Click the ZeroOmega icon in the toolbar and switch to your `Evomi` profile. 8. Verify at `https://ip.evomi.com/s`. To stop using the proxy, click the ZeroOmega icon and switch back to **Direct** or **System Proxy**. ## Firefox ### Manual Configuration Firefox has its own proxy settings, independent of the operating system. 1. Open Firefox and go to **Settings**. 2. Search for `proxy` in the search bar at the top. 3. Click **Settings...** next to **Network Settings**. 4. Select **Manual proxy configuration**. 5. Enter the Evomi proxy address: - **HTTP Proxy**: `rp.evomi.com` | **Port**: `1000` - Check **Also use this proxy for HTTPS** - Or for SOCKS5: leave HTTP fields empty and enter under **SOCKS Host**: `rp.evomi.com` | **Port**: `1002`, select **SOCKS v5** 6. Click **OK**. 7. Firefox will prompt you for your proxy username and password on the first request. ### Using FoxyProxy (Recommended Add-on) [FoxyProxy Standard](https://addons.mozilla.org/en-US/firefox/addon/foxyproxy-standard/) is an actively maintained Firefox add-on for managing proxy configurations with rule-based switching. 1. Install **FoxyProxy Standard** from [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/foxyproxy-standard/). 2. Click the FoxyProxy icon in the toolbar and select **Options**. 3. Click **Add** to create a new proxy entry. 4. Fill in the proxy details: - **Title**: `Evomi` - **Type**: `HTTP` or `SOCKS5` - **Hostname**: `rp.evomi.com` - **Port**: `1000` (HTTP) or `1002` (SOCKS5) - **Username**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 5. Click **Save**. 6. Click the FoxyProxy icon and select your **Evomi** proxy to activate it. 7. Verify at `https://ip.evomi.com/s`. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Disable when not in use**: Always switch back to a direct connection or disable the proxy extension when you are done. - **Credential prompts**: With manual configuration, browsers will prompt for credentials on the first request. Extensions like ZeroOmega and FoxyProxy handle authentication automatically. - **SOCKS5 in Chrome**: Chrome does not support SOCKS5 through system proxy settings. Use ZeroOmega for SOCKS5 support. - **Password format**: Your Evomi password includes session and mode parameters, e.g. `your_password_session-anychars_mode-speed`. Enter the full string as your password. - **Incognito/Private mode**: Extensions may need explicit permission to run in incognito/private windows. Check the extension settings to enable this. --- ## cURL Source: https://docs.evomi.com/proxy-instructions/integration-guides/curl/ This guide shows how to use Evomi proxies with cURL, the command-line tool for making HTTP requests. cURL is the fastest way to test your proxy connection and is available by default on macOS, Linux, and Windows 10+. ## Quick Start ### HTTP Proxy ```bash curl -x http://rp.evomi.com:1000 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` ### HTTPS Proxy ```bash curl -x https://rp.evomi-proxy.com:1001 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` The HTTPS endpoint uses the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. Without it, cURL rejects the handshake with `SSL: no alternative certificate subject name matches target host name`. ### SOCKS5 Proxy ```bash curl -x socks5h://rp.evomi.com:1002 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` Replace `your_username` and `your_password` with your actual Evomi credentials (keep the `_session-anychars_mode-speed` parameters). The examples above pass the credentials with `-U` instead of embedding them in the proxy URL. Both forms are equivalent — cURL sends the same proxy authentication either way — but `-U` saves you from URL-encoding passwords that contain `@`, `:` or other reserved characters. The output should display the proxy server's IP address, not your real IP. ## Syntax Options cURL provides several ways to specify proxy settings: ### Inline credentials (most common) ```bash curl -x http://user:pass@host:port https://example.com ``` ### Separate credentials flag ```bash curl -x http://rp.evomi.com:1000 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` ### Long-form flags ```bash curl --proxy http://rp.evomi.com:1000 --proxy-user your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` ### Environment variable ```bash export https_proxy=http://your_username:your_password_session-anychars_mode-speed@rp.evomi.com:1000 curl https://ip.evomi.com/s ``` This sets the proxy for all subsequent cURL requests in the current shell session. ## SOCKS5: Local vs Remote DNS - **`socks5h://`** -- DNS is resolved by the proxy server (recommended, prevents DNS leaks) - **`socks5://`** -- DNS is resolved locally before sending through the proxy ```bash # Recommended: proxy resolves DNS curl -x socks5h://rp.evomi.com:1002 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s # Local DNS resolution curl -x socks5://rp.evomi.com:1002 -U your_username:your_password_session-anychars_mode-speed https://ip.evomi.com/s ``` ## Common Options ```bash # Verbose output (shows proxy connection details) curl -v -x http://rp.evomi.com:1000 -U user:pass https://ip.evomi.com/s # Set a timeout (30 seconds) curl --connect-timeout 30 -x http://rp.evomi.com:1000 -U user:pass https://ip.evomi.com/s # Follow redirects curl -L -x http://rp.evomi.com:1000 -U user:pass https://example.com # Custom User-Agent curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -x http://rp.evomi.com:1000 -U user:pass https://example.com # Save response to file curl -o output.html -x http://rp.evomi.com:1000 -U user:pass https://example.com ``` ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Special characters in password**: If your password contains special characters (`@`, `:`, `!`), URL-encode them or use the `-U` flag to pass credentials separately. - **Verbose mode**: Use `-v` to see the full connection flow, including proxy handshake and authentication. - **Connection refused**: Verify the hostname and port. Make sure you are using the correct protocol (`http://` vs `socks5h://`) for the port. - **Timeout errors**: Increase the timeout with `--connect-timeout` and `--max-time`. Proxy connections may be slower than direct connections. - **Unset proxy**: If you set a proxy via environment variable and want to bypass it for a single request, use `--noproxy "*"`. --- ## iOS Source: https://docs.evomi.com/proxy-instructions/integration-guides/ios/ This guide covers how to configure and use Evomi proxies on iPhones and iPads. iOS has limited native proxy support (HTTP only, no SOCKS5), so a dedicated proxy app is recommended for full functionality. ## Prerequisites - An active Evomi proxy subscription - Your Evomi proxy credentials (username and password) - An iPhone or iPad running iOS 15 or later ## Method 1: Using Shadowrocket (Recommended) [Shadowrocket](https://apps.apple.com/us/app/shadowrocket/id932747118) ($2.99, one-time purchase) is the most popular iOS proxy client. It supports HTTP, HTTPS, SOCKS5, Shadowsocks, and many other protocols. It routes traffic through a local VPN service and offers rule-based routing per app or domain. ### Setup Steps 1. Install **Shadowrocket** from the App Store. 2. Open Shadowrocket and tap the **+** button in the top-right corner to add a new server. 3. Configure the proxy: - **Type**: Select `HTTP` or `SOCKS5` depending on which port you want to use - **Address**: `rp.evomi.com` - **Port**: `1000` for HTTP or `1002` for SOCKS5 - **User**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 4. Tap **Save** (top-right). 5. Back on the main screen, make sure your new server is selected (checkmark next to it). 6. Toggle the **Connect** switch at the top to enable the proxy. iOS will prompt you to allow the VPN configuration -- tap **Allow**. 7. Verify your connection by opening Safari and navigating to `https://ip.evomi.com/s`. The displayed IP should be the proxy IP. Shadowrocket supports advanced routing rules. Under the **Config** tab, you can set up rules to route specific domains or apps through the proxy while keeping others on a direct connection. ## Method 2: Using Potatso Lite (Free Alternative) [Potatso Lite](https://apps.apple.com/us/app/potatso-lite/id1239860606) is a free iOS proxy client that supports SOCKS5 and Shadowsocks protocols. ### Setup Steps 1. Install **Potatso Lite** from the App Store. 2. Open the app and tap **Add a Proxy**. 3. Select **Manual Input**. 4. Configure: - **Type**: `SOCKS5` - **Host**: `rp.evomi.com` - **Port**: `1002` - **User**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 5. Tap **Done** to save. 6. On the main screen, tap **Start** to enable the VPN connection. Allow the VPN configuration when prompted. 7. Verify at `https://ip.evomi.com/s`. ## Method 3: Manual Wi-Fi Proxy Configuration (HTTP Only) iOS natively supports HTTP proxy configuration per Wi-Fi network. This method does support username/password authentication, but it is limited to HTTP proxies only and does not cover mobile data or SOCKS5. ### Steps 1. Open **Settings** > **Wi-Fi**. 2. Tap the **(i)** icon next to your connected Wi-Fi network. 3. Scroll down and tap **Configure Proxy**. 4. Select **Manual**. 5. Fill in the details: - **Server**: `rp.evomi.com` - **Port**: `1000` - **Authentication**: Toggle **On** - **Username**: Your Evomi username - **Password**: Your Evomi password, e.g. `your_password_session-anychars_mode-speed` 6. Tap **Save** (top-right). 7. Verify at `https://ip.evomi.com/s`. This method only works for HTTP proxies on the current Wi-Fi network. It does not apply to mobile data, and some apps may ignore the system proxy setting. For full coverage, use Shadowrocket or Potatso Lite instead. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Verifying Your Connection After enabling the proxy, open Safari and navigate to: ``` https://ip.evomi.com/s ``` The displayed IP should be the proxy server's IP. If it shows your real IP, the proxy is not active. ## Tips and Troubleshooting - **Credentials**: Double-check the hostname, port, username, and full password string (including `_session-` and `_mode-` parameters). - **SOCKS5 requires an app**: iOS does not natively support SOCKS5 proxies. You must use Shadowrocket, Potatso Lite, or a similar app. - **Apps ignoring proxy**: Some apps may bypass the system Wi-Fi proxy setting. A VPN-based proxy app like Shadowrocket ensures all traffic is routed correctly. - **Toggle Airplane mode**: If you experience connectivity issues after changing proxy settings, toggling Airplane mode can reset the network stack. - **Disable when not needed**: Remember to disconnect the proxy (or toggle off the VPN in Shadowrocket/Potatso) when you are done to restore normal internet access. - **Battery usage**: VPN-based proxy apps may slightly increase battery consumption. This is normal since all traffic passes through the app's VPN tunnel. --- ## Proxifier on macOS Source: https://docs.evomi.com/proxy-instructions/integration-guides/macos-proxifier/ This guide explains how to configure Proxifier on macOS to route application traffic through Evomi proxies. Proxifier intercepts network connections from any application and tunnels them through a proxy server without requiring the application to have native proxy support. ## Prerequisites - **Proxifier** installed on your macOS machine - An active Evomi proxy subscription - Your Evomi proxy credentials (username and password) ## Step-by-Step Configuration ### 1. Launch Proxifier Open Proxifier from the **Applications** folder or search for it using **Spotlight**. ### 2. Add a New Proxy Server - Go to **Proxifier** > **Preferences** (or press `Cmd+,`). - Select the **Proxies** tab. - Click the **Add** button (`+`) at the bottom. ### 3. Enter Evomi Proxy Details In the **Edit Proxy Server** window: - **Address**: `rp.evomi.com` - **Port**: `1000` (for HTTP) or `1002` (for SOCKS5) - **Protocol**: Select **HTTP** or **SOCKS Version 5** to match the port ### 4. Enable Authentication - Check **Enable authentication**. - Enter your Evomi **Username** and **Password**. Your password should include your desired session and mode parameters: ``` yourpassword_session-123456_mode-quality ``` Replace `yourpassword` with your actual password and `123456` with any session identifier. ### 5. Test the Proxy Server - Click the **Check** button to verify connectivity. - If the test succeeds, click **OK** to save. ### 6. Configure Proxification Rules - In the Preferences window, select the **Rules** tab. - Click **Add** (`+`) to create a new rule. - Configure: - **Applications**: Leave as **Any** or specify applications to route through the proxy - **Targets**: Leave as **Any** or define specific domains/IPs - **Action**: Select **Proxy** and choose the Evomi proxy you added - Click **OK** to save. ### 7. Verify - Open a browser and navigate to `https://ip.evomi.com/s`. - The displayed IP should be the proxy IP, not your real IP. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Proxy Modes Evomi residential proxies support different modes to optimize for your use case. Append the mode parameter to your password: Mode selection is only available for residential proxies. | Mode | Password Suffix | Pool Size | Speed | Quality | Best For | |---|---|---|---|---|---| | **Standard** | *(default, no suffix)* | Largest | Good | Good | General scraping, large IP pool needs | | **Speed** | `_mode-speed` | Medium | Best | Good | Time-sensitive tasks, large data transfers | | **Quality** | `_mode-quality` | Smallest | Best | Best | Strict sites, avoiding CAPTCHAs | **Example password with quality mode:** ``` mysecretpassword_session-abc123_mode-quality ``` ## Tips and Troubleshooting - **Incorrect credentials**: Double-check your username and password, including the `_mode-` parameter. - **Connection issues**: Ensure your firewall or security settings are not blocking Proxifier. - **Authentication failed**: Verify the password includes the correct mode suffix. - **Switching modes**: Change the `_mode-` parameter in your password (`mode-speed`, `mode-standard`, `mode-quality`). - **Restart applications**: After changing proxy settings, restart applications to ensure they pick up the new configuration. --- ## Node.js Source: https://docs.evomi.com/proxy-instructions/integration-guides/nodejs/ This guide explains how to use Evomi proxies with Node.js using **Axios** (the most popular HTTP client) and **node-fetch**. Both libraries support HTTP proxies, and SOCKS5 support is available via additional packages. ## Prerequisites - Node.js 18+ installed - Your Evomi proxy credentials (username and password) ## Axios ### Installation ```bash npm install axios ``` ### HTTP Proxy Axios has built-in support for HTTP/HTTPS proxies: ```javascript const axios = require('axios'); const client = axios.create({ proxy: { protocol: 'http', host: 'rp.evomi.com', port: 1000, auth: { username: 'your_username', password: 'your_password_session-anychars_mode-speed', }, }, }); client.get('https://ip.evomi.com/s') .then(res => console.log('Proxy IP:', res.data.trim())) .catch(err => console.error('Error:', err.message)); ``` ### SOCKS5 Proxy Axios does not natively support SOCKS5. Use `socks-proxy-agent`: ```bash npm install socks-proxy-agent ``` ```javascript const axios = require('axios'); const { SocksProxyAgent } = require('socks-proxy-agent'); const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const agent = new SocksProxyAgent( `socks5h://${proxyUser}:${proxyPass}@rp.evomi.com:1002` ); axios.get('https://ip.evomi.com/s', { httpAgent: agent, httpsAgent: agent, proxy: false, }) .then(res => console.log('Proxy IP:', res.data.trim())) .catch(err => console.error('Error:', err.message)); ``` When using an agent, set `proxy: false` in Axios options to prevent Axios from trying to apply its own proxy configuration on top of the agent. ## node-fetch ### Installation ```bash npm install node-fetch https-proxy-agent ``` ### HTTP Proxy ```javascript const fetch = require('node-fetch'); const { HttpsProxyAgent } = require('https-proxy-agent'); const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const agent = new HttpsProxyAgent( `http://${proxyUser}:${proxyPass}@rp.evomi.com:1000` ); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log('Proxy IP:', body.trim())) .catch(err => console.error('Error:', err.message)); ``` ### SOCKS5 Proxy ```bash npm install node-fetch socks-proxy-agent ``` ```javascript const fetch = require('node-fetch'); const { SocksProxyAgent } = require('socks-proxy-agent'); const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const agent = new SocksProxyAgent( `socks5h://${proxyUser}:${proxyPass}@rp.evomi.com:1002` ); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log('Proxy IP:', body.trim())) .catch(err => console.error('Error:', err.message)); ``` ## ES Modules Syntax If your project uses ES modules (`"type": "module"` in `package.json`), use `import` instead of `require`: ```javascript import axios from 'axios'; import { SocksProxyAgent } from 'socks-proxy-agent'; const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const agent = new SocksProxyAgent( `socks5h://${proxyUser}:${proxyPass}@rp.evomi.com:1002` ); const { data } = await axios.get('https://ip.evomi.com/s', { httpAgent: agent, httpsAgent: agent, proxy: false, }); console.log('Proxy IP:', data.trim()); ``` ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Credentials**: Double-check your username and password. The password format is `your_password_session-anychars_mode-speed`. - **`proxy: false` with agents**: When using `httpAgent`/`httpsAgent` in Axios, always set `proxy: false` to avoid conflicts. - **`socks5h://` vs `socks5://`**: Use `socks5h://` to resolve DNS on the proxy server (recommended). Use `socks5://` for local DNS resolution. - **Timeouts**: Set timeouts to avoid hanging requests: `axios.get(url, { timeout: 15000 })` or `fetch(url, { agent, signal: AbortSignal.timeout(15000) })`. - **Special characters**: If your password contains special characters, URL-encode them with `encodeURIComponent()`. - **node-fetch v3**: Version 3+ is ESM-only. If using CommonJS, stick with `node-fetch@2` or switch to ES modules. --- ## Playwright Source: https://docs.evomi.com/proxy-instructions/integration-guides/playwright/ This guide explains how to use Evomi proxies with Playwright, a modern browser automation library with built-in proxy authentication support. Examples are provided for both JavaScript/TypeScript and Python. ## Prerequisites - Node.js 18+ (for JavaScript) or Python 3.8+ (for Python) - Playwright installed in your project - Your Evomi proxy credentials (username and password) ## Installation **JavaScript:** ```bash npm install playwright npx playwright install ``` **Python:** ```bash pip install playwright playwright install ``` ## Configuration Playwright natively supports proxy authentication -- no extensions or workarounds needed. You can configure the proxy at the browser level (applies to all pages) or at the context level (different proxies per context). ### Browser-Level Proxy This applies the proxy to all pages opened by this browser instance. **JavaScript:** ```javascript const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ proxy: { server: 'http://rp.evomi.com:1000', username: 'your_username', password: 'your_password_session-anychars_mode-speed' } }); const page = await browser.newPage(); await page.goto('https://ip.evomi.com/s'); console.log(await page.textContent('body')); await browser.close(); })(); ``` **Python:** ```python from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(proxy={ "server": "http://rp.evomi.com:1000", "username": "your_username", "password": "your_password_session-anychars_mode-speed" }) page = browser.new_page() page.goto("https://ip.evomi.com/s") print(page.text_content("body")) browser.close() ``` ### Context-Level Proxy Use different proxies for different browser contexts within the same browser instance. This is useful for multi-region testing or rotating proxies. **JavaScript:** ```javascript const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ proxy: { server: 'per-context' } }); const usContext = await browser.newContext({ proxy: { server: 'http://rp.evomi.com:1000', username: 'your_username', password: 'your_password_session-anychars_country-us' } }); const dePage = await (await browser.newContext({ proxy: { server: 'http://rp.evomi.com:1000', username: 'your_username', password: 'your_password_session-anychars_country-de' } })).newPage(); const usPage = await usContext.newPage(); await usPage.goto('https://ip.evomi.com/s'); console.log('US proxy IP:', await usPage.textContent('body')); await dePage.goto('https://ip.evomi.com/s'); console.log('DE proxy IP:', await dePage.textContent('body')); await browser.close(); })(); ``` On Chromium on Windows, you must pass `proxy: { server: 'per-context' }` at the browser level to enable context-specific proxies. On other platforms and browsers, this is not required. ### Using SOCKS5 Replace the server URL to use the SOCKS5 endpoint: ```javascript proxy: { server: 'socks5://rp.evomi.com:1002', username: 'your_username', password: 'your_password_session-anychars_mode-speed' } ``` ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Running the Script **JavaScript:** ```bash node proxy_test.js ``` **Python:** ```bash python proxy_test.py ``` The script will print the proxy IP to the console. ## Tips and Troubleshooting - **Credentials**: Double-check your username and password. The password format is `your_password_session-anychars_mode-speed`. - **Browser choice**: Playwright supports Chromium, Firefox, and WebKit. All three support proxy configuration. Replace `chromium` with `firefox` or `webkit` as needed. - **Headless mode**: Playwright runs headless by default. Add `headless: False` (Python) or `headless: false` (JS) to `launch()` to see the browser window. - **Timeouts**: For slow proxy connections, increase the navigation timeout: `page.goto(url, { timeout: 60000 })`. - **Proxy bypass**: To exclude specific hosts from the proxy, add a `bypass` field: `proxy: { server: '...', bypass: 'localhost,127.0.0.1' }`. --- ## Proxy Certificate Source: https://docs.evomi.com/proxy-instructions/integration-guides/proxy-certificate/ Some Evomi features need to read the responses passing through the proxy rather than tunnelling them untouched. [Response Caching](/proxy-instructions/residential-proxies/expert-settings/caching/) is one of them: to store a stylesheet or an image, the proxy has to see it. For HTTPS that means the connection is terminated at our proxy and re-signed on the fly with the **Evomi Proxy CA**. Your client has no reason to trust that certificate, so it aborts the handshake until you tell it to. There are two ways to handle it and neither is more correct than the other: | | Skip verification | Trust the Evomi CA | |-------------------|-----------------------------------|------------------------------------| | Setup | One flag or variable per client | Download and install once | | TLS verification | Off for that client | Stays on | | Suits | Scraping and automation against public pages | Shared machines, or processes that also talk to your own services | Most people scraping public pages take the first option, because it is a single flag and there is nothing to install or keep track of. Pick the second if the same process handles anything you actually need certificate guarantees for. **Only needed for cache-enabled requests** Standard proxying tunnels HTTPS end to end and needs neither option. You only need this page if your proxy password uses `_cache-`. ## Option 1: Skip Certificate Verification Every HTTP client has a switch for this. Nothing to download: #### CURL ```bash curl -k -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s ``` `-k` and `--insecure` are the same flag. #### Python ```Python import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) proxy_pass = "testpassword_cache-all" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify=False) print(response.text) ``` #### Node.js ```JavaScript // Per client, which keeps the rest of the process verifying as normal const agent = new https.Agent({ rejectUnauthorized: false }); // Or for the whole process // export NODE_TLS_REJECT_UNAUTHORIZED=0 ``` #### Go ```Go transport := &http.Transport{ Proxy: http.ProxyURL(proxyURL), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: transport} ``` #### Browsers ```javascript // Puppeteer const browser = await puppeteer.launch({ args: ['--proxy-server=http://rp.evomi.com:1000', '--ignore-certificate-errors'], }); // Playwright const context = await browser.newContext({ ignoreHTTPSErrors: true }); // Selenium (Chrome) options.addArguments('--ignore-certificate-errors'); ``` This is fine for scraping public pages, where you are reading content rather than trusting it. The trade-off is that the client then accepts **any** certificate from **any** host, so it can no longer detect interception by anyone else on the path. Keep it scoped to the code doing proxy work and don't apply it to code that also handles logins, payments or your own APIs — in Node and Go the per-client form above does exactly that, leaving the rest of the process verifying normally. ## Option 2: Trust the Evomi CA This keeps certificate verification switched on, so your client still rejects anything that is not signed by us or by a public CA. ### Download ```bash curl -O https://cdn.evomi.com/downloads/evomi.crt ``` | Field | Value | |----------------|--------------------------------------| | Common Name | `Evomi Proxy CA` | | Format | PEM | | Valid until | 6 January 2036 | Verify the download before installing it: ```bash openssl x509 -in evomi.crt -noout -fingerprint -sha256 ``` The SHA-256 fingerprint must be: ``` E3:75:7D:4F:CB:87:9F:6A:52:24:92:0F:20:93:8F:D4:89:D1:92:01:87:C7:EA:E4:3C:40:68:C2:4D:CC:1F:42 ``` ### Per-Tool Setup Most clients accept an extra CA through an environment variable or a single argument, which applies to that process only: #### CURL ```bash # Per command curl --cacert evomi.crt -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s # Or for every command in the shell session export CURL_CA_BUNDLE=/path/to/evomi.crt ``` #### Python ```Python import requests proxy_pass = "testpassword_cache-all" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "https": proxy_url, } # verify accepts a path to a CA bundle response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify="evomi.crt") print(response.text) # httpx and aiohttp take an ssl.SSLContext instead: # ctx = ssl.create_default_context(cafile="evomi.crt") ``` #### Node.js ```bash # Node reads additional CAs from this variable at startup export NODE_EXTRA_CA_CERTS=/path/to/evomi.crt node scraper.js ``` #### Go ```bash # Go's crypto/x509 honours these on Unix systems export SSL_CERT_FILE=/path/to/evomi.crt go run main.go ``` #### Java ```bash # Import into a dedicated truststore, leaving the JDK default untouched keytool -importcert -alias evomi-proxy-ca -file evomi.crt \ -keystore evomi-truststore.jks -storepass changeit -noprompt java -Djavax.net.ssl.trustStore=evomi-truststore.jks \ -Djavax.net.ssl.trustStorePassword=changeit -jar scraper.jar ``` Chromium ignores all of the above and reads the operating system trust store, so headless browsers need the system-wide install below — or the `--ignore-certificate-errors` flag from Option 1, which is why most browser automation ends up using that instead. ### System-Wide Installation **Scope the trust as narrowly as you can** Adding any root CA to a machine's system trust store lets certificates signed by it be accepted for **every** host that machine talks to, not only traffic you send through Evomi. Prefer the per-tool options above, which apply to a single process, and prefer a dedicated machine or container for proxy work over your daily-driver workstation. Install system-wide only when a tool gives you no other option. #### macOS ```bash sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain evomi.crt ``` To remove it later: `sudo security delete-certificate -c "Evomi Proxy CA" /Library/Keychains/System.keychain` #### Windows ```powershell # Run as Administrator certutil -addstore -f "ROOT" evomi.crt # To remove certutil -delstore "ROOT" "Evomi Proxy CA" ``` #### Debian/Ubuntu ```bash sudo cp evomi.crt /usr/local/share/ca-certificates/evomi.crt sudo update-ca-certificates # To remove sudo rm /usr/local/share/ca-certificates/evomi.crt sudo update-ca-certificates --fresh ``` The file must keep the `.crt` extension and PEM encoding or it is skipped silently. #### RHEL/Fedora ```bash sudo cp evomi.crt /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust # To remove sudo rm /etc/pki/ca-trust/source/anchors/evomi.crt sudo update-ca-trust ``` **Certificate renewal** The CA is valid until January 2036, so no rotation is required in normal use. If we ever need to replace it early we will announce it in advance — the download URL stays the same, so re-running the steps above is all that is needed. ## Verifying Setup Send one request through a cache-enabled password. A response means the certificate is being accepted: ```bash # If you skipped verification curl -k -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s # If you trusted the CA curl --cacert evomi.crt -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s ``` ## Troubleshooting | Error | Fix | |-----------------------------------------------------|------------------------------------------------------------| | `certificate signed by unknown authority` | Neither option is active for this client — apply one of them | | `unable to get local issuer certificate` (curl) | Add `-k`, or pass `--cacert evomi.crt` | | `SSLCertVerificationError` (Python) | Set `verify=False` or `verify="evomi.crt"` on the request | | `ERR_CERT_AUTHORITY_INVALID` (Chromium) | Launch with `--ignore-certificate-errors`, or install the CA system-wide | | Handshake fails with no HTTP error at all | Expected when the certificate is rejected — the connection closes during the handshake, before any response | | Works in curl but not in your app | The app has its own trust store; check for a bundled CA file such as `certifi` | | Trusted the CA but a tool still refuses it | That tool likely reads the system store rather than the environment variable — install system-wide or skip verification for it | --- ## ProxyTool Source: https://docs.evomi.com/proxy-instructions/integration-guides/proxytool/ This guide explains how to configure [ProxyTool](https://proxytool.app/?from=evomi) to route application traffic through Evomi proxies. ProxyTool is a Windows proxy client that intercepts connections with a WFP (Windows Filtering Platform) driver, so you can send a specific application through an Evomi proxy even when that application has no proxy settings of its own. Compared with a system-wide proxy setting, you decide per application which traffic is proxied and which goes direct, and you get a live view of every connection with the bandwidth and cost it produced. ## Prerequisites - **ProxyTool** installed on your Windows machine — Windows 10 or later (64-bit), with administrator privileges for the kernel driver - An active Evomi proxy subscription - Your Evomi proxy credentials (username and password) ProxyTool currently ships a Windows client. macOS and Linux clients are listed as coming soon on the [ProxyTool download page](https://proxytool.app/?from=evomi). On macOS, use [Proxifier on macOS](/proxy-instructions/integration-guides/macos-proxifier/) instead. ## Get Your Evomi Credentials Your proxy username and password are in the [Evomi dashboard](https://my.evomi.com/) under the product you want to use. The [Proxy Generator](/proxy-instructions/proxy-generator/) builds a ready-made proxy string with your geo-targeting and session settings already applied, which is the fastest way to get something you can paste straight into ProxyTool. ## Evomi Proxy Endpoints | Proxy Type | HTTP | SOCKS5 | |---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi.com:2002` | SOCKS5 is the better default in ProxyTool: it resolves DNS remotely and it is the only protocol ProxyTool can use for UDP routing. Evomi also offers an HTTPS (TLS-wrapped) proxy endpoint — see [Proxy Protocols](/proxy-instructions/proxy-protocols/) for its hostname and port, which differ from the HTTP endpoint. ## Add Your Evomi Proxy Open the **Proxies** page and click **Add Proxy**. You can either paste a full proxy string or fill in the fields by hand. ### Paste the proxy string ProxyTool parses a pasted proxy string and fills in host, port, protocol, username and password for you. Paste it into the **Proxy Name** field of the Add Proxy dialog — the parser recognises Evomi's format, and a `curl -x` command works as well. ``` socks5://rp.evomi.com:1002:testuser:testpassword_session-abc123 ``` Replace `testuser` and `testpassword` with your own credentials. The `_session-abc123` suffix on the password is an Evomi parameter — it keeps the same exit IP for as long as you reuse that session identifier. The `protocol://username:password@host:port` form that the Proxy Generator and most provider dashboards produce is parsed too. The colon-separated form above is used here because it survives copy-paste from this page unchanged. ### Or enter the fields manually | Field | Value | |---|---| | **Proxy Name** | Any label, for example `Evomi Residential` | | **Host Address** | `rp.evomi.com` | | **Port** | `1002` for SOCKS5, `1000` for HTTP | | **Proxy Type** | **SOCKS5** or **HTTP**, matching the port | Then turn on **Requires Authentication** and enter your Evomi **Username** and **Password**. For SOCKS5, ProxyTool negotiates the authentication method itself. For HTTP, leave the **Method** selector on **Basic**, which is what Evomi's username and password authentication expects. Your password carries any Evomi parameters you want to apply, appended with underscores: ``` testpassword_session-abc123_mode-quality ``` Optionally fill in **Cost per GB** — `0.49` for residential traffic — so ProxyTool's cost tracking reflects what the traffic actually costs you. ### Check the proxy before saving Click **Check** in the dialog. ProxyTool runs a five-step test — proxy connection, handshake, authentication, credential check and target connection — and reports the total latency. Save only once it passes. A newly added proxy is inactive. It starts carrying traffic when you activate it on the Proxies page or when an active routing rule points at it. ## Route an Application Through It Open the **Rules** page and click **Add Rule**. | Field | What to enter | |---|---| | **Rule Name** | A label, for example `Chrome through Evomi` | | **Application / Process** | The executable, for example `chrome.exe`. Several are separated with `;`, and **Browse** picks one from disk. Leave empty or `*` to match every application. | | **Target Hosts** | `*` for all hosts, or a comma-separated pattern such as `*.example.com` | | **Target Ports** | `*` for all ports, or a list such as `80,443` | | **Protocol** | `TCP` for Evomi proxies | | **Action** | **Proxy**, then pick your Evomi proxy from the selector | Rules are evaluated top to bottom and the first match wins, so put a specific rule above the broad ones. Leave **Enable this rule immediately** on to activate it as soon as you save. Set the rule's protocol to **TCP**. ProxyTool can relay UDP, but only over SOCKS5 — its HTTP and SOCKS4 types are TCP-only — and Evomi's SOCKS5 proxies carry TCP as well. UDP is an enterprise feature that support has to enable for your account, and it uses a separate endpoint described under [UDP Protocol Support](/proxy-instructions/residential-proxies/expert-settings/udp/). ## Verify the Connection - Open a browser you routed through the rule and navigate to `https://ip.evomi.com/s`. The displayed IP should be the proxy IP, not your real one. - Open **Traffic Monitor** → **Connections**. Each row names the process, the target, the rule that matched and the proxy that carried it, so you can confirm the traffic took the route you intended instead of going direct. To stop the browser resolving hostnames outside the tunnel, turn on **Resolve hostnames through proxy** on the **DNS** page. ## Sessions, Geo-Targeting and Pool Modes Everything Evomi exposes through the password works here unchanged, because ProxyTool sends the password through untouched. Append the parameters with underscores: | Parameter | Effect | |---|---| | `_session-abc123` | Keeps the same exit IP for that session identifier — see [Proxy Sessions](/proxy-instructions/residential-proxies/proxy-sessions/) | | `_country-US` | Targets a country — see [Geo Targeting](/proxy-instructions/residential-proxies/geo-targetting/) | | `_mode-quality` | Selects the residential pool — see [Proxy Pools](/proxy-instructions/residential-proxies/modes/) | A password combining all three looks like this: ``` testpassword_session-abc123_country-US_mode-quality ``` To run two applications on different exit IPs, add the proxy twice with a different `_session-` value in each password, then point one rule at each. ## Coming from Proxifier ProxyTool imports a Proxifier profile with **Settings** → **Profiles** → **Migrate from Proxifier**, taking the `.ppx` or `.xml` file with its proxy servers, chains, rules and DNS settings. Passwords encrypted with Windows DPAPI decrypt only on the Windows user account that created the profile; otherwise the import prompts you to re-enter them. Review the imported proxies against the endpoints above before you activate anything. ## Tips and Troubleshooting - **Authentication failed**: Check the username, and check that the password still carries the parameters you meant to send — the `_` separators and the parameter names have to match exactly. - **Traffic is not being proxied**: Confirm the proxy is active on the Proxies page, that the rule is enabled, and that no earlier rule matches the same application first. - **The wrong executable is routed**: Browsers spawn several processes. Match the main executable name, and check the process column in Traffic Monitor to see which one actually opened the connection. - **UDP traffic does not go through**: Expected on a standard account. Set the rule's protocol to TCP, or contact support about enterprise UDP support. - **Connection issues**: Make sure your firewall or antivirus is not blocking ProxyTool or its driver. - **A `DETECTABLE` or `BLOCKED` TLS warning**: ProxyTool compares the JA3 fingerprint of each client against known ones and flags scripted clients such as `curl.exe` or Python `requests`. It is informational — ProxyTool does not change your fingerprint, and it says nothing about the proxy being healthy. - **Restart applications**: After changing rules, restart the application so new connections pick up the routing. --- ## Puppeteer Source: https://docs.evomi.com/proxy-instructions/integration-guides/puppeteer/ This guide explains how to use Evomi proxies with Puppeteer, a Node.js library for controlling headless Chrome/Chromium. Puppeteer supports proxy authentication through the `page.authenticate()` method. ## Prerequisites - Node.js 18+ installed - Your Evomi proxy credentials (username and password) ## Installation ```bash npm install puppeteer ``` Puppeteer automatically downloads a compatible Chromium binary during installation. ## Configuration Puppeteer sets the proxy server via a Chrome launch argument and handles authentication separately with `page.authenticate()`. ```javascript const puppeteer = require('puppeteer'); (async () => { const proxyHost = 'rp.evomi.com'; const proxyPort = 1000; const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const browser = await puppeteer.launch({ args: [`--proxy-server=http://${proxyHost}:${proxyPort}`], }); const page = await browser.newPage(); await page.authenticate({ username: proxyUser, password: proxyPass, }); await page.goto('https://ip.evomi.com/s'); const body = await page.evaluate(() => document.body.innerText); console.log('Proxy IP:', body.trim()); await browser.close(); })(); ``` Replace `your_username` with your Evomi username and `your_password` with your actual password (keep the `_session-anychars_mode-speed` parameters). ## Explanation 1. **`--proxy-server` argument**: Tells Chromium to route all traffic through the specified proxy. Credentials are not included in this URL. 2. **`page.authenticate()`**: Sets the proxy username and password. This must be called **before** any navigation on the page. 3. **Verification**: Navigating to `https://ip.evomi.com/s` returns the IP seen by the server, which should be the proxy IP. ## Using SOCKS5 with proxy-chain Puppeteer's `page.authenticate()` only works with HTTP/HTTPS proxies. For SOCKS5 proxies, use the [proxy-chain](https://www.npmjs.com/package/proxy-chain) package to create a local forwarder: ```bash npm install proxy-chain ``` ```javascript const puppeteer = require('puppeteer'); const proxyChain = require('proxy-chain'); (async () => { const proxyUser = 'your_username'; const proxyPass = 'your_password_session-anychars_mode-speed'; const oldProxyUrl = `socks5://${proxyUser}:${proxyPass}@rp.evomi.com:1002`; const newProxyUrl = await proxyChain.anonymizeProxy(oldProxyUrl); const browser = await puppeteer.launch({ args: [`--proxy-server=${newProxyUrl}`], }); const page = await browser.newPage(); await page.goto('https://ip.evomi.com/s'); const body = await page.evaluate(() => document.body.innerText); console.log('Proxy IP:', body.trim()); await browser.close(); await proxyChain.closeAnonymizedProxy(newProxyUrl); })(); ``` `proxy-chain` creates a local proxy forwarder that handles the SOCKS5 authentication on your behalf, so Puppeteer can connect without needing `page.authenticate()`. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Running the Script ```bash node puppeteer_proxy_test.js ``` The proxy IP will be printed to the console. ## Tips and Troubleshooting - **Credentials**: Double-check your proxy host, port, username, and password. The password format is `your_password_session-anychars_mode-speed`. - **Authenticate before navigation**: `page.authenticate()` must be called before `page.goto()`. If called after, it will not apply to the current page. - **New pages need re-authentication**: Each new `page` created via `browser.newPage()` requires its own `page.authenticate()` call. - **Headless mode**: Puppeteer runs headless by default. Add `headless: false` to `launch()` to see the browser window for debugging. - **SSL errors**: If you encounter certificate errors with specific target sites, you can add `'--ignore-certificate-errors'` to the `args` array. Use with caution in production. - **Timeouts**: For slow connections, increase the navigation timeout: `page.goto(url, { timeout: 60000 })`. --- ## Python Requests Source: https://docs.evomi.com/proxy-instructions/integration-guides/python-requests/ This guide explains how to use Evomi proxies with the Python `requests` library, the most popular HTTP client for Python. It covers HTTP and SOCKS5 proxies, session-based usage, and error handling. ## Prerequisites - Python 3.8+ installed - Your Evomi proxy credentials (username and password) ## Installation ```bash pip install requests ``` For SOCKS5 proxy support: ```bash pip install "requests[socks]" ``` ## Basic Usage ### HTTP Proxy ```python import requests proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies, timeout=15) print("Proxy IP:", response.text.strip()) ``` ### SOCKS5 Proxy ```python import requests proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"socks5h://{proxy_user}:{proxy_pass}@rp.evomi.com:1002" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies, timeout=15) print("Proxy IP:", response.text.strip()) ``` The `socks5h://` scheme routes DNS resolution through the proxy server, preventing DNS leaks. Use `socks5://` if you want local DNS resolution. Replace `your_username` and `your_password` with your actual Evomi credentials. ## Using Sessions (Recommended) For multiple requests, use a `Session` object. This reuses the underlying TCP connection for better performance and lets you set the proxy once: ```python import requests proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@rp.evomi.com:1000" session = requests.Session() session.proxies.update({ "http": proxy_url, "https": proxy_url, }) session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" }) response = session.get("https://ip.evomi.com/s", timeout=15) print("Proxy IP:", response.text.strip()) response = session.get("https://example.com", timeout=15) print("Status:", response.status_code) ``` ## Error Handling ```python import requests proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@rp.evomi.com:1000" proxies = {"http": proxy_url, "https": proxy_url} try: response = requests.get("https://ip.evomi.com/s", proxies=proxies, timeout=15) response.raise_for_status() print("Proxy IP:", response.text.strip()) except requests.exceptions.ProxyError as e: print(f"Proxy connection failed: {e}") except requests.exceptions.Timeout: print("Request timed out") except requests.exceptions.HTTPError as e: print(f"HTTP error: {e}") except requests.exceptions.RequestException as e: print(f"Request failed: {e}") ``` ## Retries with Backoff For production scripts, add automatic retries: ```python import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" proxy_url = f"http://{proxy_user}:{proxy_pass}@rp.evomi.com:1000" session = requests.Session() session.proxies.update({"http": proxy_url, "https": proxy_url}) retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) response = session.get("https://ip.evomi.com/s", timeout=15) print("Proxy IP:", response.text.strip()) ``` ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Credentials**: Double-check your username and password. The password format is `your_password_session-anychars_mode-speed`. - **Special characters**: If your password contains `@`, `:`, or other special characters, URL-encode them with `urllib.parse.quote()`: ```python from urllib.parse import quote proxy_url = f"http://{quote(user)}:{quote(password)}@rp.evomi.com:1000" ``` - **Timeouts**: Always set a `timeout` to avoid hanging indefinitely. Use a tuple for separate connect/read timeouts: `timeout=(5, 30)`. - **SOCKS5 dependency**: If you get `MissingSchema` or `MissingSocksModule` errors, make sure you installed `requests[socks]`. - **Environment variables**: You can set proxies globally via environment variables (`HTTP_PROXY`, `HTTPS_PROXY`), but use `session.trust_env = False` if you want your code to ignore them. - **SSL verification**: Avoid `verify=False` in production. If you see SSL errors, update your system CA certificates. --- ## Scrapy Source: https://docs.evomi.com/proxy-instructions/integration-guides/scrapy/ This guide explains how to use Evomi proxies with Scrapy, a Python framework for web crawling and scraping. The integration uses a custom downloader middleware to inject proxy settings into every request. ## Prerequisites - Python 3.8+ installed - A Scrapy project set up - Your Evomi proxy credentials (username and password) ## Installation ```bash pip install scrapy ``` ## Configuration ### Step 1: Add Proxy Settings to `settings.py` Open your project's `settings.py` and add the Evomi proxy configuration: ```python # Evomi Proxy Configuration PROXY_HOST = "rp.evomi.com" PROXY_PORT = "1000" PROXY_USER = "your_username" PROXY_PASS = "your_password_session-anychars_mode-speed" PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}" USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" ``` ### Step 2: Create a Proxy Middleware Add the following class to your project's `middlewares.py`: ```python from scrapy.exceptions import NotConfigured class EvomiProxyMiddleware: def __init__(self, proxy_url): self.proxy_url = proxy_url @classmethod def from_crawler(cls, crawler): proxy_url = crawler.settings.get("PROXY_URL") if not proxy_url: raise NotConfigured("PROXY_URL not set in settings.py") return cls(proxy_url) def process_request(self, request, spider): if "proxy" not in request.meta: request.meta["proxy"] = self.proxy_url return None ``` Scrapy's built-in `HttpProxyMiddleware` reads `request.meta['proxy']` and automatically extracts the credentials from the URL to create the `Proxy-Authorization` header. ### Step 3: Enable the Middleware in `settings.py` ```python DOWNLOADER_MIDDLEWARES = { "your_project_name.middlewares.EvomiProxyMiddleware": 350, } ``` Replace `your_project_name` with the actual name of your Scrapy project module. ## Example Spider Create a test spider in your `spiders/` directory: ```python import scrapy class IPCheckSpider(scrapy.Spider): name = "ipcheck" start_urls = ["https://ip.evomi.com/s"] def parse(self, response): self.logger.info(f"Proxy IP: {response.text.strip()}") yield {"ip": response.text.strip()} ``` Run it with: ```bash scrapy crawl ipcheck ``` The output should show the Evomi proxy IP, not your real IP. ## Using SOCKS5 For SOCKS5 proxies, install `scrapy-socks`: ```bash pip install scrapy-socks ``` Then update `settings.py`: ```python PROXY_HOST = "rp.evomi.com" PROXY_PORT = "1002" PROXY_USER = "your_username" PROXY_PASS = "your_password_session-anychars_mode-speed" PROXY_URL = f"socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}" DOWNLOAD_HANDLERS = { "http": "scrapy_socks.handlers.http.SOCKSDownloadHandler", "https": "scrapy_socks.handlers.http.SOCKSDownloadHandler", } ``` The `socks5h://` scheme routes DNS resolution through the proxy server. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Tips and Troubleshooting - **Credentials**: Verify `PROXY_USER`, `PROXY_PASS`, `PROXY_HOST`, and `PROXY_PORT` in `settings.py`. Incorrect values are the most common source of errors. - **Password format**: The password `your_password_session-anychars_mode-speed` includes session parameters. Replace `your_password` with your actual password but keep the `_session-` and `_mode-` parts intact. - **Project name**: Make sure `your_project_name` in `DOWNLOADER_MIDDLEWARES` matches your actual Scrapy project module name. - **User-Agent**: Always set a realistic `USER_AGENT` in settings. Scrapy's default User-Agent is frequently blocked. - **Debug logging**: Set `LOG_LEVEL = "DEBUG"` in `settings.py` for detailed request/response output. - **Per-request proxy override**: To use a different proxy for a specific request, set `request.meta["proxy"]` directly in the spider's `start_requests()` or callback methods. - **Dynamic content**: Scrapy fetches raw HTML and does not execute JavaScript. For JS-heavy sites, consider `scrapy-playwright` which integrates Playwright with Scrapy. --- ## Selenium Source: https://docs.evomi.com/proxy-instructions/integration-guides/selenium/ This guide explains how to use Evomi proxies with Selenium for browser automation in Python. Since Selenium does not natively support authenticated proxies, we use a lightweight Chrome extension to handle proxy credentials automatically. ## Prerequisites - Python 3.8+ installed - Google Chrome installed - Your Evomi proxy credentials (username and password) ## Installation Install Selenium using pip: ```bash pip install selenium ``` You do **not** need to download ChromeDriver manually. Since Selenium 4.6+, Selenium Manager automatically downloads and manages the correct driver version for your installed Chrome browser. ## Configuration Standard Selenium can set a proxy via `--proxy-server`, but it does not support username/password authentication. To work around this, we dynamically create a small Chrome extension that injects the credentials. ```python import zipfile import os import tempfile from selenium import webdriver from selenium.webdriver.chrome.options import Options proxy_host = "rp.evomi.com" proxy_port = 1000 proxy_user = "your_username" proxy_pass = "your_password_session-anychars_mode-speed" manifest_json = """{ "version": "1.0.0", "manifest_version": 3, "name": "Evomi Proxy Auth", "permissions": ["proxy", "webRequest", "webRequestAuthProvider"], "host_permissions": [""], "background": {"service_worker": "background.js"} }""" background_js = """ chrome.proxy.settings.set({ value: { mode: "fixed_servers", rules: { singleProxy: { scheme: "http", host: "%s", port: %d }, bypassList: ["localhost", "127.0.0.1"] } }, scope: "regular" }); chrome.webRequest.onAuthRequired.addListener( function(details) { return { authCredentials: { username: "%s", password: "%s" } }; }, { urls: [""] }, ["blocking"] ); """ % (proxy_host, proxy_port, proxy_user, proxy_pass) plugin_dir = tempfile.mkdtemp() plugin_file = os.path.join(plugin_dir, "evomi_proxy.zip") with zipfile.ZipFile(plugin_file, "w") as zp: zp.writestr("manifest.json", manifest_json) zp.writestr("background.js", background_js) chrome_options = Options() chrome_options.add_extension(plugin_file) driver = webdriver.Chrome(options=chrome_options) driver.get("https://ip.evomi.com/s") print(driver.find_element("tag name", "body").text) driver.quit() os.remove(plugin_file) os.rmdir(plugin_dir) ``` Replace `your_username` with your Evomi username and `your_password` with your actual password (keeping the `_session-anychars_mode-speed` parameters). ## Explanation 1. **Manifest V3 extension**: We create a minimal Chrome extension using Manifest V3 (the current Chrome extension standard). It requests `proxy`, `webRequest`, and `webRequestAuthProvider` permissions. 2. **Proxy configuration**: The `background.js` service worker calls `chrome.proxy.settings.set()` to route all traffic through the Evomi proxy. 3. **Authentication**: The `onAuthRequired` listener intercepts proxy authentication challenges and automatically supplies your credentials. 4. **Extension loading**: The extension is packaged as a ZIP file and loaded into Chrome via `chrome_options.add_extension()`. 5. **Verification**: We navigate to `https://ip.evomi.com/s` which returns the IP address seen by the server -- this should be the proxy IP. 6. **Cleanup**: The temporary extension file is removed after the driver quits. ## Using SOCKS5 For SOCKS5 proxies, modify the `singleProxy` configuration in `background_js`: ```python background_js = """ chrome.proxy.settings.set({ value: { mode: "fixed_servers", rules: { singleProxy: { scheme: "socks5", host: "%s", port: %d }, bypassList: ["localhost", "127.0.0.1"] } }, scope: "regular" }); // ... rest of the onAuthRequired listener stays the same """ ``` ## Headless Mode The extension-based approach does **not** work with `--headless=new` because Chrome's headless mode does not support extensions. If you need headless operation with an authenticated proxy, consider using Playwright instead (which has built-in proxy authentication support). ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Running the Script Save the script (e.g., `selenium_proxy_test.py`) and run: ```bash python selenium_proxy_test.py ``` A Chrome window will open, navigate through the proxy, and print the proxy IP to your console. ## Tips and Troubleshooting - **Credentials**: Double-check your proxy host, port, username, and password. The password format is `your_password_session-anychars_mode-speed`. - **Chrome version**: Ensure you have a recent version of Chrome installed. The Manifest V3 extension approach requires Chrome 105+. - **Extension disabled flag**: If Chrome blocks the extension, you may need to add `chrome_options.add_argument("--disable-features=DisableLoadExtensionCommandLineSwitch")`. - **Firewall/antivirus**: Some security software may interfere with the proxy extension. Add Chrome as an exception in your security software or temporarily disable it to test. - **Alternative -- SeleniumBase**: The [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) framework has built-in proxy authentication support and can be a simpler option if you are starting a new project. --- ## Proxifier on Windows Source: https://docs.evomi.com/proxy-instructions/integration-guides/windows-proxifier/ This guide explains how to configure Proxifier on Windows to route application traffic through Evomi proxies. Proxifier intercepts network connections from any application and tunnels them through a proxy server without requiring the application to have native proxy support. ## Prerequisites - **Proxifier** installed on your Windows machine - An active Evomi proxy subscription - Your Evomi proxy credentials (username and password) ## Step-by-Step Configuration ### 1. Launch Proxifier Open Proxifier from the Start menu or desktop shortcut. ### 2. Add a New Proxy Server - Go to **Profile** > **Proxy Servers...**. - Click the **Add** button. ### 3. Enter Evomi Proxy Details In the **Proxy Server** window: - **Address**: `rp.evomi.com` - **Port**: `1002` (for SOCKS5) or `1000` (for HTTP) - **Protocol**: Select **SOCKS Version 5** or **HTTP** to match the port ### 4. Enable Authentication - Check **Enable** under **Authentication**. - Enter your Evomi **Username** and **Password**. Your password should include your desired session and mode parameters: ``` yourpassword_session-123456_mode-quality ``` Replace `yourpassword` with your actual password and `123456` with any session identifier. ### 5. Test the Proxy Server - Click the **Check** button to verify connectivity. - If the test succeeds, click **OK** to save. ### 6. Configure Proxification Rules - Go to **Profile** > **Proxification Rules...**. - Create a new rule or modify an existing one: - **Applications**: Specify the applications to route through the proxy - **Target Hosts**: Define specific domains or IP addresses (or leave as any) - **Action**: Select **Proxy** and choose the Evomi proxy you added - Click **OK** to save. ### 7. Verify - Open a browser and navigate to `https://ip.evomi.com/s`. - The displayed IP should be the proxy IP, not your real IP. ## Evomi Proxy Endpoints | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ## Proxy Modes Evomi residential proxies support different modes to optimize for your use case. Append the mode parameter to your password: Mode selection is only available for residential proxies. | Mode | Password Suffix | Pool Size | Speed | Quality | Best For | |---|---|---|---|---|---| | **Standard** | *(default, no suffix)* | Largest | Good | Good | General scraping, large IP pool needs | | **Speed** | `_mode-speed` | Medium | Best | Good | Time-sensitive tasks, large data transfers | | **Quality** | `_mode-quality` | Smallest | Best | Best | Strict sites, avoiding CAPTCHAs | **Example password with quality mode:** ``` mysecretpassword_session-abc123_mode-quality ``` ## Tips and Troubleshooting - **Incorrect credentials**: Double-check your username and password, including the `_mode-` parameter. - **Connection issues**: Ensure your firewall or antivirus is not blocking Proxifier. - **Authentication failed**: Verify the password includes the correct mode suffix. - **Switching modes**: Change the `_mode-` parameter in your password (`mode-speed`, `mode-standard`, `mode-quality`). - **Restart applications**: After changing proxy settings, restart applications to ensure they pick up the new configuration. --- ## Mobile Proxies Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/ Evomi offers a robust network of 3 million+ 3G/4G/5G mobile proxies sourced ethically from volunteer nodes. These proxies provide: - Real mobile connections with average 0.5s response times - 99.95% connection success rate - Global coverage across 150+ countries - Support for HTTP, HTTPS, and SOCKS5 protocols - Ability to target specific cities, states, or countries - Reliable access for regional testing and data collection workflows Every mobile proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At the $3.98 per GB pay-as-you-go mobile rate, that minimum is about $0.00000203776 per request, falling to $0.0000011264 at the $2.20 volume rate. Our mobile proxies are ideal for tasks requiring high anonymity and reliability, such as web scraping, ad verification, and market research. --- ## Mobile Proxies Geo Targetting Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/ Geo-targeting allows you to specify the geographic location of the proxy you want to use. This can be essential for tasks like localized testing, content access, and more. Below, we'll explain the key geo-targeting parameters and show you how to use them effectively. ## Geo-targeting Parameters - **_country-**: Specify the country of the proxy. - **_region-**: Specify the region of the proxy. - **_city-**: Specify the city of the proxy. - **_isp-**: Specify the mobile carrier of the proxy. - **_continent-**: Specify the continent of the proxy. ## Choosing the Geo Source The parameters above are resolved by a geolocation database, and you can choose which one. By default Evomi uses `ipapi` ([ip-api.com](https://ip-api.com)). Adding `_geosource-` selects a different source, which works for Mobile proxies too: ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-GB_geosource-maxmind https://ip.evomi.com/s ``` Sources disagree about individual IPs, so a filter that matches plenty of them on one source may match fewer — or none — on another. See [Geo Source Selection](/proxy-instructions/residential-proxies/expert-settings/geosource/) for the available sources and why ISP values differ between them. --- ## Mobile Proxies City Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/city/ You can target a specific city by using the `city` parameter in your proxy string. This parameter allows you to connect to a proxy located in a specific city. You can specify any city in the proxy string. If a city uses a space in its name, replace the space with a dot (e.g., `New York` becomes `new.york`). Make sure to spell the city name in lowercase. If a city is not available our proxy servers will respond with a 412 error code. ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_city-new.york https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_city-berlin https://ip.evomi.com/s ``` Note that you can find all available cities in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Mobile Proxies Continent Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/continent/ You can target a specific continent by using the `continent` parameter in the proxy URL. The available continents are `africa`, `asia`, `europe`, `north.america`, `oceania`, and `south.america`. ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_continent-asia https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_continent-europe https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_continent-africa https://ip.evomi.com/s ``` Note that you can find all available continents in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Mobile Proxies Country Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/country/ You can target a specific country by using the `country` parameter in your proxy string. This parameter allows you to connect to a proxy located in a specific country. You can specify any country in the ISO format (e.g., `US` for the United States, `CA` for Canada, etc.). For some countries the full name is supported aswell (e.g., `UnitedStates` for the United States, `Canada` for Canada, etc.). ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-US https://ip.evomi.com/s ``` Note that you can find all available countries in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Mobile Proxies ISP Targeting Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/isp/ You can target a specific mobile carrier using the `isp` parameter. This can be useful for tasks like testing content delivery, ad verification, and more. We keep a list of carriers that you can target which gets updated frequently. You must use specific shortcodes for each carrier. On the mobile endpoint only mobile network operators are targetable — a fixed-line or cable ISP shortcode will not match any IP in the mobile pool. Combine `isp` with `country` to pin a carrier to the market you mean, since the same brand operates under separate networks in different countries. Shortcodes are the carrier's identifier in our network, not its consumer brand name, so copy them exactly as they appear rather than guessing from the operator's marketing name. `vodafone`, `oonline`, `sunrisegmbh` and `tmobileusainc` are all valid examples: ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_isp-vodafone https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_isp-sunrisegmbh https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_isp-oonline https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-US_isp-tmobileusainc https://ip.evomi.com/s ``` Note that you can find all available carriers in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). The list is generated from the live pool, so it is the only authoritative source of shortcodes. --- ## Mobile Proxies Region (State) Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/geo-targetting/region/ You can target a specific region (state) by specifying the `region` in the proxy string. Regions can be things like states, provinces, or territories depending on the country. Valid examples are `california`or `southern.cape`. ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_region-california https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_region-southern.cape https://ip.evomi.com/s ``` Note that you can find all available regions in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Mobile Proxies How to Connect Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/how-to-connect/ This page is a basic guide with information about Mobile Proxies and how to connect to them. ## Endpoint and Port Information To connect to the evomi proxy system with Mobile proxies, you need to use the following endpoint and port configurations: | Proxy Protocol | Mobile Endpoint | Mobile Port | |--------------|---------------|--------| | HTTP Proxy | mp.evomi.com | 3000 | | HTTPs Proxy | mp.evomi-proxy.com | 3001 | | SOCKS5 Proxy | mp.evomi.com | 3002 | The HTTPs endpoint uses the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject `mp.evomi.com` on port 3001. The difference between the HTTP and HTTPs proxies is that the HTTPs proxy is encrypted, while the HTTP proxy is not. The SOCKS5 proxy is a more advanced protocol that can handle more types of traffic. The HTTP protocol works for HTTPs requests, you do not need to use the HTTPs protocol for HTTPs requests. The HTTPs protocol is only necessary if you need to connect to a secure HTTPs server and is generally slower than the HTTP protocol. Please note that depending on your protocol, you will need to use the correct port number. Every mobile proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At the $3.98 per GB pay-as-you-go mobile rate, that minimum is about $0.00000203776 per request, falling to $0.0000011264 at the $2.20 volume rate. ## Basic Connection Instructions Here are step-by-step instructions to help you connect using different programming languages and tools. ### CURL To connect using CURL, use the following command format: ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword https://ip.evomi.com/s ``` ### Python To connect using Python with the `requests` library, follow these steps: 1. Install the `requests` library if you haven't already: ```bash pip install requests ``` 2. Use the following script: ```python import requests proxy_pass = "testpassword" proxy_url = f"http://testuser:{proxy_pass}@mp.evomi.com:3000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` ### JavaScript To connect using JavaScript, particularly with Node.js, follow these steps: 1. Install the `node-fetch` and `http-proxy-agent` packages if you haven't already: ```bash npm install node-fetch http-proxy-agent ``` 2. Use the following script: ```javascript const fetch = require('node-fetch'); const HttpsProxyAgent = require('https-proxy-agent'); const proxyPass = 'testpassword'; const proxyUrl = `http://testuser:${proxyPass}@mp.evomi.com:3000`; const agent = new HttpsProxyAgent(proxyUrl); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log(body)) .catch(err => console.error('Error:', err)); ``` Note that you can get an easy to use interface to configure this for you, using our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Mobile Proxies Proxy Sessions Source: https://docs.evomi.com/proxy-instructions/mobile-proxies/proxy-sessions/ By default, each request to our proxy server generates a new IP address. However, you can use session parameters to maintain the same IP address for multiple requests. This feature is useful for tasks that require a stable connection, such as web resources with session-based authentication. ![Sticky Session Demo](/images/session-demo.gif) ## Session Parameters We offer three types of session parameters: `session`, `hardsession`, and `lockedsession`. All ensure you keep the same IP for all following requests, but they have different behaviors and use cases. ### The `session` parameter - A unique alphanumeric string, 6 to 10 characters long. - Optimized for the highest overall success rate. - May change IP address in favor of stability. - Works with the `lifetime` parameter. ### The `hardsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Prioritizes IP address longevity. - Keeps the same IP for as long as possible. - Does not work with the `lifetime` parameter. ### The `lockedsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Locks traffic to one specific IP. - Never switches to a replacement IP if that peer disconnects. - If the bound IP goes offline, the request fails instead of assigning a new IP. - Does not work with the `lifetime` parameter. When specifying any geo or ISP targeting parameters, the first request will use the specified parameters, and all subsequent requests will use the same IP address. If you change the targeting while maintaining the same session, the IP address will remain the same. ### The `lifetime` parameter - Specifies the duration for the `session` in minutes. - If not specified, the session will last for 30 minutes by default. - Maximum value is 1440 minutes, which is 24 hours. - After the specified time, the IP address will change. - Does not apply to `hardsession` or `lockedsession`. If you specify a higher `lifetime` value than the maximum allowed, the request will return a 412 error code. ## Examples Below are examples of using Sticky sessions in different programming languages: CURL, Python, and JavaScript. #### CURL ```bash curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-US_session-sgn34f3e_lifetime-10 https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-US_hardsession-JS9nsq2n https://ip.evomi.com/s curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-US_lockedsession-k8m2pQ1x https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_session-sgn34f3e_lifetime-10" proxy_url = f"http://testuser:{proxy_pass}@mp.evomi.com:3000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_hardsession-JS9nsq2n" proxy_url = f"http://testuser:{proxy_pass}@mp.evomi.com:3000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_lockedsession-k8m2pQ1x" proxy_url = f"http://testuser:{proxy_pass}@mp.evomi.com:3000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_country-US_session-sgn34f3e_lifetime-10'; const proxyUrl = `http://testuser:${proxyPass}@mp.evomi.com:3000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass2 = 'testpassword_country-US_hardsession-JS9nsq2n'; const proxyUrl2 = `http://testuser:${proxyPass2}@mp.evomi.com:3000`; fetch(url, { proxy: proxyUrl2 }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass3 = 'testpassword_country-US_lockedsession-k8m2pQ1x'; const proxyUrl3 = `http://testuser:${proxyPass3}@mp.evomi.com:3000`; fetch(url, { proxy: proxyUrl3 }) .then(res => res.text()) .then(body => console.log(body)); ``` Note our easy to use Proxy Generator can generate all this for you. --- ## Proxy Generator Source: https://docs.evomi.com/proxy-instructions/proxy-generator/ Our proxy generator is available for all proxy products and lets you generate a list of proxies for your usecase. You can do all this without any code or special knowledge. To use the proxy generator, follow these steps: ## Create an account It is necessary to have an account to use the proxy generator. If you don't have one yet, you can [create an Evomi account](https://my.evomi.com). ## Select the product you wish to generate proxies for ![My Product](/images/my-product.png) ## Navigate to the proxy generator tab ![Proxy Generator](/images/proxy-generator.png) ## Select the parameters you wish to use Et voilà! You can now change all the settings to your desired parameters and the proxies on the right will update accordingly. You can also copy the proxy string to your clipboard by clicking the copy button. ![Proxy Settings](/images/proxy-settings.png) --- ## Proxy Protocols Source: https://docs.evomi.com/proxy-instructions/proxy-protocols/ Understanding the differences between various proxy protocols is essential for optimizing your network tasks. This page explains the differences between HTTP, HTTPS, and SOCKS5 proxies, and guides you on when to use each. ## Proxy Protocols Overview ### HTTP Proxy HTTP proxies are designed to handle HTTP traffic. They are widely used for web scraping, accessing HTTP websites, and general web browsing. HTTP proxies can also handle HTTPS traffic through the HTTP CONNECT method, allowing you to access secure websites even when using an HTTP proxy. **Key Features:** - Supports HTTP and HTTPS traffic - Commonly used for web scraping and browsing - No encryption between client and proxy ### HTTPS Proxy HTTPS proxies add an extra layer of encryption between the client and the proxy server. While they also support both HTTP and HTTPS traffic, the communication between your device and the proxy is encrypted, providing enhanced security. **Key Features:** - Encrypted connection between client and proxy - Supports HTTP and HTTPS traffic - Slightly slower due to encryption overhead **Note:** You do not need an HTTPS proxy to access HTTPS websites. An HTTP proxy can handle HTTPS traffic perfectly fine, and is faster! ### SOCKS5 Proxy SOCKS5 proxies operate at a lower level and can handle any type of traffic, not just HTTP or HTTPS. They are more versatile and can be used for various types of network connections. **Key Features:** - Supports any TCP traffic - More versatile than HTTP/HTTPS proxies - No encryption by default - Does not support UDP traffic (in our implementation) **Important:** Our SOCKS5 proxies support only TCP traffic. UDP is not supported. ## When to Use Each Protocol | Protocol | Use Case | Advantages | Considerations | |----------|----------|------------|----------------| | **HTTP Proxy** | - General web browsing
- Web scraping of HTTP/HTTPS websites | - Simple to use
- Supports HTTP and HTTPS traffic | - No encryption between client and proxy | | **HTTPS Proxy** | - Secure communication between client and proxy
- When extra security is required | - Encrypted connection
- Supports HTTP and HTTPS traffic | - Slightly slower due to encryption overhead | | **SOCKS5 Proxy** | - Non-HTTP protocols
- Applications requiring TCP traffic
- Versatile network tasks | - Supports any TCP traffic
- More flexible | - No encryption by default
- Does not support UDP traffic in our implementation | ### Guidelines for Choosing a Proxy Protocol 1. **Use HTTP Proxy when:** - You need to access HTTP or HTTPS websites. - Encryption between your device and the proxy is not a concern. - You require a straightforward setup for web scraping or browsing. 2. **Use HTTPS Proxy when:** - You need encrypted communication between your device and the proxy. - You are handling sensitive data and require additional security. - You are willing to trade a bit of speed for encryption. 3. **Use SOCKS5 Proxy when:** - You need to proxy non-HTTP protocols over TCP. - You require a versatile proxy that can handle various types of TCP traffic. - You understand that UDP traffic is not supported in our SOCKS5 proxies. **Tip:** For most HTTP and HTTPS traffic, an HTTP proxy is sufficient. You don't need an HTTPS proxy to access HTTPS websites. ## Additional Considerations - **Performance:** HTTP proxies generally offer better performance due to less overhead compared to HTTPS proxies. - **Security:** HTTPS proxies provide encrypted connections, which are beneficial when transmitting sensitive data. - **Compatibility:** SOCKS5 proxies are compatible with a wide range of applications but require proper configuration. ## How to Configure Your Proxy Configuration steps will vary depending on the application or programming language you're using. Generally, you'll need to specify: - **Proxy Protocol (HTTP, HTTPS, or SOCKS5)** - **Endpoint and Port:** from the table below - **Authentication Credentials:** Your proxy username and password | Proxy Type | HTTP | HTTPS | SOCKS5 | |---|---|---|---| | **Residential** | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | **Mobile** | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | | **Datacenter** | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. **Example, using residential:** - **HTTP Proxy Connection:** - **Endpoint:** `rp.evomi.com` - **Port:** `1000` - **Protocol:** `HTTP` - **HTTPS Proxy Connection:** - **Endpoint:** `rp.evomi-proxy.com` - **Port:** `1001` - **Protocol:** `HTTPS` - **SOCKS5 Proxy Connection:** - **Endpoint:** `rp.evomi.com` - **Port:** `1002` - **Protocol:** `SOCKS5` **Note:** Ensure you use the correct port number for the proxy protocol you are configuring. ## Conclusion Choosing the right proxy protocol depends on your specific needs. For most web-related tasks, an HTTP proxy will suffice. If you require encrypted communication between your device and the proxy, consider using an HTTPS proxy. For more versatile needs that involve different types of TCP traffic, a SOCKS5 proxy is appropriate, keeping in mind that UDP traffic is not supported in our implementation. --- ## Residential Proxies Source: https://docs.evomi.com/proxy-instructions/residential-proxies/ Evomi offers high-quality residential proxies sourced ethically from a global network of volunteer nodes. Our proxies provide: - Access to IPs from 150+ countries - Average response times of <0.5s - 99.95% connection success rate - Flexible geo-targeting options (country/state/city) - Support for HTTP, HTTPs, and SOCKS5 protocols - Automatic IP rotation and sticky session capabilities Every residential proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At $0.49 per GB, the Core Residential rate on the 100 GB plan, that minimum is about $0.00000025088 per request. With features like unlimited threads, dedicated support, and easy integration, Evomi residential proxies are ideal for various use cases including web scraping, ad verification, and market research. --- ## Expert Settings Overview Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/ Evomi Residential Proxies offer advanced filtering parameters to fine-tune your proxy connections. These expert settings enable precise IP selection but incur **additional bandwidth consumption** due to increased filtering complexity. **Note:** Expert settings are only available for **Core Residential Proxies**. ## Bandwidth Multiplier System Each expert parameter adds a multiplier to your bandwidth usage: - **Per-parameter multipliers**: Added cumulatively - **Maximum cap**: 15x total multiplier ```math Total Bandwidth Deducted = Actual Bandwidth × (Σ Multipliers) ``` ### Example Calculation | Parameters Used | Total Multiplier | 1MB Transfer Cost | |--------------------------|------------------|-------------------| | 1 Parameter (4x) | 4x | 4MB | | 1 Parameter1 (2x) + Parameter2 (3x) | 5x | 5MB | | 5 parameters totaling 20x | 15x (capped) | 15MB | **Critical Implementation Notes** 1. Multipliers apply **per request** 2. Combination order doesn't affect calculation 3. Dashboard shows actual vs. multiplied usage breakdown 4. Enterprise customers may receive customized multiplier values ## Available Expert Parameters Use these filters by appending them to your proxy password: | Parameter | Format | Description | |--------------------------|---------------------------------|-----------------------------------------------------------------------------| | **Latency Filter** | `_latency-` | Maximum network latency (e.g., `_latency-500`) | | **Fraudscore Filter** | `_fraudscore-` | Maximum scamalytics.com score (e.g., `_fraudscore-10`) | | **Device Filter** | `_device-` | OS/device type: `windows`, `unix`, or `apple` | | **Connection Stability** | `_activesince-` | Minimum IP connection duration (e.g., `_activesince-120`) | | **ISP Filter** | `_isp-` | Specific internet provider (e.g., `_isp-comcast`) | | **ASN Filter** | `_asn-` | Autonomous System Number filter (e.g., `_asn-AS7922`) | | **Zipcode Targeting** | `_zip-` | Specific postal code area (e.g., `_zip-90210`) | | **HTTP3/QUIC Support** | `_http3-1` | Enable modern HTTP3/QUIC protocol compatibility | | **UDP Protocol Support** | `_udp-1` | Full UDP protocol support (Enterprise only - manual activation required) | | **Extended Pool** | `_extended-1` | Access 4-6x larger IP pool (cannot combine with other expert filters) | | **Geo Source** | `_geosource-` | Database used to resolve geo filters, default `ipapi` (no multiplier) | | **Response Caching** | `_cache-` | Cache static assets, e.g. `_cache-images` (requires certificate setup) | ## Implementation Example ```bash # Combined filters: fraudscore + device + latency curl -x http://rp.evomi.com:1000 -U testuser:testpassword_fraudscore-5_device-windows_latency-100 https://ip.evomi.com/s ``` **Dashboard Monitoring** Your account dashboard shows: - Actual bandwidth consumed - Multiplier-induced extra usage - Real-time multiplier values **Optimization Tips** 1. Start with 1-2 critical filters 2. Monitor success rates vs bandwidth costs 3. Use `extended-1` for large-scale operations 4. Combine geographic filters with quality parameters --- ## Ad Blocking Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/adblocking/ Evomi Residential Proxies offer a built-in ad blocking feature that allows you to block ads and trackers on the websites you visit. This feature is useful when doing any browser-based tasks. Enabling this feature can save you time and bandwidth by blocking unnecessary content. Most popular ad networks will just not load when using this feature. ![The Speedtest.net homepage loaded through a residential proxy with ad blocking enabled, showing the page content with none of its usual ad banners](/images/adblocking.png) ## Enabling Ad Blocking To enable ad blocking, add the `adblock-1` parameter to your proxy password. ## Examples Below are examples of using The inbuilt Adblock Funcionality in different programming languages: CURL, Python, and JavaScript. #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_session-sgn34f3e_lifetime-10_adblock-1 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_session-sgn34f3e_lifetime-10_adblock-1" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_hardsession-JS9nsq2n_adblock-1" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_country-US_session-sgn34f3e_lifetime-10'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass2 = 'testpassword_country-US_hardsession-JS9nsq2n'; const proxyUrl2 = `http://testuser:${proxyPass2}@rp.evomi.com:1000`; fetch(url, { proxy: proxyUrl2 }) .then(res => res.text()) .then(body => console.log(body)); ``` Note our easy to use Proxy Generator can generate all this for you. --- ## ASN Targeting Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/asn/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer ASN (Autonomous System Number) targeting for advanced network-level control. This enterprise feature enables connections through specific network infrastructures identified by their unique ASN. ## Implementation Append `_asn-` to your proxy password using the numeric ASN format: ```bash http://testuser:testpassword_asn-AS23525@rp.evomi.com:1000 ``` ## Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_asn-AS15169 https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-DE_asn-AS3320 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_asn-AS36561" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_asn-AS23525'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Common Use Cases - Network infrastructure analysis - ISP backbone testing - Corporate network simulations - DDoS mitigation testing - Content delivery network validation **Note:** The asn parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Advanced Configuration** Combine with ISP targeting for hyper-specific routing: `_asn-AS1234_isp-comcast` `_country-CA_asn-AS5678` `_region-CA_QC_asn-AS9012` --- ## Response Caching Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/caching/ **Note:** Expert settings are only available for **Core Residential Proxies**. Static assets are the bulk of most page loads and rarely change between requests. Response caching lets Evomi store those assets — scripts, stylesheets, images, fonts — and serve them from our infrastructure on subsequent requests instead of fetching them through a residential IP again. Pages render faster and the target site sees far fewer requests from you. Caching is opt-in per request. Append `_cache-` to your proxy password and choose which file types it should apply to. **HTTPS needs one change on your side** To store a response the proxy has to read it, so HTTPS on a cache-enabled password is re-signed with the Evomi Proxy CA rather than tunnelled untouched. Your client rejects that certificate by default, and requests fail during the TLS handshake until you do one of two things: - **Skip certificate verification** — one flag in your client, such as `curl -k`, `verify=False` or `--ignore-certificate-errors`. This is what most users do. - **Trust the Evomi CA** — a one-off install that keeps certificate verification switched on. Either works. [Proxy Certificate](/proxy-instructions/integration-guides/proxy-certificate/) covers both, with copy-paste snippets per language. ## Implementation Append `_cache-` to your proxy password, where `` is a comma-separated list of file extensions, one of the shorthand groups, or `all`: ```bash http://testuser:testpassword_cache-images@rp.evomi.com:1000 ``` ### Cacheable Types | Value | Expands to | |-------------|-----------------------------------------| | `images` | `png`, `jpg`, `jpeg`, `gif`, `svg`, `ico`, `webp` | | `scripts` | `js`, `css`, `wasm`, `map` | | `fonts` | `woff`, `woff2`, `ttf`, `eot`, `otf` | | `all` | Every type listed above | You can also name extensions individually and mix them with groups, so `_cache-images,js,woff2` and `_cache-png,jpg` are both valid. Only the extensions in the table are supported — `all` means all supported types, not every file on the page, so documents, media and API responses are never cached. **An unsupported type disables caching silently** If your list contains anything outside the table above, such as `_cache-html` or `_cache-mp4`, the whole list is rejected and the request continues as a normal uncached proxy request. It still succeeds, so a password that appears to do nothing is usually a typo in the type list. `_cache-` with no value behaves the same way. ## Cache Duration Entries live for **120 minutes** by default. Override this with `_cacheduration-`: ```bash http://testuser:testpassword_cache-all_cacheduration-30@rp.evomi.com:1000 ``` The origin still has a say. When the response carries a `Cache-Control: max-age`, the **shorter** of the two values wins, so a site that declares `max-age=60` caps the entry at one minute no matter how high you set `_cacheduration-`. A value below `1` or any non-numeric value is ignored and leaves the default in place. More importantly, a response with **no** `Cache-Control` header at all is not cached. Many sites omit it on static assets, so a correct-looking `_cache-` password can still produce no cache hits. ### Ignoring the Origin's Headers `_cacheignoreheader-1` caches responses regardless of what `Cache-Control` says, including assets with no header at all and assets marked `no-store`, `no-cache` or `private`. Your `_cacheduration-` value then applies verbatim: ```bash http://testuser:testpassword_cache-scripts_cacheduration-60_cacheignoreheader-1@rp.evomi.com:1000 ``` **The cache is shared, so only use this on public assets** Cached entries are keyed by URL and shared across accounts, the same way a CDN works. That is safe for public static assets, which are byte-identical for everyone who requests them. It is not safe for anything private. `_cacheignoreheader-1` overrides the `no-store`, `no-cache` and `private` directives that a site uses to mark a response as non-shareable, so using it on assets behind a login can place them in a cache other accounts read. Restrict it to public, unauthenticated assets, and leave it off when in doubt — responses that set a cookie are never cached under any setting, but that guard alone will not catch every private asset. ## What Gets Cached A response is only stored when **all** of the following hold: - The request is `GET` over HTTPS - The response status is `200` - The URL path ends in one of your selected extensions - The response has no `Set-Cookie` header - The response permits caching, or `_cacheignoreheader-1` is set Never cached, regardless of configuration: - Anything served over plain `http://`, or through the SOCKS5 endpoint — caching applies to HTTPS via the HTTP proxy endpoint only - `sw.js` and `service-worker.js`, since a stale service worker would outlive the cache entry - URLs with no file extension in the path, including most API endpoints - Responses that set a cookie Entries are matched on the full URL, including the query string, so versioned asset URLs such as `/app.js?v=1` and `/app.js?v=2` are stored separately and a new version is never served from an older entry. Matching is exact rather than normalised: if your client varies the order of query parameters between requests, each ordering is cached under its own entry. ## Bandwidth and Billing A cache hit skips the residential IP entirely and is served straight from our infrastructure, which is where the speed benefit comes from. It is still **billed as normal traffic** at your usual rate: caching reduces latency and the load you place on the target site, not the bandwidth deducted from your plan. `_cache-` itself carries no bandwidth multiplier. Any multiplier you see comes from the geo or expert filters you pair it with, at their usual rates. ## Examples #### CURL ```bash # Cache images and fonts, skipping certificate verification curl -k -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-images,fonts https://ip.evomi.com/s # Cache everything supported for 30 minutes, trusting the CA instead curl --cacert evomi.crt -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all_cacheduration-30 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_cache-scripts,images" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "https": proxy_url, } # Or verify="evomi.crt" if you installed the CA response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify=False) print(response.text) ``` #### JavaScript ```JavaScript const puppeteer = require('puppeteer'); // Caching pairs well with browser automation, where one page load // pulls dozens of static assets through the proxy. const browser = await puppeteer.launch({ args: [ '--proxy-server=http://rp.evomi.com:1000', '--ignore-certificate-errors', ], }); const page = await browser.newPage(); await page.authenticate({ username: 'testuser', password: 'testpassword_cache-all', }); await page.goto('https://ip.evomi.com/s'); ``` ## Common Use Cases - Browser automation and scraping, where every page load re-fetches the same assets - Crawling many pages on one site that share a stylesheet, script bundle and icon set - Reducing your request footprint on targets that rate-limit aggressively - Speeding up repeated page loads during test and development runs ## Troubleshooting | Symptom | Likely cause | |-------------------------------------------|--------------------------------------------------------------| | TLS or certificate errors on every request | Your client is still verifying the re-signed certificate — skip verification or trust the CA, see [Proxy Certificate](/proxy-instructions/integration-guides/proxy-certificate/) | | Requests succeed but nothing seems cached | An unsupported extension in the list, or the origin sends no `Cache-Control` — try `_cacheignoreheader-1` on public assets | | Cached response served after the site updated | The entry is still within its duration; there is no manual purge, so shorten `_cacheduration-` for assets that change often | Responses carry no hit-or-miss header today, so the practical way to confirm caching is working is to request the same asset twice and compare response times. **Pro Tip** Start narrow and widen once you have confirmed hits. Fonts and images are the safest and highest-volume wins, while scripts change most often: `_cache-fonts` `_cache-images,fonts` `_cache-all_cacheduration-30` --- ## TCP/IP Device Filter Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/device_filter/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer a TCP/IP device filter that lets you select IPs by TCP fingerprint. This is useful for testing platform-specific behavior and maintaining consistency with a selected device type. ## How TCP Fingerprinting Works The TCP fingerprint is determined by analyzing characteristics of the device's TCP/IP stack, including: - TCP window size - Initial packet sequence numbers - TCP options and their order - Time-to-Live (TTL) values These characteristics vary between operating systems and device types. For example, Windows devices have different default TCP configurations than Unix-based systems or Apple devices. ## Using the Device Filter Append `_device-` to your proxy password to filter IPs by device type. Valid options: - `_device-windows`: Filters IPs with Windows TCP fingerprints - `_device-unix`: Filters IPs from Unix-based systems (Linux/Android) - `_device-apple`: Filters IPs from Apple devices (iOS/macOS) ## Examples Below are implementation examples using CURL, Python, and JavaScript: #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_device-windows https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_device-unix" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_device-apple'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` **Note:** The device parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. Combine device filtering with other parameters like `fraudscore` for precise targeting. Example: `testpassword_country-US_device-windows_fraudscore-0` For quick configuration, use our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/) to generate authenticated proxy URLs with the fraudscore parameter. --- ## Extended IP Pool Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/extended/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer an extended IP pool feature that dramatically expands the available IP address range for high-volume operations. This specialized feature provides **4-6x larger IP pool access** but operates independently of all other expert filtering parameters. **Exclusive Feature** Extended pool cannot be combined with any other expert parameters. Using `_extended-1` disables all other filtering options including geographic targeting, device filters, and quality parameters. ## How Extended Pool Works The standard residential proxy pool contains a curated selection of high-quality IPs that support advanced filtering capabilities. The extended pool adds **4-6 times more IP addresses** by including additional residential endpoints that may not support all expert features but provide massive scale for operations requiring maximum IP diversity. ### Pool Comparison | Pool Type | IP Count | Expert Features | Best For | |-----------|----------|-----------------|----------| | **Standard Pool** | Baseline | All expert filters supported | Quality-focused operations | | **Extended Pool** | 4-6x larger | No expert filters available | Volume-focused operations | ## Implementation Append `_extended-1` to your proxy password to access the extended IP pool: ```bash http://testuser:testpassword_extended-1@rp.evomi.com:1000 ``` ## Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_extended-1 https://ip.evomi.com/s # Note: Geographic targeting still works with extended pool curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_extended-1 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_extended-1" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_extended-1'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Common Use Cases - **Large-Scale Web Scraping**: When you need maximum IP diversity over quality filtering - **High-Volume Data Collection**: Operations requiring thousands of unique IPs - **Load Distribution**: Spreading requests across the largest possible IP range - **Research Projects**: Academic or commercial research requiring extensive IP coverage - **Bulk Operations**: Mass data gathering where quantity trumps precision - **Competitive Intelligence**: Large-scale monitoring across maximum geographic spread ## Limitations and Considerations - **No Expert Filtering**: Cannot use fraudscore, device, latency, ISP, ASN, or other expert parameters - **Quality Variance**: Extended pool may include IPs with varying quality characteristics - **Support Differences**: Some IPs may not support advanced features like HTTP3 or local DNS - **Geographic Targeting**: Basic country/region targeting remains available **When to Use Extended Pool** Choose extended pool when IP quantity and diversity are more important than advanced filtering capabilities. Perfect for operations that need to spread load across maximum IP addresses rather than targeting specific quality metrics. **Note:** The extended parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Optimization Strategy** For hybrid approaches, consider: 1. Use standard pool with expert filters for critical operations 2. Use extended pool for bulk data collection 3. Combine both approaches in different parts of your workflow 4. Monitor success rates and adjust pool selection accordingly --- ## Fraudscore Parameter Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/fraudscore/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer a fraudscore parameter that allows you to filter IPs based on their [Scamalytics](https://scamalytics.com) fraudscore. By appending this parameter to your proxy password, you can specify the maximum fraudscore allowed for the IPs used in your requests. This helps in accessing targets that are sensitive to IP reputation. ## Using the Fraudscore Parameter To use the fraudscore parameter, append `_fraudscore-` to your proxy password, replacing `` with the desired maximum fraudscore. For example, `_fraudscore-0` will only allow IPs with a fraudscore of 0 or lower. ## Examples Below are examples of using the fraudscore parameter in different programming languages: CURL, Python, and JavaScript. #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_fraudscore-0 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_fraudscore-0" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_fraudscore-0'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` **Note:** The fraudscore parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. For quick configuration, use our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/) to generate authenticated proxy URLs with the fraudscore parameter. --- ## Geo Source Selection Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/geosource/ Every geo-targeting parameter you send — country, region, city, ASN, ISP, zipcode — is resolved by looking the IP up in a geolocation database. Which database that is makes a real difference: providers disagree about where a given IP sits and which network owns it, so the same `_city-london` filter can select a different set of IPs depending on the source used to answer it. The `_geosource-` parameter lets you choose. Evomi resolves geo filters against `ipapi` unless you say otherwise, and additional sources can be added over time without changing how the parameter works. ## Available Geo Sources | Value | Data source | Default | |-----------|-------------------------------------|---------| | `ipapi` | [ip-api.com](https://ip-api.com) | Yes | | `maxmind` | Alternative geolocation database | No | More sources may be added to this table in future. Until then, `_geosource-` accepts only the values above. **A filter that works on one source may match nothing on another** Changing the source changes which IPs satisfy your filter, not just how they are labelled. Sources disagree about individual IPs and cover different numbers of them, so a filter that returns plenty of results on one source can return far fewer — or none — on another. Evomi will only serve an IP that matches in the source you asked for; it will not substitute one that matches a different source. A filter nothing can satisfy therefore fails rather than returning a mismatched IP. Narrow filters like city, zipcode and ASN are the most exposed to this, so keep a retry in place and be ready to widen the filter. ## Implementation Append `_geosource-` to your proxy password, alongside the geo filters it should apply to: ```bash http://testuser:testpassword_country-DE_geosource-maxmind@rp.evomi.com:1000 ``` The parameter only changes how filters are resolved — it never selects a location on its own. Sent without any geo filter present, it has no effect. ## Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-DE_geosource-maxmind https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_city-london_geosource-maxmind https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_region-california_geosource-maxmind" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_asn-AS3320_geosource-maxmind'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Affected Parameters Every geo filter is resolved by the selected source: | Parameter | Resolved as | |----------------|-------------------------------------------------------------------| | `_country-` | The country the selected source reports for the IP | | `_region-` | The region the selected source reports | | `_city-` | The city the selected source reports | | `_zip-` | The postal code the selected source reports | | `_asn-` | The ASN the selected source attributes the IP to | | `_isp-` | The ISP name the selected source attributes the IP to | | `_continent-` | Expanded to its list of countries, then matched as above | Any other parameter — sessions, `_fraudscore-`, `_device-`, `_latency-`, protocol options — is unaffected and behaves exactly as documented on its own page. **ISP and ASN values are specific to each source** Sources use different ISP names and attribute IPs to different ASNs, so an `_isp-` or `_asn-` value taken from one source will often match nothing on another. Always take these values from the list belonging to the source you are requesting — the [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/) and dashboard list them per source. Country, region, city and zipcode values keep their usual formats across sources; only which IPs they match changes. ## Product Availability Unlike the other expert settings, this parameter is available on all three rotating proxy products. Only the endpoint changes: | Product | Endpoint | Geo filters available | |-------------|------------------------|-----------------------------------------------------------| | Residential | `rp.evomi.com:1000` | country, region, city, ASN, ISP, zipcode, continent | | Datacenter | `dcp.evomi.com:2000` | country, continent | | Mobile | `mp.evomi.com:3000` | country, region, ISP, continent | ```bash # Datacenter curl -x http://dcp.evomi.com:2000 -U testuser:testpassword_country-NL_geosource-maxmind https://ip.evomi.com/s # Mobile curl -x http://mp.evomi.com:3000 -U testuser:testpassword_country-GB_geosource-maxmind https://ip.evomi.com/s ``` ## Unrecognised Values An unrecognised source name is **ignored rather than rejected**: the request succeeds and is served from the default source, exactly as if you had omitted the parameter. Values are case-sensitive and must be lowercase, so `_geosource-MAXMIND` is not recognised and silently resolves against `ipapi`. Writing `_geosource-ipapi` explicitly is valid and identical to omitting the parameter. If a request behaves as though the parameter were absent, check the spelling and casing of the value against the table above first. ## Common Use Cases - Matching the geolocation your own verification or analytics stack reports - Cross-checking a location that two sources disagree on - Targeting an ISP or ASN as a specific source attributes it - Compliance checks that name a particular geolocation dataset as the reference **No extra bandwidth cost** Unlike the other expert settings, `_geosource-` carries **no bandwidth multiplier**, whichever source you pick — it changes how a filter is matched, not how much work the filter is. Any multiplier you see still comes from the geo filters you pair it with, at their usual rates. Parameters restricted to Core Residential, such as `_zip-`, keep that restriction when combined with this one. **Pro Tip** Start broad and narrow down. Each source matches a different set of IPs, so a filter that works at country level may find nothing once you add a city or ASN: `_country-DE_geosource-maxmind` `_country-DE_city-berlin_geosource-maxmind` `_country-US_asn-AS7922_geosource-maxmind` --- ## HTTP3/QUIC Protocol Support Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/http3/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies support HTTP3/QUIC for improved performance and compatibility with modern websites. This feature helps requests use the same current web protocols supported by modern browsers. **SOCKS5 Protocol Required** HTTP3/QUIC support is only available when using the SOCKS5 protocol. This feature will not work with HTTP/HTTPS proxy protocols. Make sure your client supports SOCKS5 connections. **Critical: Use udp.evomi.com hostname only** For HTTP3 connections, you MUST use the hostname `udp.evomi.com` exclusively. No other hostnames will work for HTTP3. ## Why HTTP3/QUIC Matters Modern browsers automatically negotiate HTTP3 connections, and websites increasingly rely on this capability. HTTP3/QUIC support helps automated workflows maintain protocol compatibility with sites that use current web standards. ### Key Benefits - **Modern Browser Compatibility**: Supports the HTTP3 capabilities available in current browsers - **Enhanced Performance**: Reduced latency and improved connection reliability - **Future-Proof**: Support for the latest web protocol standards - **Consistent Protocol Behavior**: Reduces compatibility differences when a website expects HTTP3 support ## Implementation Append `_http3-1` to your proxy password to enable HTTP3/QUIC protocol support. Use the SOCKS5 protocol: ```bash socks5://testuser:testpassword_http3-1@udp.evomi.com:1002 ``` ## Examples #### CURL ```bash # Using SOCKS5 protocol with UDP hostname for HTTP3 curl --socks5 udp.evomi.com:1002 -U testuser:testpassword_http3-1 https://ip.evomi.com/s # Combined with geographic targeting curl --socks5 udp.evomi.com:1002 -U testuser:testpassword_country-US_http3-1 https://ip.evomi.com/s ``` #### Python ```Python import requests from requests.auth import HTTPProxyAuth proxies = { "http": "socks5://udp.evomi.com:1002", "https": "socks5://udp.evomi.com:1002" } auth = HTTPProxyAuth("testuser", "testpassword_http3-1") response = requests.get("https://ip.evomi.com/s", proxies=proxies, auth=auth) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const { SocksProxyAgent } = require('socks-proxy-agent'); const proxyPass = 'testpassword_http3-1'; const proxyUrl = `socks5://testuser:${proxyPass}@udp.evomi.com:1002`; const agent = new SocksProxyAgent(proxyUrl); const url = 'https://ip.evomi.com/s'; fetch(url, { agent }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Technical Details - **Protocol**: HTTP/3 over QUIC (UDP-based) - **TLS**: Integrated TLS 1.3 encryption - **Multiplexing**: Stream-level multiplexing without head-of-line blocking - **Connection Migration**: Robust connection handling across network changes - **Protocol Requirement**: SOCKS5 protocol only (not compatible with HTTP/HTTPS proxies) ## Common Use Cases - **Web Data Collection**: Work with websites that prefer or require modern protocols - **Browser Automation**: Maintain current browser protocol compatibility - **Performance Testing**: Evaluate HTTP3 performance characteristics - **Security Research**: Test HTTP3-specific security implementations - **E-commerce Monitoring**: Monitor modern storefronts that use HTTP3 - **SEO Analysis**: Crawl websites using current transport protocols **Protocol Compatibility** HTTP3/QUIC is backward compatible. If the target server doesn't support HTTP3, connections automatically fall back to HTTP/2 or HTTP/1.1. Use our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/) to test HTTP3 compatibility with specific targets. **Note:** The http3 parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Example Configurations** Combine HTTP3 with targeting and quality parameters when needed: `_http3-1_fraudscore-0_device-windows` `_country-US_http3-1_isp-comcast_latency-100` `_zip-90210_http3-1_device-apple` --- ## ISP Targeting Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/isp/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer premium ISP (Internet Service Provider) targeting for advanced use cases requiring network-specific connections. This enterprise-grade feature enables precise control over the originating network of your proxy IPs. ## Implementation Append `_isp-` to your proxy password using our standardized ISP codes: ```bash http://testuser:testpassword_isp-att@rp.evomi.com:1000 ``` ## Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_isp-comcast https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_isp-verizon https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_isp-att" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_isp-dvodafone'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Common Use Cases - Carrier-specific content delivery testing - Network infrastructure validation - ISP-level ad campaign verification - Telecom compliance checks - Localized service availability testing **Code Management** Always verify current ISP codes using our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). Codes follow the format: `_isp-{carriercode}` (e.g., `att`, `verizon_wireless`, `telekom_de`) **Note:** The isp parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Pro Tip** Combine with geographic parameters for hyper-targeted routing: `country-US_isp-comcast` `region-TX_isp-att` `city-houston_isp-spectrum` --- ## Latency Filter Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/latency/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer a latency-based IP filtering feature that helps you select faster connections for time-sensitive operations. This parameter lets you specify the maximum network latency for IPs in milliseconds (ms). **Real-World Considerations** While our tests provide consistent benchmarks, actual user-experienced latency may vary due to: - Geographic distance between you and the IP - Network congestion - Target server response times - Local network conditions ## Using the Latency Filter Append `_latency-` to your proxy password to set maximum latency: ```bash http://testuser:testpassword_latency-50@rp.evomi.com:1000 ``` ## Implementation Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_latency-500 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_latency-300" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_latency-550_session-mysession'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` **Important Notes** 1. Latency filtering is an extra feature with increased bandwidth costs 2. Lower latency values significantly reduce available IP pool size 3. Recommended minimum value: 300ms 4. Combines with most parameters except `extended` pool **Optimization Tip** Start with higher latency values (1000-1500ms) and gradually decrease while monitoring success rates. Sudden drops in performance may indicate too strict latency filtering. **Best For** - Real-time web interactions - High-frequency trading data collection - Low-latency API integrations - Live price monitoring - Time-sensitive account operations --- ## Minimum Connection Time Filter Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/minconnection/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer a connection stability filter that ensures you only receive IPs that have maintained persistent connections to our network. This feature is particularly valuable when using sticky sessions or conducting long-running transactions. ## How It Works The `_activesince-` parameter filters IPs based on their continuous connection duration to our system: - Measures time since IP first became available in our pool - Only selects IPs active for **at least** the specified minutes - Higher minimum times = increased connection stability - Particularly effective when combined with session parameters ## Usage Add `_activesince-` to your proxy password: ```bash http://testuser:testpassword_activesince-60@rp.evomi.com:1000 ``` ## Implementation Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_session-mysession_activesince-120 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_hardsession-xyz_activesince-90" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_session-abc_activesince-180'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` **Important Considerations** 1. This is an extra feature with increased bandwidth costs 2. Higher minimum times reduce available IP pool size 3. Due to a number of factors its possible for ips to reconnect in batches, slashing the number of available IPs for a certain minimum time to 0, make sure to handle errors on your side if no IP is available. 4. Recommended maximum: 240 minutes (4 hours) 5. Combines best with `session` or `hardsession` parameters **Stability vs Availability Tradeoff** While longer-active IPs provide better stability, setting extremely high values (>300 minutes) may significantly limit available IPs. We recommend starting with 60-120 minute thresholds. **Ideal For** - Financial data scraping - Multi-step checkout processes - Account management workflows - API interactions requiring persistent IP behavior --- ## UDP Protocol Support Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/udp/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer comprehensive UDP protocol support for enterprise clients requiring full network protocol coverage. This premium enterprise-exclusive feature enables UDP traffic routing through our residential network infrastructure. **Enterprise Only Feature** UDP support is exclusively available to enterprise customers and requires manual activation by our support team. This feature is not available for standard accounts and cannot be self-activated. **SOCKS5 Protocol Required** UDP support is only available when using the SOCKS5 protocol. This feature will not work with HTTP/HTTPS proxy protocols. Make sure your client supports SOCKS5 connections. ## Understanding UDP Support Unlike our HTTP3/QUIC feature (which provides UDP-based HTTP/3 only), full UDP support enables routing of any UDP-based protocol through residential proxies. This includes custom applications, gaming protocols, VoIP traffic, DNS queries, and specialized enterprise networking tools. ## Enterprise Use Cases - **Network Protocol Testing**: Test UDP-based applications across diverse residential networks - **Gaming Infrastructure**: Route gaming traffic through residential IPs for testing - **VoIP Services**: Test voice communication systems from residential connections - **Custom Applications**: Support proprietary UDP protocols in enterprise environments - **Network Security Research**: Analyze UDP traffic patterns and behaviors - **IoT Device Simulation**: Test UDP-enabled devices from residential network contexts ## Activation Process 1. **Contact Enterprise Support**: Reach out to your enterprise account manager or support team 2. **Account Verification**: Confirm your enterprise account status and use case requirements 3. **Manual Enablement**: Our technical team will manually activate UDP support for your account 4. **Configuration Confirmation**: Receive confirmation and implementation guidelines ## Implementation Once activated by our support team, append `_udp-1` to your proxy password to enable full UDP protocol support. Use the SOCKS5 protocol: ```bash socks5://testuser:testpassword_udp-1@udp.evomi.com:1002 ``` ## Examples #### CURL ```bash # Note: CURL primarily supports HTTP/HTTPS, but UDP can be tested with custom tools # Using SOCKS5 protocol curl --socks5 udp.evomi.com:1002 -U testuser:testpassword_udp-1 https://ip.evomi.com/s # Combined with geographic targeting curl --socks5 udp.evomi.com:1002 -U testuser:testpassword_country-US_udp-1 https://ip.evomi.com/s ``` #### Python ```Python import requests from requests.auth import HTTPProxyAuth # For HTTP traffic through UDP-enabled proxy proxies = { "http": "socks5://udp.evomi.com:1002", "https": "socks5://udp.evomi.com:1002" } auth = HTTPProxyAuth("testuser", "testpassword_udp-1") response = requests.get("https://ip.evomi.com/s", proxies=proxies, auth=auth) print(response.text) # For custom UDP applications, use appropriate UDP libraries with SOCKS5 support ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const { SocksProxyAgent } = require('socks-proxy-agent'); // For HTTP traffic through UDP-enabled proxy const proxyPass = 'testpassword_udp-1'; const proxyUrl = `socks5://testuser:${proxyPass}@udp.evomi.com:1002`; const agent = new SocksProxyAgent(proxyUrl); const url = 'https://ip.evomi.com/s'; fetch(url, { agent }) .then(res => res.text()) .then(body => console.log(body)); // For custom UDP applications, use Node.js dgram module with SOCKS5 support ``` ## Technical Specifications - **Protocol Coverage**: Complete UDP protocol stack support - **Port Range**: Full UDP port range availability (subject to provider restrictions) - **Performance**: Optimized for enterprise-grade UDP traffic requirements - **Compatibility**: Works with standard UDP libraries and frameworks - **Protocol Requirement**: SOCKS5 protocol only (not compatible with HTTP/HTTPS proxies) **Enterprise Support** To enable UDP support for your enterprise account, contact our support team at enterprise@evomi.com or through your dedicated account manager. Include your account details and specific UDP use case requirements. **Note:** The udp parameter is an enterprise-exclusive extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Enterprise Configuration** Combine with other enterprise features for comprehensive networking: `_udp-1_asn-AS15169_fraudscore-0` `_country-DE_udp-1_isp-telekom_latency-50` `_zip-10115_udp-1_http3-1` --- ## Zipcode Targeting Source: https://docs.evomi.com/proxy-instructions/residential-proxies/expert-settings/zipcode/ **Note:** Expert settings are only available for **Core Residential Proxies**. Evomi Residential Proxies offer precision zipcode targeting for ultra-localized connections. This advanced enterprise feature enables you to route traffic through specific postal code areas, providing unprecedented geographic granularity for location-sensitive operations. ## Implementation Append `_zip-` to your proxy password using any valid zipcode format: ```bash http://testuser:testpassword_zip-90210@rp.evomi.com:1000 ``` ## Examples #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_zip-10001 https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_zip-90210 https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_zip-M5V3A8" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_zip-SW1A1AA'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); ``` ## Supported Formats Zipcode targeting accepts multiple international postal code formats: - **US**: 5-digit (90210) or ZIP+4 (90210-1234) - **UK**: Full postcode (SW1A 1AA) or partial (SW1A) - **Canada**: Full postal code (M5V 3A8) or forward sortation area (M5V) - **Germany**: 5-digit (10115) - **France**: 5-digit (75001) - **Australia**: 4-digit (2000) ## Common Use Cases - Hyper-local market research and competitor analysis - Regional pricing validation and compliance testing - Location-based advertising campaign verification - Neighborhood-specific service availability checks - ZIP code restricted content access testing - Local search engine optimization (SEO) validation **Format Flexibility** Spaces and dashes in postal codes are automatically normalized. Both `90210-1234` and `902101234` will work identically. Use our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/) to validate zipcode availability. **Note:** The zipcode parameter is an extra feature. Using it will incur additional bandwidth consumption. You can see the exact extra usage history in the dashboard and in the Generator. **Pro Tip** Combine with other targeting parameters for maximum precision: `_country-US_zip-90210_isp-comcast` `_zip-10001_device-windows_fraudscore-5` `_city-beverly.hills_zip-90210_latency-100` --- ## Residential Proxies Geo Targetting Source: https://docs.evomi.com/proxy-instructions/residential-proxies/geo-targetting/ Geo-targeting allows you to specify the geographic location of the proxy you want to use. This can be essential for tasks like localized testing, content access, and more. Below, we'll explain the key geo-targeting parameters and show you how to use them effectively. Our swiss quality residential proxies have the following geo-targeting parameters available: ## Geo-targeting Parameters - **_country-**: Specify the country of the proxy. - **_city-**: Specify the city of the proxy. - **_region-**: Specify the region of the proxy. - **_isp-**: Specify the Internet Service Provider (ISP) of the proxy. - **_asn-**: Specify the Autonomous System Number (ASN) of the proxy. - **_continent-**: Specify the continent of the proxy. ### Notes - You only need to specify either the country OR the region. ## Choosing the Geo Source The parameters above are resolved by a geolocation database, and you can choose which one. By default Evomi uses `ipapi` ([ip-api.com](https://ip-api.com)); adding `_geosource-` selects a different source, which is useful when the reported location needs to match your own verification tooling. See [Geo Source Selection](/proxy-instructions/residential-proxies/expert-settings/geosource/) for the list of available sources and their matching behaviour. --- ## City Targeting Source: https://docs.evomi.com/proxy-instructions/residential-proxies/geo-targetting/city/ You can target a specific country by using the `city` parameter in your proxy string. This parameter allows you to connect to a proxy located in a specific city. You can specify any city in the proxy string. If a city uses a space in its name, replace the space with a dot (e.g., `New York` becomes `new.york`). Make sure to spell the city name in lowercase. If a city is not available our proxy servers will respond with a 412 error code. ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_city-new.york https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_city-berlin https://ip.evomi.com/s ``` Note that you can find all available cities in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Residential Proxies Continent Source: https://docs.evomi.com/proxy-instructions/residential-proxies/geo-targetting/continent/ You can target a specific continent by using the `continent` parameter in the proxy URL. The available continents are `africa`, `asia`, `europe`, `north.america`, `oceania`, and `south.america`. ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_continent-asia https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_continent-europe https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_continent-africa https://ip.evomi.com/s ``` Note that you can find all available continents in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Residential Proxies Country Source: https://docs.evomi.com/proxy-instructions/residential-proxies/geo-targetting/country/ You can target a specific country by using the `country` parameter in your proxy string. This parameter allows you to connect to a proxy located in a specific country. You can specify any country in the ISO format (e.g., `US` for the United States, `CA` for Canada, etc.). For some countries the full name is supported aswell (e.g., `UnitedStates` for the United States, `Canada` for Canada, etc.). ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US https://ip.evomi.com/s ``` Note that you can find all available countries in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Residential Proxies Region (State) Source: https://docs.evomi.com/proxy-instructions/residential-proxies/geo-targetting/region/ You can target a specific region (state) by specifying the `region` in the proxy string. Regions can be things like states, provinces, or territories depending on the country. Valid examples are `california`or `southern.cape`. ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_region-california https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_region-southern.cape https://ip.evomi.com/s ``` Note that you can find all available regions in the dashboard via our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Residential Proxies How to Connect Source: https://docs.evomi.com/proxy-instructions/residential-proxies/how-to-connect/ This page is a basic guide with information about Residential Proxies and how to connect to them. ## Endpoint and Port Information To connect to the evomi proxy system with residential proxies, you need to use the following endpoint and port configurations: | Proxy Protocol | Residential Endpoint | Residential Port | |--------------|---------------|--------| | HTTP Proxy | rp.evomi.com | 1000 | | HTTPs Proxy | rp.evomi-proxy.com | 1001 | | SOCKS5 Proxy | rp.evomi.com | 1002 | The HTTPs endpoint uses the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject `rp.evomi.com` on port 1001. The difference between the HTTP and HTTPs proxies is that the HTTPs proxy is encrypted, while the HTTP proxy is not. The SOCKS5 proxy is a more advanced protocol that can handle more types of traffic. The HTTP protocol works for HTTPs requests, you do not need to use the HTTPs protocol for HTTPs requests. The HTTPs protocol is only necessary if you need to connect to a secure HTTPs server and is generally slower than the HTTP protocol. Please note that depending on your protocol, you will need to use the correct port number. Every residential proxy request has a minimum billed traffic consumption of 512 bytes. If a request consumes less than 512 bytes, it still deducts 512 bytes from your balance. At $0.49 per GB, the Core Residential rate on the 100 GB plan, that minimum is about $0.00000025088 per request. ## Basic Connection Instructions Here are step-by-step instructions to help you connect using different programming languages and tools. ### CURL To connect using CURL, use the following command format: ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword https://ip.evomi.com/s ``` ### Python To connect using Python with the `requests` library, follow these steps: 1. Install the `requests` library if you haven't already: ```bash pip install requests ``` 2. Use the following script: ```python import requests proxy_pass = "testpassword" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` ### JavaScript To connect using JavaScript, particularly with Node.js, follow these steps: 1. Install the `node-fetch` and `http-proxy-agent` packages if you haven't already: ```bash npm install node-fetch http-proxy-agent ``` 2. Use the following script: ```javascript const fetch = require('node-fetch'); const HttpsProxyAgent = require('https-proxy-agent'); const proxyPass = 'testpassword'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const agent = new HttpsProxyAgent(proxyUrl); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log(body)) .catch(err => console.error('Error:', err)); ``` Note that you can get an easy to use interface to configure this for you, using our [Proxy Generator](https://docs.evomi.com/proxy-instructions/proxy-generator/). --- ## Proxy Pools Source: https://docs.evomi.com/proxy-instructions/residential-proxies/modes/ This feature is only available for Premium Residential Proxies. Evomi residential Proxies offer a filtering process of the IPs that are used in the pool. The pool can be used in 3 different modes, which affect the size, speed, and quality of the pool. This is to ensure our proxy pool can give you the best results for your use case. While certain customers may prefer a larger pool with more IPs, others may prefer a smaller pool with higher quality IPs. The mode you choose will depend on your specific needs. ## Modes ### **Standard Mode** The standard mode is the default mode for our residential proxy pool. It offers the largest pool size, which gives you access to all of our residential IPs. This is ideal, if your target blocks IPs fast, or you need a large number of IPs for your task. To activate this mode, no additional parameters are required as it is the default mode. ### **Speed Mode** In this mode, you will only receive the fastest IPs in our pool. This is good when you need to transfer larger amounts of data or need the lowest response times. When doing time sensitive tasks, this mode is recommended. The pool size is smaller than the standard mode. To activate this mode, add the `mode-speed` parameter to your proxy password. An example would be `proxypassword_session-123456_country-US_mode-speed` ### **Quality Mode** In this mode, you receive the highest-quality IPs in our pool. These IPs are selected for speed and connection reliability, making this mode suitable for sites with stricter access requirements. The pool size is the smallest of all modes. To activate this mode, add the `mode-quality` parameter to your proxy password. An example would be `proxypassword_session-123456_country-US_mode-quality` ## When to use which mode The choice of mode depends on your specific use case and requirements. Here's a table to help guide your decision: | Mode | Pool Size | Speed | Quality | Best For | |----------|-----------|-------|---------|----------| | Standard | Largest | Good | Good | - General purpose scraping
- Tasks requiring a large IP pool
- Targets that block IPs quickly | | Speed | Medium | Best | Good | - Time-sensitive tasks
- Transferring large amounts of data
- Tasks requiring low latency | | Quality | Smallest | Best | Best | - Sites with stricter access requirements
- Workflows prioritizing connection reliability
- Complex browser-based tasks | ### Guidelines for choosing a mode: 1. **Use Standard mode when:** - You need access to the largest possible pool of IPs - Your target website frequently blocks IPs - You're doing general-purpose scraping across various websites 2. **Use Speed mode when:** - You're working with time-sensitive data - You need to transfer large amounts of data quickly - Your tasks require the lowest possible latency 3. **Use Quality mode when:** - You're working with websites that have stricter access requirements - You need the most reliable IP selection available - Your tasks prioritize connection quality over pool size Remember, you can always start with one mode and switch to another if you find it's not meeting your needs. Our flexible system allows you to adapt to different scenarios easily. Note our easy to use Proxy Generator can generate all this for you. --- ## Residential Proxies Proxy Sessions Source: https://docs.evomi.com/proxy-instructions/residential-proxies/proxy-sessions/ By default, each request to our proxy server generates a new IP address. However, you can use session parameters to maintain the same IP address for multiple requests. This feature is useful for tasks that require a stable connection, such as web resources with session-based authentication. ![Sticky Session Demo](/images/session-demo.gif) ## Session Parameters We offer three types of session parameters: `session`, `hardsession`, and `lockedsession`. All ensure you keep the same IP for all following requests, but they have different behaviors and use cases. ### The `session` parameter - A unique alphanumeric string, 6 to 10 characters long. - Optimized for the highest overall success rate. - May change IP address in favor of stability. - Works with the `lifetime` parameter. ### The `hardsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Prioritizes IP address longevity. - Keeps the same IP for as long as possible. - Does not work with the `lifetime` parameter. ### The `lockedsession` parameter - Also a unique alphanumeric string, 6 to 10 characters long. - Locks traffic to one specific IP. - Never switches to a replacement IP if that peer disconnects. - If the bound IP goes offline, the request fails instead of assigning a new IP. - Does not work with the `lifetime` parameter. When specifying any geo or ISP targeting parameters, the first request will use the specified parameters, and all subsequent requests will use the same IP address. If you change the targeting while maintaining the same session, the IP address will remain the same. ### The `lifetime` parameter - Specifies the duration for the `session` in minutes. - If not specified, the session will last for 30 minutes by default. - Maximum value is 1440 minutes, which is 24 hours. - After the specified time, the IP address will change. - Does not apply to `hardsession` or `lockedsession`. If you specify a higher `lifetime` value than the maximum allowed, the request will return a 412 error code. ## Examples Below are examples of using Sticky sessions in different programming languages: CURL, Python, and JavaScript. #### CURL ```bash curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_session-sgn34f3e_lifetime-10 https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_hardsession-JS9nsq2n https://ip.evomi.com/s curl -x http://rp.evomi.com:1000 -U testuser:testpassword_country-US_lockedsession-k8m2pQ1x https://ip.evomi.com/s ``` #### Python ```Python import requests proxy_pass = "testpassword_country-US_session-sgn34f3e_lifetime-10" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_hardsession-JS9nsq2n" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) proxy_pass = "testpassword_country-US_lockedsession-k8m2pQ1x" proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` #### JavaScript ```JavaScript const fetch = require('node-fetch'); const proxyPass = 'testpassword_country-US_session-sgn34f3e_lifetime-10'; const proxyUrl = `http://testuser:${proxyPass}@rp.evomi.com:1000`; const url = 'https://ip.evomi.com/s'; fetch(url, { proxy: proxyUrl }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass2 = 'testpassword_country-US_hardsession-JS9nsq2n'; const proxyUrl2 = `http://testuser:${proxyPass2}@rp.evomi.com:1000`; fetch(url, { proxy: proxyUrl2 }) .then(res => res.text()) .then(body => console.log(body)); const proxyPass3 = 'testpassword_country-US_lockedsession-k8m2pQ1x'; const proxyUrl3 = `http://testuser:${proxyPass3}@rp.evomi.com:1000`; fetch(url, { proxy: proxyUrl3 }) .then(res => res.text()) .then(body => console.log(body)); ``` Note our easy to use Proxy Generator can generate all this for you. --- ## Static Residential Proxies Source: https://docs.evomi.com/proxy-instructions/static-residential-proxies/ Evomi Static Residential Proxies are dedicated ISP-hosted IPs that stay yours for as long as you rent them. Unlike our rotating pools, nothing changes between requests: - Dedicated IPs registered with residential ISPs, hosted on premium ASNs - Unlimited bandwidth — billed per IP rather than per GB - Long-lived sessions with no rotation and no session parameters to manage - Shared, Private and Virgin IP options - Support for HTTP and SOCKS5 protocols Static Residential Proxies are billed per IP with unlimited bandwidth, so the 512-byte minimum request charge that applies to our per-GB products does not apply here. Ideal for account management, long-lived logins, and any workflow that needs to present the same residential IP every time. The [Frequently Asked Questions](https://docs.evomi.com/frequently-asked-questions/) cover the differences between Shared, Private and Virgin IPs, our IP replacement policy, and which locations are available. --- ## Static Residential Proxies How to Connect Source: https://docs.evomi.com/proxy-instructions/static-residential-proxies/how-to-connect/ This page is a basic guide with information about Static Residential Proxies and how to connect to them. Static Residential works differently from our rotating products in one way that matters before you write any code: you rent a set of dedicated IPs, and you pick which one to go out through on each request. You still connect to a single gateway — there is no separate host or port per IP — and the IP you want is named in your password. ## Endpoint and Port Information To connect to the evomi proxy system with Static Residential proxies, you need to use the following endpoint and port configurations: | Proxy Protocol | Static Residential Endpoint | Static Residential Port | |--------------|---------------|--------| | HTTP Proxy | isp-2.evomi.com | 12345 | | SOCKS5 Proxy | isp-2.evomi.com | 12346 | Your dashboard is the source of truth for the gateway hostname. Static Residential gateways are assigned per account, so use the endpoint shown next to your IPs rather than assuming the one above. The port numbers are the same on every gateway. There is no HTTPS proxy port for Static Residential. Use the HTTP endpoint: as with our other products, the HTTP proxy protocol carries HTTPS requests perfectly well, and your traffic to the target site is still encrypted end to end. Only the hop between your machine and the proxy is unencrypted. ## Choosing which IP to use Your dashboard lists every IP you rent in this form: ``` isp-2.evomi.com:12345:USERNAME:203.0.113.10_PASSWORD ``` That is `host:port:username:password` — the same host-first notation the [Generate Proxies](https://docs.evomi.com/public-api/endpoints/generate/) endpoint can return. It is a display format for listing proxies, not a string you paste into a client as-is. Reading it left to right: - `isp-2.evomi.com` — the gateway host - `12345` — the port, or `12346` for SOCKS5 - `USERNAME` — your Evomi username - `203.0.113.10_PASSWORD` — your password, prefixed with the IP you want to exit from and an underscore Changing the IP prefix is how you switch between the IPs you rent. Everything else stays identical, so a single set of credentials reaches your whole allocation. Because the IP lives in the password, none of the geo-targeting or session parameters used by our rotating products apply here. The IP already fixes the location, and it does not rotate. ## Basic Connection Instructions Here are step-by-step instructions to help you connect using different programming languages and tools. Replace `203.0.113.10` with one of your own IPs, and `testuser` / `testpassword` with your Evomi credentials. ### CURL To connect using CURL, use the following command format: ```bash curl -x http://isp-2.evomi.com:12345 -U testuser:203.0.113.10_testpassword https://ip.evomi.com/s ``` For SOCKS5, use port 12346 and the `socks5h://` scheme so DNS is resolved by the proxy: ```bash curl -x socks5h://isp-2.evomi.com:12346 -U testuser:203.0.113.10_testpassword https://ip.evomi.com/s ``` ### Python To connect using Python with the `requests` library, follow these steps: 1. Install the `requests` library if you haven't already: ```bash pip install requests ``` 2. Use the following script: ```python import requests proxy_pass = "203.0.113.10_testpassword" proxy_url = f"http://testuser:{proxy_pass}@isp-2.evomi.com:12345" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://ip.evomi.com/s", proxies=proxies) print(response.text) ``` ### JavaScript To connect using JavaScript, particularly with Node.js, follow these steps: 1. Install the `node-fetch` and `http-proxy-agent` packages if you haven't already: ```bash npm install node-fetch http-proxy-agent ``` 2. Use the following script: ```javascript const fetch = require('node-fetch'); const HttpsProxyAgent = require('https-proxy-agent'); const proxyPass = '203.0.113.10_testpassword'; const proxyUrl = `http://testuser:${proxyPass}@isp-2.evomi.com:12345`; const agent = new HttpsProxyAgent(proxyUrl); fetch('https://ip.evomi.com/s', { agent }) .then(res => res.text()) .then(body => console.log(body)) .catch(err => console.error('Error:', err)); ``` The output should display the static IP you selected in the password. If it shows a different address, check that the IP prefix matches one of the IPs listed in your dashboard. ## Tips and Troubleshooting - **407 Proxy Authentication Required**: the username or password was rejected. Confirm the IP prefix is one you currently rent and that the underscore between the IP and the password is present. - **Wrong exit IP**: the IP prefix is missing or misspelled. Without a valid prefix the gateway cannot tell which of your IPs to use. - **Special characters in your password**: pass credentials with cURL's `-U` flag, or build the URL from variables as in the examples above, so you do not have to URL-encode them. - **Choosing a protocol**: use 12345 for HTTP and 12346 for SOCKS5. SOCKS5 handles non-HTTP traffic and, with `socks5h://`, resolves DNS at the proxy. --- ## Scraping Products Instructions Source: https://docs.evomi.com/scraping-products-instructions/ Evomi provides turnkey scraping products designed for reliability, scale, and ease of use. This section covers products that offer managed browser automation, rendering, and proxy integration with minimal setup. For traditional proxy-based integrations, see the dedicated guides under [Proxy Instructions](/proxy-instructions/). --- ## Scraper API Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/ The Evomi Scraper API is a unified solution for extracting web data at scale. With intelligent mode selection, managed browser rendering, and global proxy integration, it handles everything from simple static pages to complex JavaScript-heavy applications through a single REST API. ## Why Scraper API? **Intelligent Mode Selection** Our smart `auto` mode automatically detects whether a page needs JavaScript rendering, optimizing both cost and performance without manual intervention. **Global Proxy Network** Route requests through residential or datacenter proxies across 150+ countries. Session management keeps your IP consistent across multi-step workflows. **Rich Output Formats** Extract data as HTML, clean Markdown, PDFs or full-page screenshots. Integrate AI enhancement for instant summaries and structured data extraction. **Built for Scale** Handle concurrent requests with automatic rate limiting, async processing for long-running tasks, and transparent credit-based pricing. ## Key Features | Feature | Description | |---------|-------------| | **Auto Mode** | Automatically chooses between fast HTTP requests and full browser rendering | | **Browser Automation** | Execute JavaScript, click buttons, fill forms—all without managing browsers | | **AI Enhancement** | Built-in AI for content summarization and structured data extraction | | **Global Proxies** | Residential and datacenter proxies with geo-targeting and session support | | **Multiple Outputs** | HTML, Markdown, PDFs or PNG screenshots with one parameter | | **Async Processing** | Background task execution for complex scraping jobs | | **Resource Blocking** | Speed up requests by blocking images, CSS, and other resources | | **Network Capture** | Intercept and capture XHR/Fetch API responses during page load | ## Quick Start Make your first request in seconds: Your API key is available in the [Evomi Dashboard](https://my.evomi.com/my/products/scraper-api-universal/playground). And you need an active trial, subscription, or pay-as-you-go credits specifically for the Scraper API to access it. ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" ``` That's it! The API intelligently handles the rest—choosing the right method, managing proxies, and returning clean content. You can even paste this URL directly into your browser to see the results. ## Base URL ``` https://scrape.evomi.com ``` All API requests use this base URL with the `/api/v1/scraper/` prefix. ## Pricing at a Glance Transparent, credit-based pricing with no surprises: | Mode | Proxy Type | Cost per Request | |------|------------|------------------| | Request | Datacenter | 1 credit | | Request | Premium (residential) | 2 credits | | Browser | Premium (residential) | 5 credits | | Auto | Premium (request succeeds) | 2 credits | | Auto | Premium (upgraded to browser) | 6 credits | | AI Enhancement | - | +30 credits | | Screenshot | - | +1 credit | | PDF | - | +1 credit | **Browser and Auto modes require premium (residential) proxies.** Datacenter proxies can only be used with `mode="request"`. Auto mode charges only for what it uses. If a simple HTTP request works, you pay 2 credits. If browser rendering is needed, you pay 50% of the request cost plus the full browser cost (6 credits total)—always optimized for value. ## Next Steps Explore the documentation to unlock the full potential of the Scraper API: - **[Quickstart](/scraping-products-instructions/scraper-api/quickstart/)** - Make your first request and see results in seconds - **[Authentication](/scraping-products-instructions/scraper-api/authentication/)** - Secure your API access - **[Parameters](/scraping-products-instructions/scraper-api/parameters/)** - Comprehensive parameter reference - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** - Code samples in Python, Node.js, and more Keep your API key secure. Never expose it in client-side code or public repositories. Store it in environment variables and use HTTPS for all requests. --- ## Scraper API Authentication Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/authentication/ All requests to the Scraper API require authentication using your unique API key. This page explains how to obtain and use your key securely. ## Getting Your API Key 1. Log in to your [Evomi Dashboard](https://my.evomi.com/settings/api) 2. Navigate to **Settings** → **API** 3. Copy your API key Keep your API key secret! Never expose it in client-side code, public repositories, or share it publicly. Treat it like a password. ## Authentication Methods The API accepts your key in three ways, checked in this priority order: ### 1. Query Parameter (Simplest) Include the key in the URL as `api_key`: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" ``` This method works directly in browser address bars, making it perfect for quick tests and simple integrations. Query parameters are convenient for testing but URLs may be logged by proxies or servers. For production, consider using headers. ### 2. Header (Recommended for Production) Pass the key in the `x-api-key` header: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com" \ -H "x-api-key: YOUR_API_KEY" ``` This is the most secure method and recommended for production use. ### 3. JSON Body (POST Requests) For POST requests, include the key in the JSON payload: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -d '{ "api_key": "YOUR_API_KEY", "url": "https://example.com" }' ``` ## Authentication Errors ### Invalid or Missing Key (401) ```json { "success": false, "error": "Invalid API key", "timestamp": "2025-10-29T12:00:00Z" } ``` **Solution:** Verify your API key in the dashboard and ensure it's correctly passed in the request. ### Insufficient Credits (402) ```json { "success": false, "error": "Insufficient credits", "credits_remaining": 0.5, "timestamp": "2025-10-29T12:00:00Z" } ``` **Solution:** Add credits to your account through the dashboard. ## Security Best Practices ### Use Environment Variables Never hardcode API keys in your source code: **Python:** ```python import os api_key = os.environ.get('EVOMI_API_KEY') ``` **Node.js:** ```javascript const apiKey = process.env.EVOMI_API_KEY; ``` **Bash:** ```bash export EVOMI_API_KEY="your_key_here" curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=$EVOMI_API_KEY" ``` ### Use HTTPS Only All requests must use HTTPS to encrypt your API key in transit. HTTP requests will be rejected. ### Rotate Keys Regularly If you suspect your key has been compromised: 1. Generate a new key in the dashboard 2. Update your applications with the new key 3. Revoke the old key ### Use Different Keys for Different Environments Consider using separate API keys for: - Development/testing - Staging - Production This allows you to track usage per environment and quickly revoke access if needed. ## Rate Limiting Your API key is associated with a concurrency limit that controls how many simultaneous requests you can make. The limit depends on your plan. Check your remaining capacity in response headers: ``` X-RateLimit-Limit: 5 X-RateLimit-Remaining: 3 ``` See the [Rate Limits](/scraping-products-instructions/scraper-api/rate-limits/) page for detailed information on managing concurrent requests. ## Testing Authentication Verify your API key is working correctly: ```bash curl "https://scrape.evomi.com/api/v1/scraper/health?api_key=YOUR_API_KEY" ``` **Success Response:** ```json { "success": true } ``` The health check endpoint doesn't consume credits—use it freely to test connectivity and authentication. --- ## Cloud Storage Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/cloud-storage/ Scraper API results are only stored temporarily by us and are removed after delivery. To ensure long-term data persistence and seamless data pipelines, we can upload results directly to cloud storage upon completion. Retrieve your scraped results directly in Amazon S3, Google Cloud Storage (GCS), Azure Blob Storage, or any S3-compatible cloud provider. ## Storage Management Manage your storage configuration through our REST API endpoints. Instead of defining your storage logic with every request, you'll save a configuration once and reference it by ID. **Note:** All sensitive credentials (`access_key`, `secret_key`) are **encrypted** when stored. **Endpoint Reference** | Endpoint | Method | Action | | :--- | :--- | :--- | | `/account/storage` | `POST` | Create a new storage configuration | | `/account/storage` | `GET` | List all saved storage configurations | | `/account/storage/{storage_id}` | `PUT` | Update an existing storage configuration | | `/account/storage/{storage_id}` | `DELETE` | Remove a storage configuration | **Authentication**: Include your API key in the `x-api-key` header for all requests. --- ### Storage Creation | Parameter | Type | Description | | :--- | :--- | :--- | | `name` | String | A unique identifier for your storage config. | | `storage_type` | String | The provider type: `s3_compatible`, `gcs`, or `azure_blob`. | | `config` | Object | Provider-specific credentials. View "Storage Providers" Tab. | | `set_as_default` | Boolean | Whether to set this configuration as the default. | ```json { "name": "My AWS S3", "storage_type": "s3_compatible", "config": { "bucket": "my-bucket", "region": "us-east-1", "access_key": "AKIAIOSFODNN7EXAMPLE", "secret_key": "wJalrXUt...", "path_prefix": "scrapers/", "public_read": false }, "set_as_default": false } ``` ## Scraper API Integration Specify your storage destination during the scrape request via query parameters: **Option A: Use Default Storage ID** ```bash ?url=https://example.com&use_default_storage=true ``` **Option B: Specify Storage ID** ```bash ?url=https://example.com&storage_id={encoded_id} ``` **Option C: Temporary Storage** ```bash ?url=https://example.com ``` --- When using cloud storage, we will upload what you requested. Understanding how different output types are handled ensures you get your data exactly where you need it. ### Delivery Modes The Scraper Api's `delivery` parameter controls how results are formatted and uploaded to storage: | Mode | Description | Storage Upload | | :--- | :--- | :--- | | `raw` | Returns content directly (HTML, markdown, AI response, etc.) | Uploads the raw response. Scraper Api's 'content=' parameter | | `json` | Returns structured JSON envelope with all metadata | Uploads complete result as JSON | ### Examples **Screenshots** ```bash ?url=https://example.com&delivery=raw&use_default_storage=true&content=screenshot ``` **Uploads:** Screenshot as `.png` **Markdown** ```bash ?url=https://example.com&delivery=raw&use_default_storage=true&content=markdown ``` **Uploads:** Markdown content as `.md` **Default HTML** ```bash ?url=https://example.com&delivery=raw&use_default_storage=true ``` **Uploads:** HTML content as `.html` ## File Name Templateing Organize your scraped data at scale using dynamic path templates. Instead of manually organizing files, use template variables to automatically structure your storage based on domains, dates, and other metadata. ### Template Variables Template variables use double curly brace syntax: `{{variable_name}}` | Variable | Description | Example Output | | :--- | :--- | :--- | | `{{task_id}}` | Unique task identifier | `abc123xyz` | | `{{url}}` | Full URL being scraped | `https://example.com/page` | | `{{domain}}` | Extracted domain name | `example.com` | | `{{status}}` | Task completion status | `success`, `failure` | | `{{timestamp}}` | Full timestamp (YYYYMMDD_HHMMSS) | `20260205_141530` | | `{{date}}` | Date only (YYYY-MM-DD) | `2026-02-05` | | `{{time}}` | Time only (HH-MM-SS) | `14-15-30` | | `{{year}}` | Four-digit year | `2026` | | `{{month}}` | Two-digit month | `02` | | `{{day}}` | Two-digit day | `05` | | `{{extension}}` | File extension based on output type | `json`, `png`, `pdf`, `html`, `md`, `txt` | --- ## Template Examples ### Domain & Date Organization ```json { "path_prefix": "scrapes/{{domain}}/{{date}}/{{task_id}}.{{extension}}" } ``` **Result:** `scrapes/example.com/2026-02-05/abc123xyz.json` ### Year/Month/Day Hierarchy ```json { "path_prefix": "data/{{year}}/{{month}}/{{day}}/{{domain}}_{{task_id}}.{{extension}}" } ``` **Result:** `data/2026/02/05/example.com_abc123xyz.json` ### Status-based Filtering ```json { "path_prefix": "results/{{status}}/{{domain}}/{{timestamp}}.{{extension}}" } ``` **Result:** `results/success/example.com/20260205_141530.json` ### Simple Domain Organization ```json { "path_prefix": "{{domain}}/{{task_id}}.{{extension}}" } ``` **Result:** `example.com/abc123xyz.json` ### Time-series Data Collection ```json { "path_prefix": "timeseries/{{domain}}/{{year}}-{{month}}/{{day}}_{{time}}.{{extension}}" } ``` **Result:** `timeseries/example.com/2026-02/05_14-15-30.json` --- ## Storage Providers Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/cloud-storage/providers/ | Provider | Type | Authentication Method | | :--- | :--- | :--- | | **Amazon S3** | S3-Compatible | Access Key & Secret Key | | **Cloudflare R2** | S3-Compatible | Access Key & Secret Key | | **DigitalOcean Spaces** | S3-Compatible | Access Key & Secret Key | | **Google Cloud Storage** | Native | Service Account JSON | | **Azure Blob Storage** | Native | Connection String | | **Backblaze B2** | S3-Compatible | Application Key & Key ID | | **Other (Wasabi, MinIO)** | S3-Compatible | Access Key & Secret Key | ## Amazon S3 ### Setup Steps 1. **Create an S3 Bucket** - Log into AWS Console - Navigate to S3 - Create a new bucket in your desired region - Note the bucket name and region 2. **Create IAM User** - Navigate to IAM → Users → Create User - Enable "Access key - Programmatic access" - Attach policy: `AmazonS3FullAccess` or create a custom policy 3. **Generate Access Keys** - Complete user creation - Save the Access Key ID and Secret Access Key ### Configuration Example ```json { "name": "AWS S3 Production", "storage_type": "s3_compatible", "config": { "bucket": "my-scraper-data", "region": "us-east-1", "access_key": "AKIAIOSFODNN7EXAMPLE", "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "endpoint_url": "https://s3.amazonaws.com", "path_prefix": "scrapes/{{domain}}/{{date}}/{{task_id}}.{{extension}}" } } ``` --- ## Cloudflare R2 ### Setup Steps 1. **Enable R2 in Cloudflare Dashboard** - Log into Cloudflare Dashboard - Navigate to R2 - Create a new bucket 2. **Generate API Tokens** - Go to R2 → Manage R2 API Tokens - Create API token with "Object Read & Write" permissions - Save the Access Key ID and Secret Access Key 3. **Get Account ID** - Find your Account ID in the R2 overview page - Note your endpoint URL format ### Configuration Example ```json { "name": "Cloudflare R2 Storage", "storage_type": "s3_compatible", "config": { "bucket": "scraper-data", "region": "auto", "access_key": "YOUR_R2_ACCESS_KEY", "secret_key": "YOUR_R2_SECRET_KEY", "endpoint_url": "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com", "path_prefix": "production/{{domain}}/{{date}}/{{task_id}}.{{extension}}" } } ``` --- ## DigitalOcean Spaces ### Setup Steps 1. **Create a Space** - Log into DigitalOcean - Navigate to Spaces - Create a new Space in your preferred region - Note the Space name and region 2. **Generate Spaces Access Keys** - Go to API → Spaces Keys - Generate New Key - Save the Access Key and Secret Key ### Configuration Example ```json { "name": "DigitalOcean Spaces", "storage_type": "s3_compatible", "config": { "bucket": "my-scraper-space", "region": "nyc3", "access_key": "YOUR_SPACES_ACCESS_KEY", "secret_key": "YOUR_SPACES_SECRET_KEY", "endpoint_url": "https://nyc3.digitaloceanspaces.com", "path_prefix": "scrapes/{{year}}/{{month}}/{{domain}}/{{task_id}}.{{extension}}" } } ``` --- ## Google Cloud Storage (GCS) ### Setup Steps 1. **Create a GCS Bucket** - Log into Google Cloud Console - Navigate to Cloud Storage - Create a new bucket - Choose location type and storage class - Note the bucket name 2. **Create Service Account** - Navigate to IAM & Admin → Service Accounts - Create a new service account - Grant "Storage Object Admin" role - Create and download JSON key 3. **Note Project ID** - Find your Project ID in the GCS console - You'll need this for authentication ### Configuration Example ```json { "name": "Google Cloud Storage", "storage_type": "gcs", "config": { "bucket": "my-scraper-bucket", "project_id": "your-project-id", "credentials_json": "{\"type\":\"service_account\",\"project_id\":\"your-project\",\"private_key_id\":\"...\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"scraper@your-project.iam.gserviceaccount.com\",\"client_id\":\"...\",\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"token_uri\":\"https://oauth2.googleapis.com/token\"}", "path_prefix": "scrapes/{{domain}}/{{date}}/{{task_id}}.{{extension}}" } } ``` --- ## Azure Blob Storage ### Setup Steps 1. **Create Storage Account** - Log into Azure Portal - Create a new Storage Account - Choose performance tier (Standard or Premium) - Select redundancy option (LRS, GRS, etc.) - Note the account name 2. **Get Connection String** - Navigate to your Storage Account - Go to Security + networking → Access keys - Click "Show keys" button - Copy the "Connection string" from Key1 or Key2 3. **Create Container** - Navigate to Containers (under Data storage) - Click "+ Container" - Create a new container (equivalent to S3 bucket) - Set public access level (Private recommended) ### Configuration Example ```json { "name": "Azure Blob Storage", "storage_type": "azure_blob", "config": { "container": "scraper-results", "connection_string": "DefaultEndpointsProtocol=https;AccountName=youraccount;AccountKey=abc123...==;EndpointSuffix=core.windows.net", "path_prefix": "scrapes/{{domain}}/{{date}}/{{task_id}}.{{extension}}" } } ``` ## Backblaze B2 ### Setup Steps 1. **Create B2 Bucket** - Log into Backblaze - Navigate to B2 Cloud Storage - Create a new bucket - Note the bucket name 2. **Generate Application Key** - Go to App Keys - Create new key with access to your bucket - Save the keyID and applicationKey 3. **Get Endpoint URL** - Find your S3-compatible endpoint in bucket settings - Format: `https://s3.REGION.backblazeb2.com` ### Configuration Example ```json { "name": "Backblaze B2", "storage_type": "s3_compatible", "config": { "bucket": "scraper-backup", "region": "us-west-002", "access_key": "YOUR_B2_KEY_ID", "secret_key": "YOUR_B2_APPLICATION_KEY", "endpoint_url": "https://s3.us-west-002.backblazeb2.com", "path_prefix": "archives/{{year}}/{{month}}/{{domain}}/{{task_id}}.{{extension}}" } } ``` --- ## Error Handling Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/errors/ The Scraper API uses standard HTTP status codes and provides detailed error messages to help you quickly identify and resolve issues. ## HTTP Status Codes | Status | Meaning | Action Required | |--------|---------|----------------| | `200 OK` | Success | Process the response | | `202 Accepted` | Async task submitted or timeout | Poll task status | | `400 Bad Request` | Invalid parameters | Fix request parameters | | `401 Unauthorized` | Authentication failed | Verify API key | | `402 Payment Required` | Insufficient credits | Add credits to account | | `408 Request Timeout` | Request exceeded timeout | Use async mode or retry | | `422 Unprocessable Entity` | Validation error | Check parameter values | | `429 Too Many Requests` | Rate limit exceeded | Wait and retry with backoff | | `500 Internal Server Error` | Server error | Retry with exponential backoff | | `503 Service Unavailable` | Temporary unavailability | Retry after delay | ## Error Response Format All errors follow a consistent JSON structure: ```json { "success": false, "error": "Error type", "message": "Detailed error description", "timestamp": "2025-10-29T12:00:00Z" } ``` ## Common Errors ### Validation Errors (422) **Cause:** Invalid parameter values or missing required fields. ```json { "success": false, "error": "Invalid request parameters", "message": "url is required and must be a valid URL; mode must be one of ['request', 'browser', 'auto']", "timestamp": "2025-10-29T12:00:00Z", "details": { "validation_errors": [ "url: value is not a valid URL", "mode: must be one of ['request', 'browser', 'auto']" ] } } ``` **Common validation errors:** | Error | Cause | Solution | |-------|-------|----------| | `url is required` | Missing URL parameter | Include `url` in request | | `invalid URL format` | URL missing protocol | Add `https://` or `http://` | | `invalid mode value` | Unknown mode | Use `request`, `browser`, or `auto` | | `invalid proxy_country` | Invalid country code | Use 2-letter ISO code (e.g., `US`, `GB`) | | `invalid proxy_session_id` | Wrong length | Use 6-8 alphanumeric characters | | `screenshot requires browser` | Screenshot with request mode | Set `mode=browser` | | `ai_source required` | AI enabled without source | Add `ai_source: markdown` or `screenshot` | ### Authentication Errors (401) **Cause:** Missing or invalid API key. ```json { "success": false, "error": "Invalid API key", "message": "The provided API key is invalid or missing", "timestamp": "2025-10-29T12:00:00Z" } ``` **Solutions:** - Verify your API key in the [dashboard](https://my.evomi.com/settings/api) - Check that you're passing the key correctly (header, query param, or body) - Ensure you're using `x-api-key` (not `x-apikey` or other variants) ### Insufficient Credits (402) **Cause:** Not enough credits to complete the request. ```json { "success": false, "error": "Insufficient credits", "message": "Not enough credits. Required: 5.0, Available: 2.5", "credits_remaining": 2.5, "timestamp": "2025-10-29T12:00:00Z" } ``` **Solutions:** - Add credits to your account through the dashboard - Use cheaper proxy type (`datacenter` instead of `residential`) - Use simpler mode (`request` instead of `browser`) - Disable expensive features (AI enhancement, screenshots) ### Rate Limit Exceeded (429) **Cause:** Too many concurrent requests. ```json { "success": false, "error": "Concurrency limit exceeded (5/5)", "message": "You have exceeded the maximum number of concurrent requests", "limit": 5, "reset": 60, "timestamp": "2025-10-29T12:00:00Z" } ``` **Solutions:** - Wait for the `reset` time (usually 60 seconds) before retrying - Implement request queuing on your side - Use async mode with polling - Contact support to increase your concurrency limit See [Rate Limits](/scraping-products-instructions/scraper-api/rate-limits/) for detailed handling strategies. ### Request Timeout (408) **Cause:** Request took longer than the timeout limit (30-45 seconds). ```json { "success": true, "task_id": "task_abc123", "status": "processing", "message": "Task is taking longer than expected. Use task_id to check status.", "check_url": "/api/v1/scraper/tasks/task_abc123", "timestamp": "2025-10-29T12:00:00Z" } ``` **Note:** The request continues processing in the background. Use the `task_id` to retrieve results. **Solutions:** - Poll the task status endpoint: `GET /api/v1/scraper/tasks/{task_id}` - Use `async=true` from the start for long-running requests - Optimize request (block resources, use datacenter proxies) ### Scraping Errors (500) **Cause:** Failed to scrape the target website. ```json { "success": false, "error": "Scraping error", "message": "Connection refused by target server", "url": "https://example.com", "credits_used": 0, "timestamp": "2025-10-29T12:00:00Z" } ``` **Common scraping errors:** | Message | Cause | Solution | |---------|-------|----------| | `DNS resolution failed` | Invalid domain | Check URL spelling | | `Connection refused` | Target blocked request | Try residential proxy | | `SSL certificate error` | Invalid HTTPS certificate | Contact support if legitimate site | | `Page crashed` | Browser issue | Retry request | | `Navigation timeout` | Page too slow | Increase `wait_seconds` | | `Target closed` | Connection interrupted | Retry with backoff | **Credits are automatically refunded** when scraping fails due to network errors, DNS issues, or server problems. You only pay for successful responses. ## Retry Strategy The API implements automatic retry logic for transient errors: **Request Mode:** Up to 5 retries **Browser/Auto Mode:** Up to 3 retries **Retryable errors:** - Network connection errors - Timeout waiting for elements - Page crashed - Browser context destroyed - SSL errors - Gateway errors (502, 504) **Non-retryable errors:** - DNS resolution failures - Authentication errors (401) - Validation errors (422) - Insufficient credits (402) - Page navigation errors ### Client-Side Retry Implementation Implement exponential backoff for optimal retry behavior: ```python import time import requests def scrape_with_retry(url, api_key, max_retries=3): """Scrape with exponential backoff retry logic""" for attempt in range(max_retries): try: response = requests.get( f"https://scrape.evomi.com/api/v1/scraper/realtime?url={url}&api_key={api_key}", timeout=60 ) # Success if response.status_code == 200: return response # Rate limited elif response.status_code == 429: data = response.json() wait_time = data.get("reset", 60) print(f"Rate limited. Waiting {wait_time}s...") time.sleep(wait_time) continue # Server error - retry with backoff elif response.status_code >= 500: if attempt < max_retries - 1: wait_time = 2 ** attempt # 1s, 2s, 4s print(f"Server error. Retrying in {wait_time}s...") time.sleep(wait_time) continue else: return response # Client error - don't retry else: return response except requests.exceptions.Timeout: if attempt < max_retries - 1: wait_time = 2 ** attempt print(f"Timeout. Retrying in {wait_time}s...") time.sleep(wait_time) else: raise return None ``` ## Error Handling Best Practices ### 1. Always Check Status Code ```python response = scrape(url, api_key) if response.status_code == 200: # Success - process content content = response.text elif response.status_code == 402: # Insufficient credits send_alert("Low credits!") add_credits() elif response.status_code == 422: # Validation error data = response.json() log_error(data['message']) else: # Other error handle_error(response) ``` ### 2. Parse Error Messages ```python if not response.ok: try: error_data = response.json() error_msg = error_data.get('message', 'Unknown error') print(f"API Error: {error_msg}") except: print(f"HTTP {response.status_code}: {response.text}") ``` ### 3. Monitor Credit Headers ```python credits_remaining = float(response.headers.get('X-Credits-Remaining', 0)) if credits_remaining < 100: send_alert(f"Low credits: {credits_remaining}") if credits_remaining < 10: pause_scraping() ``` ### 4. Handle Timeouts Gracefully ```python response = scrape(url, api_key) if response.status_code == 202: # Task submitted or timeout data = response.json() if 'task_id' in data: # Poll for results result = wait_for_task(data['task_id'], api_key) return result ``` ### 5. Use Try-Except Blocks ```python try: response = requests.get(...) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: if e.response.status_code == 429: handle_rate_limit(e.response) else: log_error(e) except requests.exceptions.Timeout: log_error("Request timed out") except requests.exceptions.RequestException as e: log_error(f"Request failed: {e}") ``` ## Debugging Tips ### Enable Verbose Logging ```python import logging logging.basicConfig(level=logging.DEBUG) # Now all requests are logged response = requests.get(...) ``` ### Check Response Headers ```python # Print all headers for key, value in response.headers.items(): print(f"{key}: {value}") # Key headers to check print(f"Credits Used: {response.headers.get('X-Credits-Used')}") print(f"Mode Used: {response.headers.get('X-Mode-Used')}") print(f"Rate Limit: {response.headers.get('X-RateLimit-Remaining')}") ``` ### Test with Health Check ```bash curl "https://scrape.evomi.com/api/v1/scraper/health?api_key=YOUR_API_KEY" -v ``` ### Validate Parameters First ```python def validate_params(params): """Validate parameters before sending request""" # Check required fields if 'url' not in params: raise ValueError("url is required") # Check URL format if not params['url'].startswith(('http://', 'https://')): raise ValueError("url must include http:// or https://") # Check mode if 'mode' in params and params['mode'] not in ['request', 'browser', 'auto']: raise ValueError("mode must be 'request', 'browser', or 'auto'") return True ``` Don't retry non-retryable errors like validation errors (422) or authentication errors (401). These require fixing the request, not retrying. --- ## Parameters Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/ The Scraper API offers comprehensive control through a rich set of parameters. This reference covers all available options organized by category. | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `url` | string | Yes | - | Target URL to scrape (must include `http://` or `https://`) | | `mode` | string | No | `auto` | [Modes](/scraping-products-instructions/scraper-api/parameters/scraping-modes/): `request`, `browser`, or `auto` | | `language` | string | No | follows proxy locale | [Language](#language): language tags like `de-CH,de` — override the locale per mode | | `timezone` | string | No | derived from `proxy_country` | [Timezone](#timezone): IANA timezone like `Asia/Tokyo` — override the browser clock (browser/auto mode only) | | `proxy_state` | string | No | - | [Geo Targeting](#geo-targeting): state/province targeting like `california` (residential only) | | `proxy_city` | string | No | - | [Geo Targeting](#geo-targeting): city targeting like `san.jose` (residential only) | | `delivery` | string | No | `raw` | [Delivery](/scraping-products-instructions/scraper-api/parameters/output-formats/): `raw` (direct content) or `json` (wrapped) | | `async` | boolean | No | `false` | Submit task for background processing | `content` | string | No | `html` | [Delivery](/scraping-products-instructions/scraper-api/parameters/output-formats/): `html`, `markdown`, `pdf` or `screenshot` | | `include_content` | boolean | No | `false` | [Delivery](/scraping-products-instructions/scraper-api/parameters/output-formats/): Include full content in JSON responses | | `excluded_tags` | array/string | No | [] | [Filtering](/scraping-products-instructions/scraper-api/parameters/filtering/): HTML tag names to remove (e.g., ["script", "form"]) | | `excluded_selectors` | array/string | No | [] | [Filtering](/scraping-products-instructions/scraper-api/parameters/filtering/): CSS selectors to remove (e.g., [".tracker", "#ads"]) | | `extract_scheme` | object | No | - | [Extraction](/scraping-products-instructions/scraper-api/parameters/extraction/schema/) scheme using CSS, XPath, and Regex. | | `scheme_id` | str | No | - | [Extraction](/scraping-products-instructions/scraper-api/parameters/extraction/schema/) scheme ID using CSS, XPath, and Regex. From /schemes/. | | `js_instructions` | array | No | - | [JavaScript](/scraping-products-instructions/scraper-api/parameters/javascript-automation/) Structured actions to perform (click, fill, wait) | | `execute_js` | string | No | - | Raw [JavaScript](/scraping-products-instructions/scraper-api/parameters/javascript-automation/) code to execute | | `networkCapture` | array | No | - | [Network](/scraping-products-instructions/scraper-api/parameters/network-capture/) Filters to capture browser network responses (max 10 filters) | | `capture_headers` | bool | No | - | Captures the HTTP response and cookie headers | | `block_resources` | array/string | No | [] | Resource to block, options: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest` | | `use_default_storage` | boolean | No | false | Use your default [Cloud Storage](/scraping-products-instructions/scraper-api/cloud-storage/) configuration to use for uploading results. | | `storage_id` | string | No | - | Specific [Cloud Storage](/scraping-products-instructions/scraper-api/cloud-storage/) configuration ID to use for uploading results. | | `webhook` | object | No | - | [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) configuration for real-time notifications | ## Proxy Configuration | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `proxy_type` | string | `residential` | Proxy type: `residential` or `datacenter` | | `proxy_country` | string | `US` | Two-letter ISO country code (e.g., `GB`, `DE`, `JP`) | | `proxy_state` | string | - | State/province targeting, e.g. `california`, `new.york` (display names like `New York` are normalized; unknown values are rejected). Residential only | | `proxy_city` | string | - | City targeting, e.g. `san.jose`, `berlin` (display names like `Los Angeles` are normalized; unknown values are rejected). Residential only | | `proxy_session_id` | string | - | Session ID (6-8 characters) for IP persistence | | `proxy_overwrite` | string | - | Full proxy URL to override default proxies (geo targeting parameters are ignored) | ## Geo Targeting `proxy_country`, `proxy_state` and `proxy_city` target the requested location in all modes (request, browser, auto). Only available with residential proxies. Any well-formed value is passed through — unsupported locations fail at the proxy. Supported countries, regions and cities are listed in the [Evomi settings API](https://api.evomi.com/public/settings). Geo targeting does not apply when using `proxy_overwrite`. ## Language | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `language` | string | follows proxy | Comma-separated language tags, e.g. `de-CH,de` or `fr-CA,fr,en` (max 128 characters, no q-values) | Overrides the locale presented to the target site. **If you don't set `language`, the locale follows your proxy automatically** — you only need this parameter to force a language that differs from the proxy's location: | Mode | When `language` IS set | When `language` is NOT set (default) | |------|------------------------|--------------------------------------| | `request` | Sent as the `Accept-Language` header on the outgoing request | `Accept-Language` is derived from `proxy_country` using the country's official languages — e.g. `CH` → `de-CH,de;q=0.9,fr;q=0.8,it;q=0.7,en;q=0.6`, `US` → `en-US,en;q=0.9`. Unknown or missing country → `en-US,en;q=0.9` | | `browser` / `auto` | Sets the browser languages & locale | Browser locale follows the proxy IP / browser profile locale automatically | **Example — force German (Switzerland) through a US proxy:** ```json { "url": "https://example.com", "mode": "request", "proxy_country": "US", "language": "de-CH,de" } ``` **Notes:** - Tags are plain BCP-47 style codes separated by commas — no `q=` quality values (`de-CH,de` ✅, `de-CH,de;q=0.9` ❌). - In `request` mode, an explicit `Accept-Language` in `additional_headers` always wins over `language`. ## Timezone | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `timezone` | string | derived from `proxy_country` | IANA timezone, e.g. `Europe/Berlin`, `Asia/Tokyo` or `UTC` (max 64 characters) | Sets the browser clock used by `Date`, `Intl` and all JavaScript time APIs. **If you don't set `timezone`, the clock matches your proxy country** — the country's primary IANA zone is used: | Mode | When `timezone` IS set | When `timezone` is NOT set (default) | |------|------------------------|--------------------------------------| | `request` | No effect — plain HTTP requests carry no timezone | No effect | | `browser` / `auto` | Sets the browser clock to the given IANA zone | Clock derived from `proxy_country` — e.g. `DE` → `Europe/Berlin`, `JP` → `Asia/Tokyo`, `US` → `America/New_York` | **Example — Tokyo clock through a US proxy:** ```json { "url": "https://example.com", "mode": "browser", "proxy_country": "US", "timezone": "Asia/Tokyo" } ``` **Notes:** - The value must be a real IANA timezone name; well-formed but nonexistent zones (`Europe/Berlint`) are rejected with a validation error. - Multi-zone countries use the primary zone (`US` → `America/New_York`, `AU` → `Australia/Sydney`). Pass an explicit `timezone` when you need a specific region. - Using `proxy_overwrite` (your own proxy) without `timezone` or `proxy_country`? The clock defaults to US Eastern — pass `proxy_country` or `timezone` to match your exit IP. ## Browser Timing | Parameter | Type | Default | Range | Description | |-----------|------|---------|-------|-------------| | `wait_seconds` | integer | `0` | 0-30 | Seconds to wait after page load | | `wait_until` | string | `domcontentloaded` | - | Page ready state options | | `domcontentloaded` | - | - | - | DOM is ready (recommended) - Most dynamic sites | | `load` | - | - | - | All resources loaded - Complete page with images | | `networkidle` | - | - | - | No network activity - Heavy AJAX/API calls | | `commit` | - | - | - | Navigation committed (fastest) - Simple static pages | **Example:** ```json { "mode": "browser", "wait_until": "networkidle", "wait_seconds": 3 } ``` ## Device Emulation | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `device` | string | `windows` | Device to emulate options | | `windows` | - | - | Viewport: 1920×1080, User-Agent: Chrome/Windows, Scrolling: Enabled (5000px), Best For: Desktop sites, dashboards | | `macos` | - | - | Viewport: 1920×1080, User-Agent: Safari/macOS, Scrolling: Enabled (5000px), Best For: macOS-specific content | | `android` | - | - | Viewport: 375×667, User-Agent: Chrome/Android, Scrolling: Disabled, Best For: Mobile-first sites, apps | ## AI Enhancement | Parameter | Type | Default | Required When | Description | |-----------|------|---------|---------------|-------------| | `ai_enhance` | boolean | `false` | - | Enable AI processing | | `ai_source` | string | - | ai_enhance=true | Source: `markdown` or `screenshot` | | `ai_prompt` | string | - | No | Custom prompt for AI processing | | `ai_force_json` | boolean | `true` | No | Force JSON output format | ## Custom Headers | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `additional_headers` | object | - | Custom HTTP headers to send with the request | **Example:** ```json { "additional_headers": { "Authorization": "Bearer token123", "Accept-Language": "en-US,en;q=0.9", "Custom-Header": "value" } } ``` **Common use cases:** API authentication, language preferences, custom tracking headers. ## Visual Capture | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `screenshot` | boolean | `false` | Capture full-page screenshot (requires `mode=browser`) | Screenshots are: - Full-page PNG images (not just viewport) - Stored in Cloudflare R2 for 30 days - Accessible via `screenshot_uri` in response - Cost: +1 credit ## PDF Capture | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `pdf` | boolean | `false` | Capture full-page PDF document (requires `mode=browser`) | PDFs are: - Full-page vector documents (searchable and scalable) - Stored in Cloudflare R2 for 30 days - Accessible via `pdf_uri` in response - Cost: +1 credit ### Content Formats **`html`** - Raw HTML with absolute URLs **`markdown`** - Clean, readable text format (ideal for AI processing) **`screenshot`** - Full-page PNG image (requires `mode=browser`) **`pdf`** - Full-page PDF document (requires `mode=browser`) ## Parameter Validation The API validates all parameters before processing. Common validation errors: - **Invalid URL:** Must include `http://` or `https://` - **Invalid mode:** Must be `request`, `browser`, or `auto` - **Invalid proxy_country:** Must be a 2-letter ISO code - **Invalid proxy_session_id:** Must be 6-8 alphanumeric characters - **Invalid language:** Must be comma-separated language tags (2-3 letters, optional subtags), max 128 characters, no q-values - **Capture without browser:** `screenshot=true` | `pdf=true` require `mode=browser` - **AI without source:** `ai_enhance=true` requires `ai_source` - **Invalid filter:** `excluded_tags` | `excluded_selectors` require `content=markdown` | `content=html` Validation errors return `422 Unprocessable Entity` with details in the response. --- ## AI Enhancement Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/ai-enhancement/ Transform raw web content into structured insights with built-in AI processing. Extract summaries, generate structured data, and analyze content—all in a single API call. **AI Model:** Google Gemini 2.0 Flash **Cost:** +30 credits (additive to base cost) ## Quick Start Enable AI processing by setting `ai_enhance=true` and specifying a source: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://news.example.com/article", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract headline, author, date, and create a 2-sentence summary as JSON" }' ``` ## Parameters | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `ai_enhance` | Yes | `false` | Enable AI processing | | `ai_source` | Yes* | - | Source for AI: `markdown` or `screenshot` | | `ai_prompt` | No | - | Custom instructions for AI | | `ai_force_json` | No | `true` | Force JSON output format | *Required when `ai_enhance=true` ## AI Sources ### Markdown Source Process the text content of the page. **Value:** `markdown` ```json { "url": "https://blog.example.com/post", "ai_enhance": true, "ai_source": "markdown" } ``` **Best for:** - Article summarization - Data extraction from text - Content classification - Information extraction - Structured data generation - Q&A from documentation **Characteristics:** - Processes clean text (no HTML boilerplate) - Token-efficient - Fast processing - Ideal for text-heavy content ### Screenshot Source Process the visual appearance of the page using vision AI. **Value:** `screenshot` **Requirements:** - Must set `screenshot=true` - Requires `mode=browser` or `mode=auto` ```json { "url": "https://example.com", "mode": "browser", "screenshot": true, "ai_enhance": true, "ai_source": "screenshot" } ``` **Best for:** - Layout analysis - Visual content extraction - Image-based content - UI element detection - Design analysis - Charts and graphs **Characteristics:** - Processes visual representation - Can extract text from images - Understands layout and design - Higher processing cost ## Custom Prompts Guide AI processing with custom instructions via `ai_prompt`: ### Structured Data Extraction ```json { "ai_prompt": "Extract the article headline, author, publication date, and create a 2-sentence summary. Return as JSON with keys: headline, author, date, summary." } ``` **Example Response:** ```json { "ai_response": { "headline": "The Future of Web Scraping", "author": "Jane Doe", "date": "2025-01-15", "summary": "Web scraping is evolving with AI integration. Modern APIs now offer built-in intelligence for data extraction." } } ``` ### Bullet Point Summary ```json { "ai_prompt": "Summarize the main points in 5 concise bullet points." } ``` **Example Response:** ```json { "ai_response": { "summary": [ "Web scraping is becoming more intelligent", "AI helps extract structured data automatically", "Modern APIs combine scraping and AI processing", "Costs are predictable with transparent pricing", "Integration is simple with REST APIs" ] } } ``` ### Contact Information ```json { "ai_prompt": "Extract all contact information including email, phone, address, and social media links. Return as JSON." } ``` ### Product Data ```json { "ai_prompt": "Extract all product names, prices, and availability status. Return as a JSON array with fields: name, price, available." } ``` ### Visual Analysis (Screenshot Source) ```json { "ai_source": "screenshot", "ai_prompt": "Describe the page layout, identify main sections, and list all call-to-action buttons with their text and position." } ``` ## Default Behavior Without a custom prompt, AI provides intelligent defaults based on source: ### Default for Markdown ```json { "title": "Page Title", "description": "Brief description", "summary": "Concise content summary", "key_points": [ "Main point 1", "Main point 2", "Main point 3" ] } ``` ### Default for Screenshot ```json { "layout_description": "Visual layout description", "visible_text": ["Text element 1", "Text element 2"], "ui_elements": ["Button", "Form", "Navigation"], "call_to_action": ["Sign Up", "Learn More"] } ``` ## Response Formats ### With delivery=raw (Default) Returns ONLY the AI response: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "ai_enhance": true, "ai_source": "markdown", "delivery": "raw" }' ``` **Response:** ```json { "title": "Example Domain", "summary": "This domain is for use in illustrative examples...", "key_points": ["Used for examples", "No prior coordination needed"] } ``` **Headers:** ``` Content-Type: application/json; charset=utf-8 X-Response-Source: ai-raw-mode X-Credits-Used: 15.0 ``` ### With delivery=json Returns AI response in `ai_response` field: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "ai_enhance": true, "ai_source": "markdown", "delivery": "json" }' ``` **Response:** ```json { "success": true, "url": "https://example.com", "ai_response": { "title": "Example Domain", "summary": "This domain is for use in illustrative examples...", "key_points": ["Used for examples", "No prior coordination needed"] }, "credits_used": 15.0, "credits_remaining": 85.0, "mode_used": "browser" } ``` ## Force JSON Output By default, `ai_force_json=true` instructs AI to return valid JSON. **For free-form text responses:** ```json { "ai_enhance": true, "ai_source": "markdown", "ai_force_json": false, "ai_prompt": "Write a creative short story inspired by this content." } ``` ## Use Cases ### 1. News Article Extraction ```json { "url": "https://news.example.com/article/123", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract: headline, author, date, category, tags, and 2-sentence summary. Return as JSON." } ``` ### 2. E-commerce Product Scraping ```json { "url": "https://shop.example.com/product/456", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract product name, price, description, specifications, and availability. Return as structured JSON." } ``` ### 3. Job Posting Analysis ```json { "url": "https://jobs.example.com/posting/789", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract: job title, company, location, salary range, requirements, and responsibilities as JSON." } ``` ### 4. Business Directory ```json { "url": "https://directory.example.com/business/abc", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract business name, address, phone, email, website, hours, and services as JSON." } ``` ### 5. Documentation Summary ```json { "url": "https://docs.example.com/guide", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Create a concise summary of this documentation page with 5 key takeaways." } ``` ### 6. Visual Layout Analysis ```json { "url": "https://example.com", "mode": "browser", "screenshot": true, "ai_enhance": true, "ai_source": "screenshot", "ai_prompt": "Analyze the page layout. Identify header, navigation, main content area, sidebar, and footer. List all visible call-to-action buttons." } ``` ## Cost Calculation AI enhancement adds 30 credits to your base cost: | Configuration | Base Cost | AI Cost | Total | |--------------|-----------|---------|-------| | Request + Datacenter + AI | 1 | +10 | **11 credits** | | Request + Residential + AI | 2 | +10 | **12 credits** | | Browser + Residential + AI | 5 | +10 | **15 credits** | | Auto + Residential + AI (request succeeds) | 2 | +10 | **12 credits** | | Auto + Residential + AI (upgraded to browser) | 6 | +10 | **16 credits** | **With Screenshot:** | Configuration | Base + Screenshot | AI Cost | Total | |--------------|-------------------|---------|-------| | Browser + Residential + Screenshot + AI | 5 + 1 | +10 | **16 credits** | **Mode Restriction:** Browser and auto modes require premium (residential) proxies. Datacenter proxies can only be used with `mode="request"`. ## Best Practices ### 1. Be Specific in Prompts Bad: ```json {"ai_prompt": "Extract data"} ``` Good: ```json {"ai_prompt": "Extract product name, price in USD, and availability status as JSON with keys: name, price, available"} ``` ### 2. Request Structured Output Always specify the output format you want: ```json {"ai_prompt": "Extract contact info and return as JSON with fields: email, phone, address"} ``` ### 3. Use Markdown for Text Content For articles, blogs, and text-heavy pages: ```json { "content": "markdown", "ai_source": "markdown" } ``` This gives AI clean, token-efficient text. ### 4. Use Screenshots for Visual Content For image-heavy pages, dashboards, or UI analysis: ```json { "screenshot": true, "ai_source": "screenshot" } ``` ### 5. Combine with Other Features **With JavaScript automation:** ```json { "mode": "browser", "js_instructions": [ {"click": ".show-full-content"} ], "ai_enhance": true, "ai_source": "markdown" } ``` **With session management:** ```json { "proxy_session_id": "sess123", "ai_enhance": true, "ai_source": "markdown" } ``` ### 6. Test Prompts Iteratively Start simple and refine: ```json // Test 1 {"ai_prompt": "Summarize this article"} // Test 2 {"ai_prompt": "Summarize in 3 bullet points"} // Test 3 {"ai_prompt": "Summarize in 3 bullet points as JSON array"} ``` ### 7. Monitor Token Usage Longer content = higher processing time. For very long pages, consider extracting specific sections first. ## Limitations ### Content Length Very long pages may be truncated. For optimal results, keep source content under 50,000 characters. ### Language Support Gemini 2.0 Flash supports multiple languages, but English prompts generally yield best results. ### JSON Forcing While `ai_force_json=true` encourages JSON output, AI may occasionally return text if it's more appropriate for the prompt. ### Processing Time AI processing adds 2-5 seconds to request time. Factor this into timeout expectations. ## Error Handling ### AI Processing Failed ```json { "success": false, "error": "AI processing failed", "message": "Unable to process content with AI" } ``` **Solutions:** - Try with a simpler prompt - Check that content source has meaningful data - Verify source (markdown vs screenshot) is appropriate ### Invalid AI Source ```json { "success": false, "error": "Invalid parameters", "message": "ai_source is required when ai_enhance is true" } ``` **Solution:** Add `ai_source: markdown` or `ai_source: screenshot` AI responses are generated by Google Gemini and may occasionally be inconsistent or incomplete. Always validate critical extracted data. --- ## Extraction Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/extraction/ Reliable structured extraction is achieved through Css, Xpath, Regex, & Llms. Css, Xpath, & Regex extraction is communicated though the `extract_scheme` or `scheme_id` parameter which is a extraction scheme where each object defines a data/area "bucket." These objects act as either a **Rule** (to pull data) or a **Container** (to scope data). **Note:** `extract_scheme` can be defined with each scraper request or using a presaved configuration using `scheme_id`. View "Scheme" Tab. ## Defining an `extract_scheme` 1 - Select data/container with CSS/xpath/regex locators 2 - Nest to target extraction into subsections if needed 3 - Configure extract field types (content/attribute/exists/count) ## Extraction Parameters | Parameter | Type | Description | | :--- | :--- | :--- | | **`label`** | String | The key name for the field in the resulting JSON. | | **`type`** | Enum | `content` (text), `attribute` (links/src), `exists` (bool), `count` (int), or `nest`. | | **`selector`** | CSS | A standard CSS selector to target the element. | | **`xpath`** | XPath | A standard XPath expression for complex navigation. | | **`regex`** | Pattern | A Regular Expression to extract text from the raw HTML/text. | | **`attribute`** | String | **Required if type="attribute"**. Specify the target (e.g., `href`, `src`). | | **`fields`** | Array | **Required if type="nest"**. A recursive list of extraction objects. | --- Below is a standard configuration for extracting a list of blog posts from a page: ```json { "extract_scheme": [ { "label": "blog_posts", "type": "nest", "selector": "article.post-card", "fields": [ { "label": "title", "type": "content", "selector": "h2" }, { "label": "link", "type": "attribute", "selector": "a.main-link", "attribute": "href" }, { "label": "author_via_xpath", "type": "content", "xpath": ".//span[contains(@class, 'author-name')]" } ] } ] } ``` Set `delivery=json` to receive results, View "Output Formats" Tab. * **Hybrid Targeting:** You can **mix and match** locator types. For example, you can use a `selector` to find a parent container and then use `regex` or `xpath` to find specific data inside it. * **Use `regex` for raw strings:** Best when data isn't wrapped in its own tag (e.g., pulling a price out of a string like "Price: $45.00 only today"). * **Use `type: "nest"` for lists:** Use this for repeated elements like product grids to keep your JSON structured. * **Use `type: "exists"` for validation:** Perfect for checking if a "Sold Out" badge is present without needing the actual text. --- ## Request Mode with Filtering then Extracting (2 credits) ```json { "url": "https://spa-website.com", "mode": "request", "excluded_tags": ["aside", "form", "nav"], "excluded_selectors": [".ads"], "proxy_type": "residential", "proxy_country": "US", "extract_scheme": [ { "label": "main_content", "type": "nest", "selector": "div", "fields": [ { "label": "headline", "type": "content", "xpath": "//h1" }, { "label": "body_text", "type": "content", "selector": "p" }, { "label": "external_link", "type": "attribute", "selector": "a", "attribute": "href" } ] } ] } ``` ## AI Extraction | Parameter | Type | Default | Required When | Description | |-----------|------|---------|---------------|-------------| | `ai_enhance` | boolean | `false` | - | Enable AI processing | | `ai_source` | string | - | ai_enhance=true | Source: `markdown` or `screenshot` | | `ai_prompt` | string | - | No | Custom prompt for AI processing | | `ai_force_json` | boolean | `true` | No | Force JSON output format | **AI Model:** Google Gemini 2.0 Flash **Cost:** +30 credits (additive) **Example:** ```json { "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract product name, price, and availability as JSON" } ``` See [AI Enhancement](/scraping-products-instructions/scraper-api/parameters/ai-enhancement/) for detailed examples. --- ## Scheme Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/extraction/schema/ **Schemes** are reusable extraction Blueprints. Instead of defining your scheme logic with every request, you can save a configuration once and reference it by ID. Our system also supports **AI-assisted schemes**, allowing you to automatically generate and fix broken selectors using real page data. ## Endpoint Reference | Endpoint | Method | Action | | :--- | :--- | :--- | | `/account/schemes` | `POST` | Create a new scheme | | `/account/schemes` | `GET` | List all saved schemes | | `/account/schemes/{id}` | `GET` | Retrieve specific scheme details | | `/account/schemes/{id}` | `PUT` | Update an existing scheme | | `/account/schemes/{id}` | `DELETE` | Remove a scheme | | `/account/schemes/{id}/status` | `GET` | Check background processing status | **Authentication**: Include your API key in the `x-api-key` header for all requests. --- ## Configuration Parameters When creating or updating a scheme, use the following parameters: | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `name` | string | **Yes** | A unique nickname for your scheme. | | `config or config_id` | JSON | **Yes** | See **Config Object Details** | | `test` | boolean | No | Required if `fix` is true; validates the scheme against a URL. | | `fix` | boolean | No | If `true`, uses AI to repair or generate the scheme. | | `async` | boolean | No | Processes the request in the background. | | `extract_prompt` | string | Conditional | Required if `fix` is true; guides the AI on what to extract. | > **Config Details (`config`)** > The `config` object is a standard scraper api object and is compatible with any scrape parameter, with requirements based on your mode: > * **`url`**: **Always Required** if `test` or `fix` is enabled. > * **`extract_scheme`**: **Required** if `fix` is false. > * **Other**: View "Test" Tab ### Creation Logic | Mode | `test` | `fix` | System Behavior | | :--- | :--- | :--- | :--- | | **Create** | `false` | `false` | Saves the scheme config as-is. | | **Test** | `true` | `false` | Saves if it extracts data on chosen url. | | **Fix** | `true` | `true` | Fix and save scheme config on chosen url. | > **Credit Usage**: Using the `fix` feature costs the Scraper Api fee for your configuration + 30 credits for each Ai fix; Up 60 credits in total are possible for multiple Ai fixes. If the AI fails to generate a valid scheme, credits are automatically refunded. --- ## Usage Examples ### Create a scheme ```bash curl -X POST https://scrape.evomi.com/api/v1/account/schemes \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Product Details", "config": { "extract_scheme": [ { "label": "product_data", "selector": "body", "type": "nest", "fields": [ { "label": "price", "selector": ".p-price", "type": "content" } ] } ] } }' ``` ### List Your schemes ```bash curl -G "https://scrape.evomi.com/api/v1/account/schemes" \ -d "page=1" \ -d "per_page=10" \ -H "x-api-key: YOUR_API_KEY" ``` ### Scrape Using a scheme ID Once saved, you don't need to pass the full config anymore—just the ID. ```bash curl -X POST https://scrape.evomi.com/api/v1/scraper/realtime \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/item-123", "scheme_id": "sch_xxxxx" }' ``` --- ## Background Processing Creating or updating a scheme with `async` (or when `test`/`fix` exceeds the sync timeout) runs the scheme test as a background task. Every response belonging to that task carries the [task timing fields](/scraping-products-instructions/scraper-api/result-polling/#task-timing), mirrored as `X-Task-*` headers. **Submit (async), HTTP 202:** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "id": "sch_xxxxx", "status": "processing", "message": "Schema test submitted for background processing", "check_url": "/api/v1/account/schemes/sch_xxxxx/status", "credits_reserved": 35, "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check status (`GET /account/schemes/{scheme_id}/status`)** — note this endpoint is polled by **scheme id**, not task id: ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Scheme is still processing", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:13.455Z", "finished_at": null, "elapsed_seconds": 8.4 } ``` Once the test finishes, the same endpoint returns the test result with the final timing — `finished_at` set and `elapsed_seconds` frozen at its last value. A scheme that has never been tested returns `{"status": "not_tested"}` with no timing fields, since no task exists for it. --- ## Response Codes * **200/201**: Success / Created * **202**: Request accepted (Async processing) * **400**: Validation error (check your JSON syntax) * **402**: Insufficient credits * **404**: scheme ID not found * **500**: Internal server error --- ## Fix Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/extraction/schema/fix/ When `fix=true`, the system enters **Autonomous Mode**. We will automatically generate or improve your `extract_scheme` based on the requirements provided in your `extract_prompt`. If `success=False` is false the scheme configuration will not be saved. ## Generate or Fix Scheme Configuration | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `test` | boolean | **Yes** | Must be `true` to enable Ai. | | `fix` | boolean | **Yes** | Must be `true` to enable Ai. | | `extract_prompt` | string | **Yes** | Describe what to find (e.g., "Extract product title and price"). | `extract_scheme` | array | No | **Optional.** Provide a valid or broken scheme, a rough outline ect.| --- ## How It Works Simplifed 1. **Initial Analysis**: The system scrapes the target page using your `config` and the Scraper Api. 2. **AI Synthesis**: Our AI analyzes the page structure against your `extract_prompt`. 3. **Generation**: A new `generated_scheme` is created. 4. **Validation**: The system runs a test scrape using the new scheme. 5. **Finalization**: The scheme is saved when extraction quality meets our standards. --- ## Example Request ```bash curl -X POST https://scrape.evomi.com/api/v1/account/schemes \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "AI Product scheme", "config": { "url": "https://example.com/product" }, "test": true, "fix": true, "extract_prompt": "Extract the product name, current price, and gallery images" }' ``` --- ## Response Format ```json { "success": true, "scheme_id": 115, "generated_scheme": [ { "label": "title", "selector": "h1.product-title", "type": "content" } ], "message": "Quality adequate: 0/4 empty (0.0%)", "scrape_result": { "..." } } ``` --- > **Credits**: Requires credits for the initial scrape plus the AI fee of 30 or 60 credits depending on fixes needed. --- ## Test Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/extraction/schema/test/ Set `test=true` to validate your `extract_scheme` against a live target URL before the system saves it. If `success=False` is false the scheme configuration will not be saved. ## Validation Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `test` | boolean | **Yes** | Set to `true` to enable validation. | | `config.url` | string | **Yes** | The live URL to test your selectors against. | | `config.extract_scheme` | array | **Yes** | The scheme you want to verify (unless `fix=true`). | --- ## How It Works Simplfied 1. **Live Scrape**: The system initiates a request to your test URL using the provided config. 2. **Data Extraction**: It applies your `extract_scheme` to the live page content. 3. **Quality Check**: The system evaluates the results for extraction quality and only saves if so. --- ## Example Request ```bash curl -X POST https://scrape.evomi.com/api/v1/account/schemes \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Live Test scheme", "test": true, "config": { "url": "https://example.com", "extract_scheme": [ { "label": "title", "selector": "h1", "type": "content" } ] } }' ``` --- ## Response Format A successful test returns the extraction results and the confirmed `scheme_id`. ```json { "success": true, "scheme_id": "sch_xxxx", "message": "Quality adequate: 0/4 empty (0.0%)", "scrape_result": { "extraction": [ {"label": "title", "content": "Example Domain"}, ] } } --- ## Filtering Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/filtering/ | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `excluded_tags` | array/string | `[]` | HTML tag names to remove (e.g., `["script", "form"]`) | | `excluded_selectors` | array/string | `[]` | CSS selectors to remove (e.g., `[".tracker", "#ads"]`) | These parameters are only available when `content=markdown`, `content=html`, or `ai_source=markdown`. **Note:** `content=`, `ai_source=`, & `extraction=` are affected by the exclusions. When `content=markdown`, some HTML tags are automatically excluded. JSON delivery omits content by default. Set `include_content=true` to include it, or use `delivery=raw` for direct content. ## Request Mode with Filtering (2 credits) ```json { "url": "https://spa-website.com", "mode": "request", "excluded_tags": ["aside", "form"], "excluded_selectors": [".ads"], "proxy_type": "residential", "proxy_country": "US" } ``` ## Parameter Validation The API validates all parameters before processing. Common validation errors: - **Invalid filter:** `excluded_tags` | `excluded_selectors` require `content=markdown` | `content=html` Validation errors return `422 Unprocessable Entity` with details in the response. --- ## JS Automation Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/javascript-automation/ Execute custom interactions on web pages before extracting content. Click buttons, fill forms, and wait for dynamic elements—all without managing browsers yourself. JavaScript automation requires `mode=browser` or `mode=auto`. It's automatically available when browser rendering is used. ## Structured Actions Use `js_instructions` to define a sequence of actions to perform: ```json { "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": "#search-input"}, {"fill": ["#search-input", "web scraping"]}, {"click": "#search-button"}, {"wait": 2000} ] } ``` ## Supported Actions ### Click Click any element on the page. **Syntax:** `{"click": "selector"}` ```json {"click": "#submit-button"} {"click": ".cookie-accept"} {"click": "button[type='submit']"} ``` **Example:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "js_instructions": [ {"click": ".show-more-button"} ] }' ``` ### Fill Fill form fields with text. **Syntax:** `{"fill": ["selector", "value"]}` ```json {"fill": ["#email", "user@example.com"]} {"fill": ["input[name='username']", "johndoe"]} {"fill": ["#search-box", "product search term"]} ``` **Example:** ```json { "js_instructions": [ {"fill": ["#username", "john@example.com"]}, {"fill": ["#password", "secret123"]}, {"click": "#login-button"} ] } ``` ### Wait Pause execution for a specified duration. **Syntax:** `{"wait": milliseconds}` ```json {"wait": 1000} // 1 second {"wait": 2500} // 2.5 seconds {"wait": 5000} // 5 seconds ``` **Example:** ```json { "js_instructions": [ {"click": "#load-more"}, {"wait": 3000}, // Wait for content to load {"click": "#load-more"} ] } ``` ### Wait For Wait until an element appears on the page. **Syntax:** `{"wait_for": "selector"}` ```json {"wait_for": ".results"} {"wait_for": "#dynamic-content"} {"wait_for": "div.loaded"} ``` **Example:** ```json { "js_instructions": [ {"click": "#search-button"}, {"wait_for": ".search-results"} // Wait until results appear ] } ``` ## CSS Selectors Use standard CSS selectors to target elements: | Selector | Description | Example | |----------|-------------|---------| | `#id` | Element by ID | `#submit-btn` | | `.class` | Elements by class | `.accept-cookies` | | `tag` | By tag name | `button`, `input` | | `[attribute]` | By attribute | `[name="email"]`, `[type="submit"]` | | `parent > child` | Direct child | `.nav > a` | | `ancestor descendant` | Descendant | `.container button` | | `:pseudo` | Pseudo-selector | `:first-child`, `:last-of-type` | ## Common Patterns ### 1. Search Form Interaction ```json { "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": "#search-input"}, {"fill": ["#search-input", "web scraping"]}, {"click": "#search-button"}, {"wait": 2000}, {"wait_for": ".search-results"} ] } ``` ### 2. Login Flow ```json { "url": "https://example.com/login", "mode": "browser", "js_instructions": [ {"fill": ["input[name='username']", "user@example.com"]}, {"fill": ["input[name='password']", "password123"]}, {"click": "button[type='submit']"}, {"wait_for": ".dashboard"} ] } ``` ### 3. Accept Cookie Banner ```json { "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": ".cookie-banner"}, {"click": ".accept-cookies"}, {"wait": 500} ] } ``` ### 4. Load More Content ```json { "url": "https://example.com", "mode": "browser", "js_instructions": [ {"click": ".load-more"}, {"wait": 2000}, {"wait_for": ".new-content"}, {"click": ".load-more"}, {"wait": 2000} ] } ``` ### 5. Navigate Menu ```json { "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": "nav.main-menu"}, {"click": "nav a[href='/products']"}, {"wait": 1000}, {"wait_for": ".product-grid"} ] } ``` ### 6. Multi-Step Form ```json { "url": "https://example.com/form", "mode": "browser", "js_instructions": [ {"fill": ["#firstName", "John"]}, {"fill": ["#lastName", "Doe"]}, {"fill": ["#email", "john@example.com"]}, {"fill": ["#phone", "555-1234"]}, {"click": "#submit"}, {"wait_for": ".success-message"} ] } ``` ## Raw JavaScript Execution For advanced use cases, execute custom JavaScript code with `execute_js`: ```json { "url": "https://example.com", "mode": "browser", "execute_js": "return document.title + ' - ' + window.location.href;" } ``` The result is available in the `js_result` field: ```json { "success": true, "js_result": "Example Domain - https://example.com/" } ``` ### Extract Data with JavaScript ```json { "execute_js": "return Array.from(document.querySelectorAll('.product')).map(p => ({name: p.querySelector('.name').textContent, price: p.querySelector('.price').textContent}));" } ``` **Response:** ```json { "js_result": [ {"name": "Product 1", "price": "$19.99"}, {"name": "Product 2", "price": "$29.99"} ] } ``` ### Modify Page Before Scraping ```json { "execute_js": "document.querySelectorAll('.ad').forEach(ad => ad.remove()); return 'Ads removed';" } ``` ## Response Format ### Instructions Result When using `js_instructions`, the response includes execution details: ```json { "js_instructions_result": [ {"action": "wait_for", "selector": "#button", "status": "success"}, {"action": "click", "selector": "#button", "status": "success"}, {"action": "wait", "duration": 2000, "status": "success"} ] } ``` ### Execute JS Result When using `execute_js`, the return value is in `js_result`: ```json { "js_result": "Return value from your JavaScript code" } ``` ## Best Practices ### 1. Always Wait for Dynamic Elements Bad: ```json [ {"click": "#dynamic-button"} ] ``` Good: ```json [ {"wait_for": "#dynamic-button"}, {"click": "#dynamic-button"} ] ``` ### 2. Add Delays After Actions Give pages time to respond to interactions: ```json [ {"click": "#submit"}, {"wait": 1000}, // Wait for response {"wait_for": ".result"} ] ``` ### 3. Use Specific Selectors Bad (too generic): ```json {"click": "button"} ``` Good (specific): ```json {"click": "#submit-btn"} {"click": "button.primary-action"} ``` ### 4. Test Selectors First Before automating, test your selectors in the browser console: ```javascript document.querySelector("#your-selector") ``` ### 5. Handle Errors Gracefully If an element isn't found, the API will retry automatically. Use `wait_for` to ensure elements exist: ```json [ {"wait_for": ".ajax-content"}, // Ensures element loads {"click": ".ajax-content button"} ] ``` ### 6. Choose Appropriate Wait Times | Scenario | Wait Time | |----------|-----------| | Fast interactions | 500-1000ms | | API/AJAX calls | 2000-3000ms | | Page transitions | 3000-5000ms | | Heavy animations | 2000ms+ | ### 7. Combine with Other Features **With Screenshots:** ```json { "mode": "browser", "screenshot": true, "js_instructions": [ {"wait_for": ".modal"}, {"click": ".close-modal"} ] } ``` **With Resource Blocking:** ```json { "mode": "browser", "block_resources": ["image", "font"], "js_instructions": [ {"click": "#load-more"} ] } ``` **With AI Enhancement:** ```json { "mode": "browser", "js_instructions": [ {"click": ".show-full-article"} ], "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Summarize in 3 bullet points" } ``` ## Limitations ### Maximum Actions Keep instruction sequences reasonable (< 20 actions). Very long sequences may timeout. ### Timeout Limits Browser mode has a 45-second timeout. Ensure your instruction sequence completes within this time. ### Element Visibility Elements must be visible on the page to be clicked. Hidden or off-screen elements may fail. ### Dynamic Content For heavily dynamic sites, increase `wait_seconds` at the page level: ```json { "mode": "browser", "wait_seconds": 5, "js_instructions": [...] } ``` ## Troubleshooting ### Element Not Found ```json {"action": "click", "selector": "#button", "status": "failed", "error": "Element not found"} ``` **Solutions:** - Add `{"wait_for": "#button"}` before clicking - Check selector spelling in browser DevTools - Ensure element isn't in an iframe - Use more specific selector ### Click Not Working **Possible causes:** - Element is covered by another element - Element is outside viewport - Element requires specific wait condition **Solutions:** ```json [ {"wait_for": "#button"}, {"wait": 500}, // Give page time to settle {"click": "#button"} ] ``` ### Form Fill Not Working **Solutions:** - Ensure input field is visible and enabled - Wait for field to be ready: ```json [ {"wait_for": "#email"}, {"wait": 200}, {"fill": ["#email", "user@example.com"]} ] ``` ### Timeout Errors If instructions timeout, optimize your sequence: - Remove unnecessary waits - Use `wait_for` instead of fixed `wait` - Block resources to speed up page load - Consider splitting into multiple requests JavaScript automation requires browser mode, which only works with residential proxies. Each automated request costs 5 credits (flat rate). Add +1 credit for screenshots and +30 credits for AI enhancement. --- ## Network Capture Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/network-capture/ Network Capture allows you to intercept and capture HTTP responses made by the browser during page load. This is powerful for extracting API responses, JSON data, and other network traffic that wouldn't be visible in the page HTML. ## Overview When scraping JavaScript-heavy applications, the data you need often comes from XHR/Fetch API calls rather than the initial HTML. Network Capture lets you define filters to capture these responses automatically. Network Capture only works with `mode=browser` or `mode=auto`. It is not available in `mode=request` since there is no browser to intercept network traffic. ## Basic Usage | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `networkCapture` | array | - | Array of filter objects (max 10 filters) | **Simple Example:** ```json { "url": "https://example.com/products", "mode": "browser", "networkCapture": [ { "filterType": "resourceType", "resourceType": "fetch", "httpResponseBody": true } ] } ``` This captures all Fetch API responses and includes the response body. ## Filter Types Network Capture supports six filter types, plus a composite type for combining filters. ### Resource Type Filter Capture responses by resource type. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"resourceType"` | | `resourceType` | string | Yes | One of: `document`, `xhr`, `fetch` | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture all XHR requests:** ```json { "filterType": "resourceType", "resourceType": "xhr", "httpResponseBody": true } ``` **Available Resource Types:** | Type | Description | |------|-------------| | `document` | Main HTML document requests | | `xhr` | XMLHttpRequest (AJAX) calls | | `fetch` | Fetch API requests | ### URL Filter Capture responses matching a URL pattern. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"url"` | | `value` | string | Yes | URL pattern to match (3-8192 characters) | | `matchType` | string | No | How to match: `contains` (default), `startsWith`, `endsWith`, `exact` | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture API endpoints:** ```json { "filterType": "url", "value": "/api/v1/products", "matchType": "contains", "httpResponseBody": true } ``` **Match Types:** | Type | Description | Example Pattern | Matches | |------|-------------|-----------------|---------| | `contains` | URL contains the value | `/api/` | `https://site.com/api/users` | | `startsWith` | URL starts with the value | `https://api.` | `https://api.site.com/data` | | `endsWith` | URL ends with the value | `.json` | `https://site.com/data.json` | | `exact` | URL exactly matches | `https://site.com/api` | Only `https://site.com/api` | ### Status Code Filter Capture responses with specific HTTP status codes. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"statusCode"` | | `statusCodes` | array | Yes | Array of status codes to capture | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture successful responses:** ```json { "filterType": "statusCode", "statusCodes": [200, 201], "httpResponseBody": true } ``` **Example - Capture errors for debugging:** ```json { "filterType": "statusCode", "statusCodes": [400, 401, 403, 404, 500], "httpResponseBody": true } ``` ### Content Type Filter Capture responses by MIME type. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"contentType"` | | `contentTypes` | array | Yes | Array of content types to match | | `matchType` | string | No | How to match: `contains` (default), `startsWith`, `endsWith`, `exact` | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture JSON responses:** ```json { "filterType": "contentType", "contentTypes": ["application/json"], "matchType": "contains", "httpResponseBody": true } ``` **Example - Capture multiple content types:** ```json { "filterType": "contentType", "contentTypes": ["application/json", "text/javascript", "application/xml"], "httpResponseBody": true } ``` ### HTTP Method Filter Capture responses by HTTP method. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"method"` | | `methods` | array | Yes | Array of HTTP methods | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture POST responses:** ```json { "filterType": "method", "methods": ["POST"], "httpResponseBody": true } ``` **Example - Capture data-modifying requests:** ```json { "filterType": "method", "methods": ["POST", "PUT", "PATCH"], "httpResponseBody": true } ``` ### Header Filter Capture responses containing a specific header with a matching value. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"header"` | | `headerName` | string | Yes | Name of the header to check | | `headerValue` | string | Yes | Value to match | | `matchType` | string | No | How to match: `contains` (default), `startsWith`, `endsWith`, `exact` | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | **Example - Capture responses with custom header:** ```json { "filterType": "header", "headerName": "X-Api-Version", "headerValue": "v2", "matchType": "exact", "httpResponseBody": true } ``` ### Composite Filter Combine multiple filters with AND logic. A response must match ALL conditions to be captured. | Field | Type | Required | Description | |-------|------|----------|-------------| | `filterType` | string | Yes | Must be `"composite"` | | `conditions` | array | Yes | Array of simple filters (2-10 conditions) | | `httpResponseBody` | boolean | No | Include response body (default: `false`) | Composite filters cannot be nested. The `conditions` array must contain only simple filter types (not other composite filters). **Example - Capture JSON API responses:** ```json { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "/api/", "matchType": "contains" }, { "filterType": "contentType", "contentTypes": ["application/json"] }, { "filterType": "statusCode", "statusCodes": [200] } ], "httpResponseBody": true } ``` This captures responses that: - Have `/api/` in the URL **AND** - Return `application/json` content type **AND** - Have a 200 status code ## Response Format Captured network responses are returned in the `networkCapture` field of the API response. ### Response Structure ```json { "success": true, "url": "https://example.com/products", "networkCapture": [ { "interceptionStatus": { "status": "success", "error": null }, "statusCode": 200, "url": "https://example.com/api/v1/products", "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "httpResponseBody": "{\"products\":[...]}", "filter": { "filterType": "url", "value": "/api/", "matchType": "contains", "httpResponseBody": true }, "request": { "url": "https://example.com/api/v1/products", "method": "GET", "headers": { "accept": "application/json" }, "body": null } } ] } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `interceptionStatus` | object | Status of the capture operation | | `interceptionStatus.status` | string | `"success"` or `"error"` | | `interceptionStatus.error` | string | Error message if status is `"error"` | | `statusCode` | integer | HTTP status code of the response | | `url` | string | Full URL of the captured request | | `headers` | object | Response headers as key-value pairs | | `httpResponseBody` | string | Response body (only if `httpResponseBody: true` in filter) | | `filter` | object | The filter that matched this response | | `request` | object | Details about the original request | | `request.url` | string | Request URL | | `request.method` | string | HTTP method (GET, POST, etc.) | | `request.headers` | object | Request headers | | `request.body` | string | Request body (for POST/PUT requests) | ## Practical Examples ### Capture Product API Data Extract product data from an e-commerce site that loads products via API: ```json { "url": "https://shop.example.com/category/electronics", "mode": "browser", "wait_until": "networkidle", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "/products", "matchType": "contains" }, { "filterType": "resourceType", "resourceType": "fetch" } ], "httpResponseBody": true } ] } ``` ### Capture GraphQL Responses Capture GraphQL API responses: ```json { "url": "https://app.example.com/dashboard", "mode": "browser", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "/graphql", "matchType": "endsWith" }, { "filterType": "method", "methods": ["POST"] } ], "httpResponseBody": true } ] } ``` ### Debug Failed Requests Capture failed API calls for debugging: ```json { "url": "https://example.com/app", "mode": "browser", "networkCapture": [ { "filterType": "statusCode", "statusCodes": [400, 401, 403, 404, 500, 502, 503], "httpResponseBody": true } ] } ``` ### Capture Multiple Endpoints Use multiple filters to capture different types of responses: ```json { "url": "https://example.com/dashboard", "mode": "browser", "networkCapture": [ { "filterType": "url", "value": "/api/users", "matchType": "contains", "httpResponseBody": true }, { "filterType": "url", "value": "/api/orders", "matchType": "contains", "httpResponseBody": true }, { "filterType": "url", "value": "/api/products", "matchType": "contains", "httpResponseBody": true } ] } ``` ### Capture JSON with Size Filtering Capture only JSON responses with specific characteristics: ```json { "url": "https://example.com/search", "mode": "browser", "wait_until": "networkidle", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "contentType", "contentTypes": ["application/json"], "matchType": "contains" }, { "filterType": "statusCode", "statusCodes": [200] }, { "filterType": "resourceType", "resourceType": "xhr" } ], "httpResponseBody": true } ] } ``` ## Best Practices ### Use Specific Filters Be as specific as possible with your filters to avoid capturing unnecessary data: ```json // Good - specific URL pattern { "filterType": "url", "value": "/api/v2/products/search", "matchType": "contains" } // Less ideal - too broad { "filterType": "resourceType", "resourceType": "fetch" } ``` ### Combine with wait_until Use `wait_until: "networkidle"` to ensure all API calls complete before the response is returned: ```json { "url": "https://example.com", "mode": "browser", "wait_until": "networkidle", "networkCapture": [...] } ``` ### Only Request Body When Needed Setting `httpResponseBody: true` increases response size. Only enable it when you need the actual response content: ```json // When you need response bodies { "filterType": "url", "value": "/api/data", "httpResponseBody": true } // When you only need to know what requests were made { "filterType": "url", "value": "/api/data", "httpResponseBody": false } ``` ### Use Composite for Precision Composite filters help avoid capturing unwanted responses: ```json { "filterType": "composite", "conditions": [ {"filterType": "url", "value": "/api/"}, {"filterType": "statusCode", "statusCodes": [200]}, {"filterType": "contentType", "contentTypes": ["application/json"]} ], "httpResponseBody": true } ``` ## Test Examples Test Network Capture with these reliable sites. Copy and run immediately. ### HTTPBin - Basic JSON Response HTTPBin is a simple HTTP testing service: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "url": "https://httpbin.org/", "mode": "browser", "delivery": "json", "wait_until": "networkidle", "networkCapture": [ { "filterType": "url", "value": "httpbin.org", "matchType": "contains", "httpResponseBody": true } ] }' ``` **What you'll capture:** All requests made to httpbin.org. --- ### JSONPlaceholder - Fake REST API JSONPlaceholder provides a free fake REST API: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "url": "https://jsonplaceholder.typicode.com/", "mode": "browser", "delivery": "json", "wait_until": "networkidle", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "jsonplaceholder.typicode.com", "matchType": "contains" }, { "filterType": "contentType", "contentTypes": ["application/json"] } ], "httpResponseBody": true } ] }' ``` **What you'll capture:** JSON responses from the fake API. --- ### DummyJSON - Products API DummyJSON provides fake product data: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "url": "https://dummyjson.com/", "mode": "browser", "delivery": "json", "wait_until": "networkidle", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "dummyjson.com", "matchType": "contains" }, { "filterType": "statusCode", "statusCodes": [200] } ], "httpResponseBody": true } ] }' ``` **What you'll capture:** Product data, user info, and cart data as JSON. --- ### GitHub - Capture Repository API GitHub's web interface makes API calls to load repository data: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "url": "https://github.com/facebook/react", "mode": "browser", "delivery": "json", "wait_until": "networkidle", "networkCapture": [ { "filterType": "composite", "conditions": [ { "filterType": "url", "value": "latest-commit/main", "matchType": "contains" }, { "filterType": "contentType", "contentTypes": ["application/json"] } ], "httpResponseBody": true } ] }' ``` **What you'll capture:** Repository stats, contributor data, and more. --- ### Capture All XHR/Fetch Requests (Any Site) A generic example that captures all XHR/Fetch requests from any site: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "url": "YOUR_TARGET_URL", "mode": "browser", "delivery": "json", "wait_until": "networkidle", "networkCapture": [ { "filterType": "resourceType", "resourceType": "xhr", "httpResponseBody": true }, { "filterType": "resourceType", "resourceType": "fetch", "httpResponseBody": true } ] }' ``` **What you'll capture:** All XHR and Fetch API calls made by the page. HTTPBin, JSONPlaceholder, and DummyJSON are free public test APIs - perfect for learning how Network Capture works. ## Limitations - Maximum of **10 filters** per request or **15 MB** whichever first occurs - Composite filters support **2-10 conditions** - Only available in `mode=browser` or `mode=auto` - URL pattern length: 3-8192 characters --- ## Output Formats Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/output-formats/ The Scraper API offers flexible output formats to match your use case—from raw HTML for downstream processing to clean Markdown for AI applications, or full-page screenshots for visual analysis. ## Content Formats Control the output format with the `content` parameter: ### HTML (Default) Raw HTML with absolute URLs, perfect for parsing or archival. **Value:** `html` ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&content=html&api_key=YOUR_API_KEY" ``` **Characteristics:** - Complete HTML document with ``, ``, etc. - All relative URLs converted to absolute - Includes meta tags, scripts, and styles - Original page structure preserved **Best for:** - HTML parsing with BeautifulSoup, Cheerio, etc. - Archiving complete pages - Custom processing pipelines - Extracting structured data with CSS selectors ### Markdown Clean, readable text format ideal for AI processing and content analysis. **Value:** `markdown` ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://blog.example.com/post&content=markdown&api_key=YOUR_API_KEY" ``` **Characteristics:** - Removes boilerplate (headers, footers, navigation) - Preserves structure (headings, lists, tables) - Converts links to markdown format: `[text](url)` - Strips scripts, styles, and unnecessary elements - Makes URLs absolute - Lightweight and token-efficient **Best for:** - LLM/AI input (GPT, Claude, etc.) - Content summarization - Text analysis and NLP - Knowledge base extraction - Article archival **Example output:** ```markdown # Blog Post Title Published on January 15, 2025 This is the main content with preserved **formatting** and [links](https://example.com). ## Section Heading - Bullet point one - Bullet point two More content here... ``` ### Screenshot Full-page PNG image capture. **Value:** `screenshot` ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "content": "screenshot" }' \ -o screenshot.png ``` **Characteristics:** - PNG format - Full page (not just viewport) - Captures everything a user would see - Stored in CDN for 30 days - Available as URI and base64 data **Requirements:** - Must use `mode=browser` or `mode=auto` - Automatically sets `screenshot=true` **Cost:** +1 credit (additive) **Best for:** - Visual documentation - UI/UX testing - Page monitoring and change detection - Visual AI processing - Compliance and archival ### PDF Full-page PDF document. **Value:** `pdf` ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "content": "pdf" }' \ -o document.pdf ``` **Characteristics:** - PDF format (vector, searchable, scalable) - Full page document - Stored in CDN for 30 days - Available as URI and base64 data **Requirements:** - Must use `mode=browser` or `mode=auto` - Automatically sets `pdf=true` **Cost:** +1 credit (additive) **Best for:** - Document archiving - Print-ready formats - Legal/compliance documentation - Content preservation ## Delivery Modes Control how the response is delivered with the `delivery` parameter: ### Raw Delivery (Default) Returns content directly with appropriate Content-Type header. **Value:** `raw` ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=raw&api_key=YOUR_API_KEY" ``` **Response:** ``` Content-Type: text/html; charset=utf-8 X-Credits-Used: 1.0 X-Credits-Remaining: 99.0 X-Task-Created-At: 2026-09-28T20:31:12.482Z X-Task-Started-At: 2026-09-28T20:31:14.102Z X-Task-Finished-At: 2026-09-28T20:31:28.771Z X-Task-Elapsed-Seconds: 16.3 ... ``` With raw delivery the body is the page itself, so task timing is available only in the `X-Task-*` headers — see [Result Polling → Task Timing](/scraping-products-instructions/scraper-api/result-polling/#task-timing). **Content-Type mapping:** | Content Format | Content-Type | |----------------|--------------| | `html` | `text/html; charset=utf-8` | | `markdown` | `text/markdown; charset=utf-8` | | `screenshot` | `image/png` | | `pdf` | `application/pdf` | **Best for:** - Direct file saving - Piping to other tools - Minimal overhead - Browser viewing ### JSON Delivery Returns structured JSON with metadata and optional content. **Value:** `json` ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&api_key=YOUR_API_KEY" ``` **Default response (without content):** ```json { "success": true, "id": "task_abc123", "url": "https://example.com", "domain": "example.com", "title": "Example Domain", "meta": { "description": "Example page description", "keywords": "example, demo" }, "status_code": 200, "credits_used": 1.0, "credits_remaining": 99.0, "mode_used": "auto (request)", "hints": ["Content omitted by default. Set include_content=true to include it."] } ``` By default, JSON delivery omits the full content to save bandwidth and reduce response size. Add `include_content=true` to include it. **With content included:** ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&include_content=true&api_key=YOUR_API_KEY" ``` ```json { "success": true, "url": "https://example.com", "domain": "example.com", "title": "Example Domain", "content": "...", "status_code": 200, "credits_used": 1.0, "credits_remaining": 99.0, "mode_used": "auto (request)" } ``` **Best for:** - Structured API responses - Metadata extraction - Credit tracking - Status code checking - Multi-field responses ## Format Combinations | Content | Delivery | Result | |---------|----------|--------| | `html` | `raw` | Raw HTML with `text/html` Content-Type | | `html` | `json` | JSON with HTML in `content` field (if `include_content=true`) | | `markdown` | `raw` | Raw Markdown with `text/markdown` Content-Type | | `markdown` | `json` | JSON with Markdown in `content` field (if `include_content=true`) | | `screenshot` | `raw` | PNG binary with `image/png` Content-Type | | `screenshot` | `json` | JSON with `screenshot_uri` field | | `pdf` | `raw` | PDF binary with `application/pdf` Content-Type | | `pdf` | `json` | JSON with `pdf_uri` field | ## Use Case Examples ### 1. Save HTML to File ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&content=html&delivery=raw&api_key=YOUR_API_KEY" \ -o page.html ``` ### 2. Get Metadata Only (No Content) Perfect for checking pages before full scraping: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&api_key=YOUR_API_KEY" ``` Returns title, status code, credits used, but no content—saves bandwidth and money. ### 3. Extract Clean Text for AI ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://blog.example.com/post&content=markdown&delivery=raw&api_key=YOUR_API_KEY" \ | python process_with_ai.py ``` ### 4. Get Screenshot with Metadata ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "screenshot": true, "delivery": "json" }' ``` **Response:** ```json { "success": true, "url": "https://example.com", "screenshot_uri": "https://scraper-results.evomi.com/screenshots/task_abc123.png", "credits_used": 6.0, "credits_remaining": 94.0 } ``` ### 5. Markdown with Full JSON Response ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://blog.example.com/post", "content": "markdown", "delivery": "json", "include_content": true }' ``` **Response:** ```json { "success": true, "url": "https://blog.example.com/post", "title": "How to Build Scalable APIs", "content": "# How to Build Scalable APIs\n\nPublished...", "meta": { "description": "Learn best practices...", "author": "John Doe" }, "credits_used": 1.0 } ``` ## Special Response Fields ### With Extract_scheme `extract_scheme` returns data in the specified scheme ```json { "extraction": [ { "title": "Example Page Title", "description": "This is a sample description text", "contact_email": "info@example.com" }, { "phone_numbers": ["555-0123", "555-0456"], "social_links": { "twitter": "https://twitter.com/example", "facebook": "https://facebook.com/example" } }, [] //No data found ] } ``` ### With Screenshots When screenshots are captured: ```json { "screenshot_uri": "https://scraper-results.evomi.com/screenshots/task_abc123.png", "screenshot_data": "iVBORw0KGgoAAAANSUhEUgAA..." // base64 encoded } ``` - **`screenshot_uri`:** Public CDN URL (available for 30 days) - **`screenshot_data`:** Base64 encoded PNG data (in JSON responses) ### With PDFs When PDFs are captured: ```json { "pdf_uri": "https://scraper-results.evomi.com/pdfs/task_abc123.png", "pdf_data": "iVBORw0KGgoAAAANSUhEUgAA..." // base64 encoded } ``` - **`pdf_uri`:** Public CDN URL (available for 30 days) - **`pdf_data`:** Base64 encoded PDF data (in JSON responses) ### With AI Enhancement When AI processing is enabled: ```json { "ai_response": { "summary": "This article explains...", "key_points": ["Point 1", "Point 2"], "headline": "Main Title" } } ``` See [AI Enhancement](/scraping-products-instructions/scraper-api/parameters/ai-enhancement/) for details. ### With JavaScript Execution When JavaScript is executed: ```json { "js_result": "Return value from execute_js", "js_instructions_result": [ {"action": "click", "selector": "#button", "status": "success"} ] } ``` ## Best Practices ### Choose the Right Format **Use HTML when:** - Parsing with DOM libraries (BeautifulSoup, Cheerio) - Need exact page structure - Building web archives - Custom processing required **Use Markdown when:** - Feeding content to AI/LLMs - Text analysis or NLP - Content summarization - Human-readable archives **Use Screenshots when:** - Visual documentation needed - UI/UX testing - Compliance requirements - Visual AI processing **Use PDFs when:** - Document archiving needed - Print-quality preservation - Legal compliance requirements - Searchable text preservation ### Optimize Bandwidth **For metadata checks:** ```json {"delivery": "json"} ``` Omits content, returns only metadata. **For full responses:** ```json {"delivery": "json", "include_content": true} ``` Includes everything. **For direct content:** ```json {"delivery": "raw"} ``` No JSON wrapper, just content. ### Save Files Correctly **HTML:** ```bash curl "..." -o page.html ``` **Markdown:** ```bash curl "..." -o article.md ``` **Screenshot:** ```bash curl "..." -o screenshot.png ``` **PDF:** ```bash curl "..." -o document.pdf ``` PDFs and Screenshots are stored for 30 days. Download and store them locally if you need longer retention. --- ## Proxy Configuation Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/proxy/ | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `proxy_type` | string | `residential` | Proxy type: `residential` or `datacenter` | | `proxy_country` | string | `US` | Two-letter ISO country code (e.g., `GB`, `DE`, `JP`) | | `proxy_session_id` | string | - | Session ID (6-8 characters) for IP persistence | | `proxy_overwrite` | string | - | Full proxy URL to override default proxies | ## Proxy Types Comparison | Feature | Premium (Residential) | Datacenter | |---------|----------------------|------------| | Request Mode Cost | 2 credits | 1 credit | | Browser Mode Cost | 5 credits | ❌ Not supported | | Auto Mode Cost | 2-6 credits | ❌ Not supported | | Success Rate | Highest | High | | Speed | Fast | Fastest | | Best For | Browser/auto modes, regional content testing, complex sites | Request mode only, public APIs, high-volume scraping | **Session Management Example:** ```json { "proxy_session_id": "cart123", "proxy_country": "US" } ``` Use the same session ID across multiple requests to maintain the same IP address—perfect for login flows or shopping carts. See [Proxy Types](/scraping-products-instructions/scraper-api/proxy-types/) for detailed information. **Captcha Solver:** The automatic captcha solver is only active when using `mode="browser"` and `proxy_type="residential"`. It is not possible to use the automatic captcha solver with datacenter proxies or request mode. --- ## Scraping Modes Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/scraping-modes/ Parameter `mode` | string | Scraping mode options: `request`, `browser`, or `auto` **`request`** - Fast HTTP request without JavaScript - Best for static sites (blogs, news, documentation) - Works with both datacenter (1 credit) and residential proxies (2 credits) - Timeout: 30 seconds **`browser`** - Full browser with JavaScript rendering - Best for SPAs (React, Vue, Angular) - Supports PDFs, screenshots and JavaScript automation - **Requires premium (residential) proxies** - 5 credits flat rate - Timeout: 45 seconds **`auto`** (default) - Intelligent detection - Tries HTTP request first, upgrades to browser if needed - **Requires premium (residential) proxies** - Charges 2 credits if request succeeds, 6 credits if upgraded to browser **Mode Restrictions:** Browser and auto modes only work with premium (residential) proxies. Attempting to use datacenter proxies with `mode="browser"` or `mode="auto"` will return an error. --- ## Scraper API Parameter Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/parameters/usage-examples/ ## Fast Static Scraping with Datacenter (1 credit) ```json { "url": "https://example.com", "mode": "request", "proxy_type": "datacenter" } ``` ## Request Mode with Residential (2 credits) ```json { "url": "https://example.com", "mode": "request", "proxy_type": "residential", "proxy_country": "US" } ``` ## Request Mode with Filtering (2 credits) ```json { "url": "https://spa-website.com", "mode": "request", "excluded_tags": ["aside", "form"], "excluded_selectors": [".ads"], "proxy_type": "residential", "proxy_country": "US" } ``` ## Auto Mode with Filtering and Extraction (2-6 credits) ```json { "url": "https://dynamic-site.com", "mode": "auto", "content": "markdown", "excluded_tags": ["nav", "footer", "form"], "excluded_selectors": [".popup", ".cookie-banner"], "proxy_type": "residential", "proxy_country": "US", "extract_scheme": [ { "label": "page_metadata", "type": "nest", "selector": "head", "fields": [ { "label": "meta_title", "type": "content", "selector": "title" }, { "label": "canonical_url", "type": "attribute", "selector": "link[rel='canonical']", "attribute": "href" } ] }, { "label": "product_list", "type": "nest", "selector": ".product-card", "fields": [ { "label": "name", "type": "content", "selector": ".product-title" }, { "label": "price", "type": "content", "selector": ".price" }, { "label": "in_stock", "type": "exists", "selector": ".in-stock" } ] }, { "label": "sidebar_categories", "type": "nest", "xpath": "//aside//ul[@class='categories']", "fields": [ { "label": "category_names", "type": "content", "selector": "li" } ] } ] } ``` ## Browser Mode with Screenshot (6 credits) ```json { "url": "https://spa-website.com", "mode": "browser", "screenshot": true, "proxy_type": "residential", "wait_until": "networkidle" } ``` ## Browser Mode with PDF (6 credits) ```json { "url": "https://spa-website.com", "mode": "browser", "pdf": true, "proxy_type": "residential", "wait_until": "networkidle" } ``` ## AI-Powered Content Extraction (15 credits) ```json { "url": "https://news.site.com/article", "mode": "browser", "content": "markdown", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Summarize in 3 bullet points", "proxy_type": "residential" } ``` ## Auto Mode - Let API Decide (2-6 credits) ```json { "url": "https://protected-site.com", "mode": "auto", "proxy_type": "residential", "proxy_country": "US", "block_resources": ["image", "font"] } ``` --- ## Proxy Types Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/proxy-types/ The Scraper API integrates seamlessly with Evomi's global proxy network, giving you access to residential and datacenter proxies across 150+ countries. Choose the right proxy type for your use case to optimize success rates and costs. ## Proxy Types ### Premium/Residential Proxies (Default) Residential IP addresses from ISPs worldwide, selected for broad site compatibility and high request success rates. **Cost:** - Request mode: 2 credits - Browser mode: 5 credits (flat rate) - Auto mode: 2-6 credits | Feature | Description | |---------|-------------| | **IP Type** | Real residential IPs from ISPs | | **Success Rate** | Highest across complex and region-sensitive sites | | **Speed** | Fast | | **Rotating** | Yes (session support available) | | **Countries** | 150+ countries | | **Mode Support** | All modes (request, browser, auto) | **Best For:** - Browser and auto modes (required) - Regional content testing (streaming, e-commerce) - Sites with stricter access requirements - Social media platforms - Sneaker sites and ticket vendors - High-value scraping targets **Example:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "proxy_type": "residential", "proxy_country": "US" }' ``` ### Datacenter Proxies High-speed datacenter IPs optimized for performance and cost. **Cost:** 1 credit (request mode only) | Feature | Description | |---------|-------------| | **IP Type** | Datacenter IPs | | **Success Rate** | High for most sites | | **Speed** | Fastest | | **Rotating** | Yes (session support available) | | **Countries** | 50+ countries | | **Mode Support** | Request mode only | **Mode Restriction:** Datacenter proxies can only be used with `mode="request"`. Browser and auto modes require premium (residential) proxies. **Best For:** - Request mode static scraping - Public APIs and JSON endpoints - News sites and blogs - Documentation and knowledge bases - High-volume scraping - Development and testing - Cost-sensitive projects **Example:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.example.com/data", "proxy_type": "datacenter", "proxy_country": "DE" }' ``` ## Cost Comparison | Configuration | Credits | Notes | |--------------|---------|-------| | Request + Datacenter | 1 | Lowest cost option | | Request + Residential | 2 | 2× premium proxy cost | | Browser + Residential | 5 | Flat rate, residential required | | Browser + Datacenter | ❌ | Not supported | | Auto + Residential (request succeeds) | 2 | Same as request mode | | Auto + Residential (upgraded to browser) | 6 | 50% of request + browser | | Auto + Datacenter | ❌ | Not supported | | Screenshot | +1 | Additive cost | | AI Enhancement | +10 | Additive cost | ## Country Selection Target any country using two-letter ISO 3166-1 alpha-2 codes: ### Popular Countries | Code | Country | Code | Country | |------|---------|------|---------| | `US` | United States | `GB` | United Kingdom | | `DE` | Germany | `FR` | France | | `JP` | Japan | `AU` | Australia | | `CA` | Canada | `BR` | Brazil | | `IN` | India | `IT` | Italy | | `ES` | Spain | `NL` | Netherlands | | `SG` | Singapore | `KR` | South Korea | | `MX` | Mexico | `SE` | Sweden | **Example:** ```json { "url": "https://example.com", "proxy_type": "residential", "proxy_country": "JP" } ``` ## Session Management Maintain the same IP address across multiple requests using session IDs—perfect for multi-step workflows like login flows, shopping carts, or paginated scraping. ### Session ID Requirements - **Length:** 6-8 characters - **Format:** Alphanumeric (a-z, A-Z, 0-9) - **Case-sensitive:** `session1` ≠ `SESSION1` - **Persistence:** Same ID = same IP across requests ### Use Cases **Login and Browse:** ```bash # Step 1: Login page curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/login", "proxy_session_id": "user001", "proxy_country": "US" }' # Step 2: Dashboard (same IP) curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/dashboard", "proxy_session_id": "user001", "proxy_country": "US" }' ``` **Shopping Cart Flow:** ```json { "proxy_session_id": "cart789", "proxy_country": "US" } ``` Use the same session ID for: 1. Browse products 2. Add to cart 3. View cart 4. Checkout **Paginated Scraping:** ```json { "proxy_session_id": "scrape1", "proxy_country": "GB" } ``` Maintain the same IP when collecting multiple pages of results to preserve session consistency. ## Custom Proxy Override Bring your own proxy by providing a complete proxy URL: ```json { "url": "https://example.com", "proxy_overwrite": "http://username:password@proxy.example.com:8080" } ``` **Supported Protocols:** - `http://username:password@host:port` - `https://username:password@host:port` - `socks5://username:password@host:port` When using `proxy_overwrite`, the `proxy_country` parameter is ignored. However, pricing still uses the `proxy_type` multiplier for cost calculation. ## Proxy Behavior by Mode The proxy configuration applies differently based on the scraping mode: | Mode | Proxy Usage | |------|-------------| | `request` | Proxy used for HTTP request | | `browser` | Proxy used for browser connection | | `auto` | Proxy used for initial request, then browser if upgraded | ## Best Practices ### Choose the Right Proxy Type **Use Datacenter When:** - Using request mode only (datacenter is not supported with browser/auto modes) - Scraping public content - Cost optimization is priority - Target site has straightforward access requirements - High-volume scraping of simple sites **Use Residential When:** - Using browser or auto mode (residential is required) - Testing region-specific content - Target has stricter access requirements - Social media or e-commerce scraping - Success rate is more important than cost ### Optimize Session Usage **Do:** - Use sessions for related requests (login → browse → action) - Keep session IDs unique per workflow - Reuse sessions within a reasonable timeframe **Don't:** - Use the same session ID across unrelated workflows - Share session IDs between different users/accounts - Keep sessions active indefinitely ### Monitor Success Rates Track which proxy type works best for your targets: ```python # Python example results = { 'datacenter_success': 0, 'residential_success': 0 } # Try datacenter first response = scrape(url, proxy_type='datacenter') if response.status_code == 200: results['datacenter_success'] += 1 else: # Fall back to residential response = scrape(url, proxy_type='residential') results['residential_success'] += 1 ``` ### Country Selection Strategy **For Regional Content Testing:** ```json { "proxy_country": "GB" // Match the target country } ``` **For General Scraping:** ```json { "proxy_country": "US" // Largest IP pool, best performance } ``` **For Regional E-commerce:** ```json { "proxy_country": "DE" // Match customer location } ``` ## Proxy Errors ### Connection Refused ```json { "success": false, "error": "Proxy connection failed" } ``` **Solutions:** - Try a different proxy_country - Switch to residential proxies for better success rate - Check if the target site is blocking your proxy type ### Timeout ```json { "success": false, "error": "Request timeout" } ``` **Solutions:** - Increase `wait_seconds` for slow-loading sites - Try datacenter proxies for faster connections - Use `async=true` for long-running requests Some websites block all datacenter IPs. If you consistently get blocked with datacenter proxies, switch to residential for a higher success rate. --- ## Quickstart Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/quickstart/ Get started with the Evomi Scraper API in minutes. This guide walks you through making your first request and understanding the response. ## Your First Request The simplest way to scrape a webpage is with a GET request: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" ``` The API returns the page's HTML content directly. That's it—no configuration needed! You can even paste this URL directly into your browser to see the results. ## Understanding Auto Mode By default, the API uses `auto` mode, which intelligently decides between two approaches: **Fast HTTP Request** For static pages (news sites, blogs, documentation), a simple HTTP request is used. Fast, efficient, and costs just 2 credits with residential proxies. **Browser Rendering** For JavaScript-heavy sites (React, Vue, Angular apps), the API automatically upgrades to a full browser. You pay only when it's needed (6 credits when upgraded). **Note:** Auto mode requires premium (residential) proxies. Datacenter proxies can only be used with `mode="request"`. ```bash # This might use fast mode (2 credits) curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://news.ycombinator.com&api_key=YOUR_API_KEY" # This likely needs browser mode (6 credits) curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://spa-website.com&api_key=YOUR_API_KEY" ``` Check the `X-Mode-Used` header in the response to see which mode was used: ``` X-Mode-Used: auto (request) X-Mode-Used: auto (browser) ``` ## Using JSON Format For structured responses with metadata, use JSON delivery: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&api_key=YOUR_API_KEY" ``` **Response:** ```json { "success": true, "url": "https://example.com", "domain": "example.com", "title": "Example Domain", "status_code": 200, "credits_used": 1.0, "credits_remaining": 99.0, "mode_used": "auto (request)" } ``` By default, JSON mode omits the full content to save bandwidth. Add `include_content=true` to include it in the response. ## POST Requests For complex configurations, use POST with JSON: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "proxy_country": "US", "wait_until": "networkidle" }' ``` ## Common Use Cases ### 1. Extract Clean Text (Markdown) Perfect for AI processing or content analysis: ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://blog.example.com/post&content=markdown&api_key=YOUR_API_KEY" ``` ### 2. Capture a Screenshot Get a full-page PNG screenshot: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "screenshot": true }' \ -o screenshot.png ``` ### 3. Use Residential Proxies For geo-restricted content or better success rates: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "proxy_type": "residential", "proxy_country": "GB" }' ``` ### 4. Extraction & Logic Extract specific data points and run logic on them: ```bash curl -X POST "[https://scrape.evomi.com/api/v1/scraper/realtime](https://scrape.evomi.com/api/v1/scraper/realtime)" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": " "https://example.com"", "extract_scheme": [ { "label": "is_in_stock", "selector": "button#add-to-cart", "type": "exists" }, { "label": "price", "selector": ".product-price", "type": "content" } ] }' ``` ### 5. AI-Powered Extraction Get structured summaries automatically: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://news.example.com/article", "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract headline, author, date, and create a 2-sentence summary as JSON" }' ``` ## Monitoring Your Usage Every response includes credit information in the headers: ``` X-Credits-Used: 1.0 X-Credits-Remaining: 99.0 X-Mode-Used: auto (request) ``` You can also find this in JSON responses: ```json { "credits_used": 1.0, "credits_remaining": 99.0 } ``` ## Handling Errors The API uses standard HTTP status codes: | Status | Meaning | Action | |--------|---------|--------| | 200 | Success | Process the response | | 400 | Bad request | Check your parameters | | 401 | Unauthorized | Verify your API key | | 402 | Insufficient credits | Add credits to your account | | 429 | Rate limit exceeded | Wait and retry | Error responses include helpful messages: ```json { "success": false, "error": "Invalid request parameters", "message": "url is required and must be a valid URL" } ``` ## What's Next? Now that you've made your first request, explore more advanced features: - **[Parameters](/scraping-products-instructions/scraper-api/parameters/)** - Full reference of all configuration options - **[Proxy Types](/scraping-products-instructions/scraper-api/proxy-types/)** - Learn about proxy selection and session management - **[JavaScript Automation](/scraping-products-instructions/scraper-api/parameters/javascript-automation/)** - Interact with pages before scraping - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** - See complete code examples in multiple languages Start with small tests to understand costs before scaling. Use `delivery=json` without `include_content` to check metadata and costs without full content transfer. --- ## Rate Limits Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/rate-limits/ The Scraper API implements intelligent rate limiting to ensure fair resource allocation and optimal performance. Understanding these limits helps you build efficient, scalable applications. ## Concurrency Limits Each account has a maximum number of simultaneous requests that can be processed. This limit is enforced per API key. ### How It Works 1. When you start a request, your concurrency counter increments 2. If the counter exceeds your limit, the request is rejected with `429 Too Many Requests` 3. When a request completes (success or failure), the counter decrements 4. Counters automatically expire after 60 seconds to prevent stuck states ### Checking Your Limits Every response includes rate limit headers: ``` X-RateLimit-Limit: 5 X-RateLimit-Remaining: 3 ``` | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Maximum concurrent requests allowed | | `X-RateLimit-Remaining` | Current available capacity | ### Rate Limit Response When you exceed your limit: **Status:** `429 Too Many Requests` ```json { "success": false, "error": "Concurrency limit exceeded (5/5)", "limit": 5, "reset": 60, "timestamp": "2025-10-29T12:00:00Z" } ``` | Field | Description | |-------|-------------| | `limit` | Your maximum concurrent requests | | `reset` | Seconds until limit resets (default: 60) | ## Best Practices ### 1. Monitor Remaining Capacity Check the `X-RateLimit-Remaining` header before sending batches: ```python import requests def check_capacity(api_key): response = requests.get( f"https://scrape.evomi.com/api/v1/scraper/health?api_key={api_key}" ) remaining = int(response.headers.get("X-RateLimit-Remaining", 0)) return remaining # Use it if check_capacity(api_key) >= 3: # Send batch of 3 requests process_batch(urls[:3]) ``` ### 2. Implement Exponential Backoff When rate limited, wait before retrying: ```python import time def scrape_with_backoff(url, api_key, max_retries=3): for attempt in range(max_retries): response = requests.get( f"https://scrape.evomi.com/api/v1/scraper/realtime?url={url}&api_key={api_key}" ) if response.status_code == 200: return response elif response.status_code == 429: # Rate limited data = response.json() wait_time = data.get("reset", 60) if attempt < max_retries - 1: print(f"Rate limited. Waiting {wait_time}s...") time.sleep(wait_time) else: raise Exception("Max retries exceeded") else: return response ``` ### 3. Use Async Mode for Batches For large batches, use `async=true` to submit tasks without blocking: ```python import asyncio import aiohttp async def submit_async_task(session, url, api_key): async with session.post( "https://scrape.evomi.com/api/v1/scraper/realtime", headers={"x-api-key": api_key}, json={"url": url, "async": True} ) as response: data = await response.json() return data["task_id"] async def process_batch(urls, api_key): async with aiohttp.ClientSession() as session: tasks = [submit_async_task(session, url, api_key) for url in urls] task_ids = await asyncio.gather(*tasks) return task_ids # Submit 100 URLs without waiting for results task_ids = asyncio.run(process_batch(urls, api_key)) ``` ### 4. Queue Requests Locally Implement a request queue to respect your concurrency limit: ```python from queue import Queue from threading import Thread import time class ScraperQueue: def __init__(self, api_key, max_concurrent=5): self.api_key = api_key self.max_concurrent = max_concurrent self.queue = Queue() self.active = 0 def scrape(self, url): """Add URL to queue""" self.queue.put(url) def worker(self): """Worker thread that processes queue""" while True: url = self.queue.get() # Wait if at capacity while self.active >= self.max_concurrent: time.sleep(0.1) self.active += 1 try: response = requests.get( f"https://scrape.evomi.com/api/v1/scraper/realtime?url={url}&api_key={self.api_key}" ) self.process_response(response) finally: self.active -= 1 self.queue.task_done() def start(self, num_workers=5): """Start worker threads""" for _ in range(num_workers): t = Thread(target=self.worker, daemon=True) t.start() # Usage scraper = ScraperQueue(api_key, max_concurrent=5) scraper.start(num_workers=5) # Add URLs for url in urls: scraper.scrape(url) # Wait for completion scraper.queue.join() ``` ### 5. Pace Your Requests Even within your concurrency limit, avoid bursting: ```python import time def scrape_with_pacing(urls, api_key, delay=0.5): """Scrape URLs with minimum delay between requests""" results = [] for url in urls: response = scrape(url, api_key) results.append(response) time.sleep(delay) # 500ms between requests return results ``` ## Optimizing for Scale ### Strategy 1: Use Datacenter Proxies Datacenter proxies are faster, reducing request duration and freeing up concurrency slots quicker: ```json { "proxy_type": "datacenter" } ``` ### Strategy 2: Block Unnecessary Resources Speed up requests by blocking images, stylesheets, and fonts: ```json { "block_resources": ["image", "stylesheet", "font", "media"] } ``` ### Strategy 3: Use Request Mode When Possible If JavaScript isn't needed, use `request` mode for 6× faster execution: ```json { "mode": "request" } ``` ### Strategy 4: Batch with Async Submit large batches with `async=true`, then poll results: ```python # Submit 100 tasks task_ids = [] for url in urls[:100]: response = submit_async(url, api_key) task_ids.append(response['task_id']) # Poll all tasks results = [wait_for_task(tid, api_key) for tid in task_ids] ``` ## Error Recovery Always implement proper error handling: ```python def scrape_with_recovery(url, api_key): try: response = requests.get( "https://scrape.evomi.com/api/v1/scraper/realtime", params={"url": url}, headers={"x-api-key": api_key}, timeout=60 ) if response.status_code == 200: return response elif response.status_code == 429: # Rate limited - wait and retry time.sleep(60) return scrape_with_recovery(url, api_key) elif response.status_code == 408: # Timeout - check task status data = response.json() return wait_for_task(data['task_id'], api_key) else: # Other error print(f"Error: {response.status_code}") return None except requests.exceptions.Timeout: print("Request timed out") return None ``` Don't hammer the API with retries. Always implement exponential backoff and respect the `reset` time in rate limit responses. --- ## Result Polling Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/result-polling/ When scraping tasks take longer than the timeout limit or when you submit async requests, you'll need to poll for results. This guide covers how to retrieve results from background tasks. ## When Polling Is Needed Polling is required in these scenarios: 1. **Explicit async requests** — You set `async=true` in your request 2. **Request timeouts** — A synchronous request exceeds the timeout limit and auto-converts to async ## Timeout Handling Different modes have different timeout limits: | Mode | Timeout | What Happens After | |------|---------|-------------------| | `request` | 30 seconds | Auto-converts to async task | | `browser` | 45 seconds | Auto-converts to async task | | `auto` | 30-45 seconds | Depends on mode used | ### Timeout Response When a synchronous request times out, you receive a `202 Accepted` response: ```json { "success": true, "task_id": "task_abc123", "status": "processing", "message": "Task is taking longer than expected. Use task_id to check status.", "check_url": "/api/v1/scraper/tasks/task_abc123", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:40.101Z", "finished_at": null, "elapsed_seconds": 45.3 } ``` ## Submitting Async Requests For long-running or batch jobs, submit async requests from the start: ### Request ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "async": true }' ``` ### Response ```json { "task_id": "task_abc123", "status": "processing", "check_url": "/api/v1/scraper/tasks/task_abc123", "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` ## Checking Task Status ### Request ```bash curl "https://scrape.evomi.com/api/v1/scraper/tasks/task_abc123?api_key=YOUR_API_KEY" ``` ### Response (Processing — HTTP 202) ```json { "success": true, "task_id": "task_abc123", "status": "processing", "message": "Task is still processing", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:40.101Z", "finished_at": null, "elapsed_seconds": 32.1 } ``` While the task is still waiting in the queue (`"status": "pending"`), the same response shape is returned with `"started_at": null`. ### Response (Completed) ```json { "task_id": "task_abc123", "status": "completed", "success": true, "url": "https://example.com", "domain": "example.com", "title": "Example Domain", "content": "...", "status_code": 200, "credits_used": 1.0, "credits_remaining": 99.0, "mode_used": "auto (request)", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:40.101Z", "finished_at": "2026-09-28T20:31:57.800Z", "elapsed_seconds": 45.3 } ``` ### Response (Failed) ```json { "task_id": "task_abc123", "status": "failed", "success": false, "error": "Connection timeout after 30 seconds", "credits_used": 0.5, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:40.101Z", "finished_at": "2026-09-28T20:31:51.900Z", "elapsed_seconds": 39.4 } ``` Use [Cloud Storage](/scraping-products-instructions/scraper-api/cloud-storage/) otherwise task results are stored on our servers for a limited time: a task record is deleted once its result has been fetched (success or failure), and otherwise expires 10 minutes after the last status write — after that, polling returns `404`. If not using cloud storage retrieve your results promptly after completion. ## Task Timing Every response that belongs to a task — the task-status `GET` endpoints and the task-creating `POST` routes — carries four timing fields. They let you separate **queue wait** from **actual work**: - `started_at − created_at` = time the task spent pending in the queue - `finished_at − started_at` = time the worker spent processing | Field | Type | Set when | Notes | |-------|------|----------|-------| | `created_at` | string, ISO-8601 UTC `YYYY-MM-DDTHH:MM:SS.sssZ` (always milliseconds, `Z` suffix) | The task was accepted by the API (it entered `pending`) | Never changes afterwards | | `started_at` | same format, or `null` | A worker picked the task up (first move to `processing`) | `null` the whole time the task is queued | | `finished_at` | same format, or `null` | The task reached `success` or `failure` | `null` until it finishes | | `elapsed_seconds` | number with one decimal, or `null` | always | `finished_at − created_at` once finished; `now − created_at` while running — it grows between polls and freezes when the task finishes | `null` means "not yet / not known". Tasks created before these fields existed return `null` for all four rather than a made-up value. ### The same data as headers Every task response also carries the timing as headers — `X-Task-Created-At`, `X-Task-Started-At`, `X-Task-Finished-At` and `X-Task-Elapsed-Seconds` — with the same values and format. **Null values are omitted** (headers can't be null), so a queued task simply has no `X-Task-Started-At` header. When the response body isn't JSON — `delivery=raw` returns the page, screenshot or PDF itself as the body — the headers are the only place the timing appears. And in the rare case where a JSON body already uses one of the four field names for its own data (for example a saved config's own `created_at`), the timing fields are not injected into that body at all — read them from the `X-Task-*` headers instead. A response body never mixes its own timestamps with the task's. ### Where the fields appear - **All task-status GET endpoints** — scraper, crawl, map, search, config generate, schemes and agent (e.g. `GET /api/v1/scraper/tasks/{task_id}`) — on every response: `pending`, `processing`, the `202` "result is being finalized", success, failure and unknown-status. - **All task-creating POSTs** — scraper `/realtime`, crawl, map, search, config generate, scheme create/update and agent `/converse`: - async `202` (task accepted): `created_at` set, `started_at`/`finished_at` `null`, `elapsed_seconds` ≈ `0.0` - sync `202` after the wait limit ("still processing"): same shape; `started_at` is `null` while the task is still queued - sync success / failure within the wait limit: full timing including `finished_at` - **Not present:** responses where no task was created (validation errors, `402` insufficient credits, `429` concurrency limit), `404` task not found, and agent `/request` (it is stateless and never creates a task). ## Polling for Results ### Python Example ```python import time import requests def wait_for_task(task_id, api_key, max_wait=120, poll_interval=2): """Poll task status until completion""" url = f"https://scrape.evomi.com/api/v1/scraper/tasks/{task_id}" headers = {"x-api-key": api_key} start_time = time.time() while time.time() - start_time < max_wait: response = requests.get(url, headers=headers) data = response.json() status = data.get("status") if status == "completed": return data elif status == "failed": raise Exception(f"Task failed: {data.get('error')}") # Still processing time.sleep(poll_interval) raise TimeoutError(f"Task {task_id} did not complete in {max_wait} seconds") # Usage result = wait_for_task("task_abc123", api_key) print(result["content"]) ``` ### JavaScript Example ```javascript async function waitForTask(taskId, apiKey, maxWait = 120000, pollInterval = 2000) { const url = `https://scrape.evomi.com/api/v1/scraper/tasks/${taskId}`; const headers = { 'x-api-key': apiKey }; const startTime = Date.now(); while (Date.now() - startTime < maxWait) { const response = await fetch(url, { headers }); const data = await response.json(); if (data.status === 'completed') { return data; } else if (data.status === 'failed') { throw new Error(`Task failed: ${data.error}`); } await new Promise(resolve => setTimeout(resolve, pollInterval)); } throw new Error(`Task ${taskId} did not complete in time`); } // Usage const result = await waitForTask('task_abc123', apiKey); console.log(result.content); ``` ### Go Example ```go func WaitForTask(taskID, apiKey string, maxWait time.Duration) (map[string]interface{}, error) { url := fmt.Sprintf("https://scrape.evomi.com/api/v1/scraper/tasks/%s?api_key=%s", taskID, apiKey) startTime := time.Now() pollInterval := 2 * time.Second for time.Since(startTime) < maxWait { resp, err := http.Get(url) if err != nil { return nil, err } var data map[string]interface{} json.NewDecoder(resp.Body).Decode(&data) resp.Body.Close() status := data["status"].(string) if status == "completed" { return data, nil } else if status == "failed" { return nil, fmt.Errorf("task failed: %v", data["error"]) } time.Sleep(pollInterval) } return nil, fmt.Errorf("task did not complete in %v", maxWait) } ``` ## Raw vs JSON Responses When polling for results, the response format depends on the `delivery` parameter you set in your original request. ### Raw Delivery (Default) If you used `delivery=raw` (or didn't specify delivery), the polled result returns the raw content directly: ```bash curl "https://scrape.evomi.com/api/v1/scraper/tasks/task_abc123?api_key=YOUR_API_KEY" ``` **Response:** ``` Content-Type: text/html; charset=utf-8 X-Credits-Used: 1.0 X-Credits-Remaining: 99.0 X-Task-Created-At: 2026-09-28T20:31:12.482Z X-Task-Started-At: 2026-09-28T20:31:14.102Z X-Task-Finished-At: 2026-09-28T20:31:28.771Z X-Task-Elapsed-Seconds: 16.3 ... ``` **With raw delivery, you get only the content—no metadata like title, status code, or credits used in the response body.** Metadata is available only in response headers — including the task timing, which appears only as `X-Task-*` headers. ### JSON Delivery If you used `delivery=json`, the polled result returns a structured JSON response with metadata: ```bash curl "https://scrape.evomi.com/api/v1/scraper/tasks/task_abc123?api_key=YOUR_API_KEY" ``` **Response:** ```json { "task_id": "task_abc123", "status": "completed", "success": true, "url": "https://example.com", "title": "Example Domain", "status_code": 200, "credits_used": 1.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.102Z", "finished_at": "2026-09-28T20:31:28.771Z", "elapsed_seconds": 16.3 } ``` **With JSON delivery, you must set `include_content=true` in your original request to receive the scraped content.** Without this parameter, only metadata is returned—no HTML, Markdown, or other content. ## Using Delivery and Include_content When using JSON delivery mode, you must set `include_content=true` to receive the scraped content in the response. ### Without include_content ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&api_key=YOUR_API_KEY" ``` Response omits content: ```json { "success": true, "url": "https://example.com", "title": "Example Domain", "status_code": 200, "credits_used": 1.0, "hints": ["Content omitted by default. Set include_content=true to include it."] } ``` ### With include_content ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&include_content=true&api_key=YOUR_API_KEY" ``` Response includes content: ```json { "success": true, "url": "https://example.com", "title": "Example Domain", "content": "...", "status_code": 200, "credits_used": 1.0 } ``` **Important:** When using `delivery=json`, always set `include_content=true` if you need the scraped content (HTML, Markdown, etc.) in the response. Without this parameter, only metadata is returned to save bandwidth. ## Batch Processing with Async For large batches, submit all tasks first, then poll for results: ```python import asyncio import aiohttp async def submit_and_wait(urls, api_key): async with aiohttp.ClientSession() as session: # Submit all tasks task_ids = [] for url in urls: async with session.post( "https://scrape.evomi.com/api/v1/scraper/realtime", headers={"x-api-key": api_key}, json={"url": url, "async": True} ) as resp: data = await resp.json() task_ids.append(data["task_id"]) # Poll for all results results = [] for task_id in task_ids: result = await poll_task(session, task_id, api_key) results.append(result) return results async def poll_task(session, task_id, api_key, max_wait=120): url = f"https://scrape.evomi.com/api/v1/scraper/tasks/{task_id}" headers = {"x-api-key": api_key} start = asyncio.get_event_loop().time() while asyncio.get_event_loop().time() - start < max_wait: async with session.get(url, headers=headers) as resp: data = await resp.json() if data["status"] == "completed": return data elif data["status"] == "failed": raise Exception(f"Task failed: {data.get('error')}") await asyncio.sleep(2) raise TimeoutError(f"Task {task_id} timed out") # Usage urls = ["https://example1.com", "https://example2.com", "https://example3.com"] results = asyncio.run(submit_and_wait(urls, api_key)) ``` ## Webhook Alternative Instead of polling, you can use webhooks to receive notifications when tasks complete. See [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) for details. ```json { "url": "https://example.com", "async": true, "webhook": { "url": "https://your-server.com/webhook", "webhook_type": "custom", "events": ["completed", "failed"] } } ``` ## Best Practices 1. **Use appropriate poll intervals** — Poll every 2-3 seconds, not faster 2. **Set reasonable timeouts** — Most tasks complete within 60 seconds 3. **Handle failures gracefully** — Check the `error` field in failed responses 4. **Retrieve results promptly** — Task records are deleted once their result has been fetched, and otherwise expire 10 minutes after the last status write 5. **Use webhooks for batches** — Avoid polling hundreds of tasks simultaneously 6. **Stop on a terminal response** — Once `finished_at` is set, `elapsed_seconds` stops growing and re-polling returns the same frozen value (until the record is deleted or expires) --- ## Schedules Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/schedules/ **Schedules** allow you to automate scraping tasks by running saved [Scraper Configs](/scraping-products-instructions/scraper-api/scraper-config/) at specific times and regular intervals. Set up hourly price monitoring, daily reports, or any recurring scraping task. --- ## Endpoint Reference | Endpoint | Method | Action | | :--- | :--- | :--- | | `/account/schedule` | `POST` | Create a new schedule | | `/account/schedule` | `GET` | List all schedules | | `/account/schedule/{id}` | `GET` | Retrieve specific schedule | | `/account/schedule/{id}` | `PUT` | Update an existing schedule | | `/account/schedule/{id}` | `DELETE` | Remove a schedule | | `/account/schedule/{id}/toggle` | `POST` | Pause or activate a schedule | | `/account/schedule/{id}/runs` | `GET` | View execution history | **Authentication**: Include your API key in the `x-api-key` header for all requests. **Base URL**: `https://scrape.evomi.com/api/v1` --- ## Quick Examples ### Create a Schedule ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Hourly Price Monitor", "config_id": "cfg_abc123", "interval_minutes": 60 }' ``` **Response:** ```json { "success": true, "id": "job_xyz789", "name": "Hourly Price Monitor", "config_id": "cfg_abc123", "interval_minutes": 60, "is_active": true, "next_run": "2026-02-20T14:00:00", "message": "Schedule created successfully" } ``` ### List Your Schedules ```bash curl "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" ``` **Response:** ```json { "success": true, "schedules": [ { "id": "job_xyz789", "name": "Hourly Price Monitor", "config_id": "cfg_abc123", "interval_minutes": 60, "is_active": true, "last_run": "2026-02-20T13:00:00", "last_status": "success", "next_run": "2026-02-20T14:00:00" } ], "pagination": { "page": 1, "per_page": 20, "total": 1 } } ``` ### Get a Specific Schedule ```bash curl "https://scrape.evomi.com/api/v1/account/schedule/job_xyz789" \ -H "x-api-key: YOUR_API_KEY" ``` ### Update a Schedule ```bash curl -X PUT "https://scrape.evomi.com/api/v1/account/schedule/job_xyz789" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Monitor", "interval_minutes": 30 }' ``` ### Pause or Activate a Schedule ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule/job_xyz789/toggle" \ -H "x-api-key: YOUR_API_KEY" ``` ### Delete a Schedule ```bash curl -X DELETE "https://scrape.evomi.com/api/v1/account/schedule/job_xyz789" \ -H "x-api-key: YOUR_API_KEY" ``` ### View Execution History ```bash curl "https://scrape.evomi.com/api/v1/account/schedule/job_xyz789/runs" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## How Schedules Work 1. **Create a Scraper Config** — First, save a [Scraper Config](/scraping-products-instructions/scraper-api/scraper-config/) with your scraping settings 2. **Create a Schedule** — Reference the config ID and set an interval 3. **Automatic Execution** — The scheduler runs your config at the specified interval 4. **Track Results** — View execution history and status for each run Make sure you have sufficient credits for the scrape configuration. --- ## Parameter Reference ### Create Parameters | Parameter | Type | Required | Default | Description | | :--- | :--- | :--- | :--- | :--- | | `name` | string | Yes | — | Unique name for the schedule | | `config_id` | string | Yes | — | ID of the scraper config to run | | `interval_minutes` | integer | Yes | — | Run every N minutes (min: 5, max: 525600) | | `start_time` | string | No | now | UTC time for first run (e.g., `"14:00"`) | | `stop_on_error` | boolean | No | `true` | Pause schedule on failure | | `webhook` | object | No | - | [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) configuration for real-time notifications | ### Update Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `name` | string | No | New name for the schedule | | `config_id` | string | No | Change to a different scraper config | | `interval_minutes` | integer | No | New interval in minutes | | `stop_on_error` | boolean | No | Change error handling behavior | | `webhook` | object | No | Webhook configuration (set to `null` to remove) | ### List Query Parameters | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `page` | integer | 1 | Page number for pagination | | `per_page` | integer | 20 | Results per page (max 100) | | `active_only` | boolean | false | Filter to show only active schedules | --- ## Advanced Options ### Start Time Control when your schedule first runs. Specify a UTC time in `HH:MM` format: ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily Morning Report", "config_id": "cfg_abc123", "interval_minutes": 1440, "start_time": "08:00" }' ``` If the time has already passed today, the first run will be scheduled for tomorrow. ### Stop on Error By default, schedules pause automatically when a run fails. Set `stop_on_error: false` to continue running even after block and connection errors: ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Resilient Monitor", "config_id": "cfg_abc123", "interval_minutes": 60, "stop_on_error": false }' ``` ### Webhook Notifications Receive real-time notifications when your scheduled jobs run. Configure a webhook to get notified on events like `triggered`, `completed`, `failed`, and `paused`. ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Price Monitor with Notifications", "config_id": "cfg_abc123", "interval_minutes": 60, "webhook": { "url": "https://discord.com/api/webhooks/123456789/abcdefg", "type": "discord", "events": ["completed", "failed"] } }' ``` Review [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) for more details on webhook configuration. --- ## Common Intervals | Interval | Description | | :--- | :--- | | `5` | Every 5 minutes | | `15` | Every 15 minutes | | `30` | Every 30 minutes | | `60` | Every hour | | `120` | Every 2 hours | | `360` | Every 6 hours | | `720` | Every 12 hours | | `1440` | Every day | | `10080` | Every week | --- ## Execution Status Each scheduled run can have one of the following statuses: | Status | Description | | :--- | :--- | | `triggered` | Job has been queued for execution | | `success` | Job completed successfully | | `failed` | Job failed to complete | --- ## Response Codes | Status | Meaning | Action | | :--- | :--- | :--- | | 200 | Success | Request processed successfully | | 201 | Created | Schedule created successfully | | 400 | Bad request | Check your parameters | | 401 | Unauthorized | Verify your API key | | 404 | Not found | Schedule ID does not exist | | 409 | Conflict | Schedule name already exists | | 422 | Validation error | Check JSON syntax and required fields | --- ## What's Next? - **[Scraper Configs](/scraping-products-instructions/scraper-api/scraper-config/)** — Create reusable scraper configurations - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** — See complete code examples - **[Errors](/scraping-products-instructions/scraper-api/errors/)** — Handle API errors gracefully --- ## Scraper Configs Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/scraper-config/ **Scraper Configs** are reusable scraper blueprints. Instead of defining your full scraper configuration with every request, you can save a configuration once and reference it by ID. --- ## Endpoint Reference | Endpoint | Method | Action | | :--- | :--- | :--- | | `/account/configs` | `POST` | Create a new config | | `/account/configs` | `GET` | List all saved configs | | `/account/configs/{id}` | `GET` | Retrieve specific config | | `/account/configs/{id}` | `PUT` | Update an existing config | | `/account/configs/{id}` | `DELETE` | Remove a config | | `/account/configs/generate` | `POST` | AI-generate a config | | `/account/configs/generate/tasks/{id}` | `GET` | Check AI generation status | **Authentication**: Include your API key in the `x-api-key` header for all requests. **Base URL**: `https://scrape.evomi.com/api/v1` --- ## Quick Examples ### Create a Config ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/configs" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Product Scraper", "config": { "url": "https://example.com/product/123", "mode": "browser", "extract_scheme": [ { "label": "title", "selector": "h1", "type": "content" }, { "label": "price", "selector": ".price", "type": "content" } ] } }' ``` **Response:** ```json { "success": true, "id": "cfg_abc123", "name": "Product Scraper", "config": { "url": "https://example.com/product/123", "mode": "browser", "extract_scheme": [...] }, "created_at": "2026-02-19T12:00:00", "message": "Scrape config created successfully" } ``` ### List Your Configs ```bash curl "https://scrape.evomi.com/api/v1/account/configs" \ -H "x-api-key: YOUR_API_KEY" ``` **Response:** ```json { "success": true, "configs": [ { "id": "cfg_abc123", "name": "Product Scraper", "created_at": "2026-02-19T12:00:00", "updated_at": "2026-02-19T12:00:00" } ], "total": 1, "page": 1, "per_page": 20 } ``` ### Get a Specific Config ```bash curl "https://scrape.evomi.com/api/v1/account/configs/cfg_abc123" \ -H "x-api-key: YOUR_API_KEY" ``` ### Update a Config ```bash curl -X PUT "https://scrape.evomi.com/api/v1/account/configs/cfg_abc123" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Product Scraper", "config": { "url": "https://example.com/product/123", "mode": "request" } }' ``` ### Delete a Config ```bash curl -X DELETE "https://scrape.evomi.com/api/v1/account/configs/cfg_abc123" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Using a Saved Config Once you have a saved config, you can use it in scrape requests by passing the `config_id` instead of the full configuration: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "config_id": "cfg_abc123" }' ``` You can also override specific fields when using a config: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "config_id": "cfg_abc123", "url": "https://example.com/product/456", "proxy_country": "GB" }' ``` --- ## Parameter Reference ### Create/Update Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `name` | string | Yes (POST) | Unique name for the scrape config | | `config` | object | Yes (POST) | Scraper configuration object | ### List Query Parameters | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `page` | integer | 1 | Page number for pagination | | `per_page` | integer | 20 | Results per page (max 100) | | `sort_by` | string | `created_at` | Sort field: `created_at`, `updated_at`, `name` | | `sort_order` | string | `desc` | Sort direction: `asc` or `desc` | ### Config Object Options The `config` object supports all standard scraper parameters: | Option | Type | Description | | :--- | :--- | :--- | | `url` | string | Target URL to scrape | | `mode` | string | Scrape mode: `request`, `browser`, or `auto` | | `proxy_type` | string | Proxy type: `datacenter` or `residential` | | `proxy_country` | string | Country code for proxy (e.g., `US`, `GB`) | | `wait_until` | string | Page load condition: `load`, `domcontentloaded`, `networkidle` | | `wait_seconds` | integer | Additional wait time after page load (0-60) | | `extract_scheme` | array | Extraction rules for structured data | | `excluded_tags` | array | HTML tags to remove before processing | | `excluded_selectors` | array | CSS selectors to remove | | `block_resources` | array | Resource types to block for faster loading | | `js_instructions` | array | Automated browser interactions | | `additional_headers` | object | Custom HTTP headers | | `screenshot` | boolean | Capture screenshot (browser mode) | | `pdf` | boolean | Capture PDF (browser mode) | See the [Parameters](/scraping-products-instructions/scraper-api/parameters/) section for complete details on all available options. --- ## Response Codes | Status | Meaning | Action | | :--- | :--- | :--- | | 200 | Success | Request processed successfully | | 201 | Created | Config created successfully | | 400 | Bad request | Check your parameters | | 401 | Unauthorized | Verify your API key | | 404 | Not found | Config ID does not exist | | 422 | Validation error | Check JSON syntax and required fields | --- ## What's Next? - **[AI Creation](/scraping-products-instructions/scraper-api/scraper-config/ai-creation/)** — Generate configs automatically with AI - **[Extraction Schema](/scraping-products-instructions/scraper-api/parameters/extraction/schema/)** — Learn about extraction schemes - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** — See complete code examples --- ## AI Creation Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/scraper-config/ai-creation/ Generate complete scraper configurations automatically using natural language prompts. Our AI agent analyzes your request, discovers URLs, inspects pages, and builds optimized extraction schemas—all without manual configuration. --- ## Our config generator will: 1. Parse your natural language request 2. Discover relevant URLs if needed 3. Scrape pages to understand the structure 4. Generate optimized extraction schemas 5. Test and validate the configuration 6. Return a ready-to-use config --- ## Endpoint Reference ### Generate Config Config generation is **always asynchronous**. Submit your request and poll for results. ```bash POST https://scrape.evomi.com/api/v1/account/configs/generate ``` **Request:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/configs/generate" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Blog Scraper", "prompt": "Scrape the title, author, date and content from blog posts on evomi.com" }' ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Config generation submitted for background processing", "check_url": "/api/v1/account/configs/generate/tasks/550e8400-e29b-41d4-a716-446655440000", "credits_reserved": 150, "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` ### Check Task Status ```bash GET https://scrape.evomi.com/api/v1/account/configs/generate/tasks/{task_id} ``` ```bash curl "https://scrape.evomi.com/api/v1/account/configs/generate/tasks/550e8400-e29b-41d4-a716-446655440000" \ -H "x-api-key: YOUR_API_KEY" ``` **Response (Still Processing — HTTP 202):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Config generation is still processing", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.000Z", "finished_at": null, "elapsed_seconds": 47.2 } ``` **Response (Completed — HTTP 200):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "result": { "success": true, "id": "cfg_xyz789", "name": "Product Scraper", "config": { ... }, "created_at": "2026-09-28T20:32:22", "message": "Scrape config generated and saved successfully", "ai_steps_used": 4, "credits_used": 120 }, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.000Z", "finished_at": "2026-09-28T20:32:22.300Z", "elapsed_seconds": 69.8 } ``` Note the two `created_at` values: the one inside `result` is when the **saved config record** was created; the top-level timing fields describe the **generation task** itself — when it was queued, picked up and finished (see [Task Timing](/scraping-products-instructions/scraper-api/result-polling/#task-timing)). The timing is also mirrored as `X-Task-*` headers on every response. --- ## Credit Costs | Action | Cost | | :--- | :--- | | AI step (LLM call) | **30 credits** each | | Max steps per request | 8 steps (240 credits max) | | Internal API calls for discovery & scraping | Charged at normal rates (2-10 credits estimate)| Credits are reserved upfront and unused credits are automatically refunded. If the AI fails to generate a valid config, you're only charged for steps actually used. Average requests are 1-3 steps. --- ## Example Prompts ### Blog & News Content ```json { "name": "Evomi Blog Posts", "prompt": "Scrape blog posts from evomi.com - find the blog section and extract title, date and content" } ``` ```json { "name": "BBC News Headlines", "prompt": "Scrape headlines and summaries from https://www.bbc.com/news" } ``` ### E-Commerce Products ```json { "name": "Product Price Tracker", "prompt": "Scrape product name, price, availability and image URL from amazon.com product pages" } ``` ```json { "name": "Shopify Product Scraper", "prompt": "I need a config for Shopify e-commerce product pages with price and title extraction, heres my link ..." } ``` ### Anti-Block & Protected Sites ```json { "name": "Amazon Product Scraper", "prompt": "Scrape product title, price, and reviews from Amazon product pages, heres the link ..., I dont want to be blocked" } ``` ```json { "name": " Job Listings", "prompt": "Scrape job title, company, location, and description from ...com job posting pages, heres my cookies ..., I dont want to be blocked " } ``` ```json { "name": "Glassdoor Reviews", "prompt": "Scrape company reviews from https://www.glassdoor.com/Reviews/Google-Reviews-E9079.htm, I dont want to be blocked" } ``` ### Performance & Filtering ```json { "name": "Clean Content Config", "prompt": "Scrape https://example.com but exclude all ads, navigation, footers and sidebars" } ``` ```json { "name": "Fast Scraping Config", "prompt": "Create a fast scraping config" } ``` ```json { "name": "Article Content Only", "prompt": "Scrape only the main article content from news sites, stripping out all navigation, ads, and related articles sections" } ``` --- ## Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `name` | string | Yes | Unique name for the generated config | | `prompt` | string | Yes | Natural language description of what to scrape | ### Prompt Tips **Got a bad result? Being specific can help!** - **Target URL or domain or search pattern** — Helps the AI find the right pages - **Data fields names** — List exactly what you want extracted - **Site characteristics** — Mention if it's an SPA, requires browser rendering, or presents interactive challenges. **Examples:** | ❌ Vague | ✅ Specific | | :--- | :--- | | "scrape products" | "Scrape product name, price, and availability from amazon.com product pages" | | "get news" | "Scrape headline, author, date, and summary from BBC News articles" | | "make a config" | "Create a browser config for scraping JavaScript-rendered content " | | "get all the info from ... " | "go to ...com and interact with the sliding cards using javascript and ..." | If the AI cannot fulfill your request (e.g., site requires login with no cookies provided, paywall, or no matching content found), it will return a `deny` action with an explanation. You'll only be charged for steps used. --- ## Error Responses ### AI Denied Request Returned by the task status endpoint as HTTP 500, with the worker's result nested under `result`: ```json { "success": false, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "failed", "error": "The requested content requires user login credentials. Please provide authentication cookies if allowed.", "result": { "success": false, "action": "deny", "reason": "The requested content requires user login credentials. Please provide authentication cookies if allowed.", "ai_steps_used": 2, "credits_used": 60 }, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.000Z", "finished_at": "2026-09-28T20:31:33.900Z", "elapsed_seconds": 21.4 } ``` ### Generation Failed ```json { "success": false, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "failed", "error": "Could not find product urls on amazon", "result": { "success": false, "action": "error", "reason": "Could not find product urls on amazon", "ai_steps_used": 2, "credits_used": 150 }, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.000Z", "finished_at": "2026-09-28T20:32:15.600Z", "elapsed_seconds": 63.1 } ``` ### Insufficient Credits ```json { "success": false, "error": "Insufficient credits", "error_code": "INSUFFICIENT_CREDITS", "message": "Your account has insufficient credits. Required: 180, Available: 50" } ``` --- ## Full Workflow Example ### Step 1: Submit Generation Request ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/configs/generate" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "News Scraper", "prompt": "Scrape headlines, summaries and links from the BBC News homepage" }' ``` ### Step 2: Poll for Status ```bash curl "https://scrape.evomi.com/api/v1/account/configs/generate/tasks/abc123-uuid-here" \ -H "x-api-key: YOUR_API_KEY" ``` Repeat until `status` is `success` or `failure`. ### Step 3: Use the Generated Config ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "config_id": "cfg_abc123" }' ``` --- ## Best Practices 1. **Be specific** — Detailed prompts yield better configs 2. **Poll for results** — Generation is async; poll the task endpoint until complete 3. **Test before scaling** — Run a test scrape to verify the config works 4. **Iterate** — If results aren't perfect, refine your prompt and regenerate --- ## Tools Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/ The Scraper API includes powerful tools for URL discovery and domain crawling. Use these tools to find and collect URLs at scale. ## Available Tools | Tool | Description | Endpoint | |------|-------------|----------| | **[Url Discovery](/scraping-products-instructions/scraper-api/tools/url-discovery/)** | Discover URLs from sitemaps, Common Crawl, or active crawl. Returns URLs only. | `/api/v1/scraper/map` | | **[Domain Crawling](/scraping-products-instructions/scraper-api/tools/domain-crawling/)** | Crawl a website and automatically scrape every discovered URL. | `/api/v1/scraper/crawl` | | **[Search](/scraping-products-instructions/scraper-api/tools/search/)** | Find relevant domains by searching for topics, products, or keywords. | `/api/v1/scraper/search` | ## Quick Comparison | Feature | Url Discovery | Domain Crawling | Search | |---------|--------------|-----------------|--------| | Returns | URLs only | URLs + scraped content | Domains + titles | | Scrapes | No | Yes (every URL) | No | | Sources | sitemap, commoncrawl, crawl | crawl only | web search | | Default depth | 1 | 1 | N/A | | URL validation | Optional | Not applicable | N/A | ## Which Tool Should You Use? **Use Url Discovery when:** - You only need URLs (not content) - You want flexibility in discovery sources - You need to validate which URLs are live - You're building a URL list for another process **Use Domain Crawling when:** - You need both URLs and scraped content - You want to scrape every discovered page - You want a single request to do discovery + scraping - You're doing complete site scraping **Use Search when:** - You don't know the exact domain to scrape - You want to find relevant websites by topic or keyword - You need to discover competitors or similar sites - You're doing market research or lead generation ## Getting Started Choose a tool above to see full documentation, parameters, and examples. Both tools support async mode for large jobs. Use `async: true` to process in the background and poll for results. ## Credit Requirements **Url Discovery:** - 2 credits per source (sitemap, commoncrawl) - 0.5 credits per URL validation - Crawl source uses scraper rates **Domain Crawling:** - Actual costs depend on number of URLs scraped - Uses [Scraper API pricing](/scraping-products-instructions/scraper-api/#pricing-at-a-glance) **Search:** - 5 credits per query used - Refunded for unused queries if max URLs reached ## Related Documentation - **[Scraper API Main Docs](/scraping-products-instructions/scraper-api/)** - Core scraping functionality - **[Parameters](/scraping-products-instructions/scraper-api/parameters/)** - All scraper configuration options - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** - Code samples --- ## Domain Crawling Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/domain-crawling/ Our Domain Crawling API crawls a website and automatically scrapes every discovered URL. Perfect for crawling a whole domain in a single request. ## What is Domain Crawling? Domain Crawling actively browses a website by following links from the homepage and scrapes each page it finds. **Live scraper** — discovered URLs are scraped according to your patterns and the content is returned. No separate scraping step needed. ## How It Works 1. Starts from the domain homepage 2. Follows links up to specified depth 3. Scrapes every discovered URL 4. Returns both the URLs and scraped content ## Parameters Overview | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `domain` | string | Yes | Domain to crawl (e.g., "example.com") | | `max_urls` | integer | Yes | Maximum URLs to discover and scrape (1-10,000) | | `depth` | integer | No | How deep to follow links. Default: 1 | | `url_pattern` | string | No | Regex pattern to filter which URLs to scrape | | `scraper_config` | object | No | Custom scraper settings. Default: raw HTML | | `async` | boolean | No | Process in background. Default: false | | `webhook` | object | No | [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) configuration for real-time notifications | ## Scraper Config When you provide a `scraper_config`, it overrides the default settings: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 10, "scraper_config": { "mode": "request", } }' ``` **Default behavior:** If no `scraper_config` is provided, each URL is scraped with raw HTML output (the default scraper mode). ## Pricing Domain Crawling uses your scraper credits. Each scraped page costs credits according to [Scraper API pricing](/scraping-products-instructions/scraper-api/#pricing-at-a-glance). ## Quick Start Crawl a domain and scrape all discovered URLs: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 10 }' ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 10, "scraper_tasks_submitted": 10, "results": [ { "url": "https://example.com/", "source": "crawl", "scrape_task_id": "abc-123", "scrape_check_url": "/api/v1/scraper/tasks/abc-123" }, { "url": "https://example.com/about", "source": "crawl", "scrape_task_id": "def-456", "scrape_check_url": "/api/v1/scraper/tasks/def-456" } ], "credits_used": 20.0, "credits_remaining": 980.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.102Z", "finished_at": "2026-09-28T20:32:07.300Z", "elapsed_seconds": 54.8 } ``` Each result includes a `scrape_task_id` to check individual scraping status. ## Filter with URL Patterns Only scrape URLs matching a pattern: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "url_pattern": "/blog/.*", "max_urls": 20 }' ``` ## Control Crawl Depth Default depth is 1 (pages linked from homepage). Increase for deeper discovery: **Depth 1** — Homepage and direct links: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "depth": 1, "max_urls": 50 }' ``` **Depth 2** — Homepage, direct links, and their links: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "depth": 2, "max_urls": 100 }' ``` ## Custom Scraper Settings Override default scraping behavior: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 10, "scraper_config": { "mode": "request", } }' ``` ## Async Mode For large crawls, use async mode: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 100, "async": true }' ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Crawl task submitted for background processing", "check_url": "/api/v1/scraper/crawl/tasks/550e8400-e29b-41d4-a716-446655440000", "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check status:** ```bash curl "https://scrape.evomi.com/api/v1/scraper/crawl/tasks/550e8400-e29b-41d4-a716-446655440000" \ -H "x-api-key: YOUR_API_KEY" ``` While the task runs, polling returns `202` with the [task timing fields](/scraping-products-instructions/scraper-api/result-polling/#task-timing) (also mirrored as `X-Task-*` headers); a finished poll returns the full result with the final timing. ## Common Use Cases **Complete Site Scraping** Crawl an entire website and get all content in one request. **Product Discovery** Find all product pages and scrape them for pricing, inventory, or details. **Content Aggregation** Build a content library by crawling and scraping multiple pages. **Competitive Intelligence** Automatically gather competitor content across many pages. ## Base URL ``` https://scrape.evomi.com ``` All API requests use this base URL with the `/api/v1/scraper/crawl` endpoint. ## Error Handling | Status | Meaning | |--------|---------| | 200 | Success | | 202 | Async task processing | | 400 | Bad request | | 401 | Unauthorized | | 402 | Insufficient credits | | 404 | Task not found | | 429 | Rate limit exceeded | ## Next Steps - **[Usage Examples](/scraping-products-instructions/scraper-api/tools/domain-crawling/usage-examples/)** - More detailed examples - Just need URLs? Use **[Url Discovery](/scraping-products-instructions/scraper-api/tools/url-discovery/)** instead Each discovered URL is scraped, so costs can add up quickly. Start with small `max_urls` values to understand your costs before scaling. --- ## Domain Crawling Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/domain-crawling/usage-examples/ Practical examples showing request payloads and response formats for common Domain Crawling scenarios. Use the endpoint: ``` POST https://scrape.evomi.com/api/v1/scraper/crawl ``` Include your API key in the header: `x-api-key: YOUR_API_KEY` Domain Crawling scrapes discovered URLs. Each scraped page uses credits according to Scraper API pricing. ## Basic Crawling ### Example 1: Crawl Homepage and Links Discover and scrape pages from a domain starting at the homepage. **Request:** ```json { "domain": "example.com", "max_urls": 10 } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 10, "scraper_tasks_submitted": 10, "results": [ { "url": "https://example.com/", "source": "crawl", "scrape_task_id": "abc-123", "scrape_check_url": "/api/v1/scraper/tasks/abc-123" }, { "url": "https://example.com/about", "source": "crawl", "scrape_task_id": "def-456", "scrape_check_url": "/api/v1/scraper/tasks/def-456" } ], "credits_used": 20.0, "credits_remaining": 980.0 } ``` ### Example 2: Deeper Crawl Crawl deeper to find more pages. **Request:** ```json { "domain": "example.com", "depth": 2, "max_urls": 50 } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 50, "scraper_tasks_submitted": 50, "results": [ { "url": "https://example.com/", "source": "crawl" }, { "url": "https://example.com/page1", "source": "crawl" } ], "credits_used": 100.0, "credits_remaining": 900.0 } ``` ## URL Pattern Filtering ### Example 3: Scrape Only Blog Posts Only scrape URLs matching a pattern. **Request:** ```json { "domain": "example.com", "url_pattern": "/blog/.*", "max_urls": 20 } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 18, "scraper_tasks_submitted": 18, "results": [ { "url": "https://example.com/blog/post-1", "source": "crawl", "scrape_task_id": "blog-001", "scrape_check_url": "/api/v1/scraper/tasks/blog-001" } ], "credits_used": 36.0, "credits_remaining": 964.0 } ``` ### Example 4: Product Pages Only Target specific URL patterns like product pages. **Request:** ```json { "domain": "shop.example.com", "url_pattern": "/products?/[a-z0-9-]+", "max_urls": 100 } ``` **Response:** ```json { "success": true, "domain": "shop.example.com", "discovered_count": 87, "scraper_tasks_submitted": 87, "results": [ { "url": "https://shop.example.com/products/laptop", "source": "crawl", "scrape_task_id": "prod-001", "scrape_check_url": "/api/v1/scraper/tasks/prod-001" } ], "credits_used": 174.0, "credits_remaining": 826.0 } ``` ## Custom Scraper Settings ### Example 5: JavaScript Rendering Enable JavaScript rendering for dynamic sites. **Request:** ```json { "domain": "example.com", "max_urls": 10, "scraper_config": { "mode": "request", "js_rendering": true, "wait_for": 2000 } } ``` ### Example 6: Get Markdown Output Request content in Markdown format. **Request:** ```json { "domain": "blog.example.com", "max_urls": 20, "scraper_config": { "content": "markdown" } } ``` **Response:** ```json { "success": true, "domain": "blog.example.com", "discovered_count": 20, "scraper_tasks_submitted": 20, "results": [ { "url": "https://blog.example.com/post-1", "source": "crawl", "scrape_task_id": "md-001", "scrape_check_url": "/api/v1/scraper/tasks/md-001" } ], "credits_used": 60.0, "credits_remaining": 940.0 } ``` ### Example 7: AI Enhancement Use AI to extract structured data automatically. **Request:** ```json { "domain": "news.example.com", "max_urls": 10, "scraper_config": { "ai_enhance": true, "ai_source": "markdown", "ai_prompt": "Extract: headline, author, publish_date, summary. Return as JSON." } } ``` ## Async Mode ### Example 8: Large Crawl Use async mode for large crawls. **Request:** ```json { "domain": "largecorp.com", "max_urls": 500, "async": true } ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Crawl task submitted for background processing", "check_url": "/api/v1/scraper/crawl/tasks/550e8400-e29b-41d4-a716-446655440000", "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check Status:** ``` GET https://scrape.evomi.com/api/v1/scraper/crawl/tasks/550e8400-e29b-41d4-a716-446655440000 ``` **Status Response (Completed):** ```json { "success": true, "domain": "largecorp.com", "discovered_count": 473, "scraper_tasks_submitted": 473, "results": [ { "url": "https://largecorp.com/", "source": "crawl", "scrape_task_id": "task-001", "scrape_check_url": "/api/v1/scraper/tasks/task-001" } ], "credits_used": 946.0, "credits_remaining": 54.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:14.102Z", "finished_at": "2026-09-28T20:32:07.300Z", "elapsed_seconds": 54.8 } ``` ## Real-World Scenarios ### Example 9: Complete Site Scrape Crawl and scrape an entire website. **Request:** ```json { "domain": "mycompany.com", "depth": 2, "max_urls": 200, "async": true } ``` ### Example 10: Competitor Product Research Discover and scrape competitor product pages. **Request:** ```json { "domain": "competitor.com", "url_pattern": "/(products?|shop|store)/", "max_urls": 500, "async": true, "scraper_config": { "extract_scheme": [ { "label": "product", "selector": ".product-info", "type": "nest", "fields": [ { "label": "name", "selector": "h1", "type": "content" }, { "label": "price", "selector": ".price", "type": "content" } ] } ] } } ``` ### Example 11: Blog Content Aggregation Aggregate content from multiple blog posts. **Request:** ```json { "domain": "blog.example.com", "url_pattern": "/posts/[a-z0-9-]+", "max_urls": 100, "scraper_config": { "content": "markdown" } } ``` ## Error Responses ### Insufficient Credits **Response (402 Payment Required):** ```json { "success": false, "error": "Insufficient credits", "message": "Need at least 2 credits to start crawl" } ``` ### Invalid Domain **Response (400 Bad Request):** ```json { "success": false, "error": "Invalid request parameters", "message": "domain is required and must be a valid domain name" } ``` ## Best Practices ### 1. Start Small Test with small max_urls first: ```json { "domain": "example.com", "max_urls": 10 } ``` ### 2. Use Patterns Filter with url_pattern to target specific content: ```json { "domain": "blog.example.com", "url_pattern": "/blog/[0-9]{4}/", "max_urls": 50 } ``` ### 3. Async for Large Jobs Use async mode for 100+ URLs: ```json { "domain": "largecorp.com", "max_urls": 500, "async": true } ``` ### 4. Set Appropriate Depth Start with depth 1, increase if needed: ```json { "domain": "example.com", "depth": 2, "max_urls": 100 } ``` ### 5. Customize Scraper Config Get exactly the output you need: ```json { "domain": "example.com", "max_urls": 20, "scraper_config": { "content": "markdown", } } ``` Each discovered URL is scraped. Costs can add up quickly. Start with small max_urls values to understand your costs before scaling. --- ## Search Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/search/ Our Search API finds relevant domains based on search queries. Perfect for discovering content without knowing the exact domain. ## What is Search? Search allows you to find domains by searching for topics, products, or keywords. It returns relevant domains with titles that you can then scrape or map. **Discover domains** — Find relevant websites without prior knowledge of their URLs. ## How It Works 1. Submit one or more search queries 2. The API searches the web privately 3. Returns matching domains and titles ## Parameters Overview | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `query` | string/array | Yes | Search query string(s) - single string or list of up to 10 queries | | `max_urls` | integer | No | Maximum URLs/domains to return (1-100). Default: 20 | | `region` | string | No | Region for search results. Also determines proxy location. Default: 'us-en' | | `webhook` | object | No | [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) configuration for real-time notifications | ## Pricing Search uses your 5 credits per query used. If max urls are reached you will be refunded for unused queries. ## Quick Start Search for domains related to a topic: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "e-commerce platforms", "max_urls": 10 }' ``` **Response:** ```json { "success": true, "queries": ["e-commerce platforms"], "discovered_count": 10, "results": [ { "domain": "www.shopify.com", "url": "https://www.shopify.com/blog/best-ecommerce-platforms", "title": "11 Best Ecommerce Platforms for Your Business in 2026", "query": "e-commerce platforms" }, { "domain": "www.forbes.com", "url": "https://www.forbes.com/advisor/business/software/best-ecommerce-platform/", "title": "Best E-Commerce Platforms of 2026 - Forbes Advisor", "query": "e-commerce platforms" } ], "credits_used": 1.0, "credits_remaining": 9999.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:13.200Z", "finished_at": "2026-09-28T20:31:21.900Z", "elapsed_seconds": 9.4 } ``` ## Multiple Queries Search multiple topics in a single request: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": ["e-commerce platforms", "online store builders"], "max_urls": 20 }' ``` ## Region-Specific Results Get results tailored to a specific region. The region also determines the proxy location: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "news websites", "max_urls": 15, "region": "uk-en" }' ``` ## Available Regions | Region Code | Description | |-------------|-------------| | `us-en` | United States (English) | | `uk-en` | United Kingdom (English) | | `de-de` | Germany (German) | | `fr-fr` | France (French) | | `es-es` | Spain (Spanish) | | `it-it` | Italy (Italian) | | `jp-jp` | Japan (Japanese) | | `au-en` | Australia (English) | ## Response Fields | Field | Description | |-------|-------------| | `domain` | The domain name of the discovered URL | | `url` | The full URL | | `title` | Page title from search results | | `query` | Which query matched this result | ## Common Use Cases **Competitor Discovery** Find competitor websites by searching for your product category. **Lead Generation** Discover potential leads by searching for industry-specific terms. **Market Research** Find relevant websites and articles for research purposes. **Content Discovery** Locate content sources for aggregation or analysis. ## Async Mode For large searches, use async mode: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "proxy services", "max_urls": 50, "async": true }' ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Search task submitted for background processing", "check_url": "/api/v1/scraper/search/tasks/550e8400-e29b-41d4-a716-446655440000", "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check status:** ```bash curl "https://scrape.evomi.com/api/v1/scraper/search/tasks/550e8400-e29b-41d4-a716-446655440000" \ -H "x-api-key: YOUR_API_KEY" ``` While the task runs, polling returns `202` with the [task timing fields](/scraping-products-instructions/scraper-api/result-polling/#task-timing) (also mirrored as `X-Task-*` headers); a finished poll returns the full result with the final timing. ## Base URL ``` https://scrape.evomi.com ``` All API requests use this base URL with the `/api/v1/scraper/search` endpoint. ## Error Handling | Status | Meaning | |--------|---------| | 200 | Success | | 202 | Async task processing | | 400 | Bad request | | 401 | Unauthorized | | 402 | Insufficient credits | | 404 | Task not found | | 429 | Rate limit exceeded | ## Next Steps - **[Usage Examples](/scraping-products-instructions/scraper-api/tools/search/usage-examples/)** - More detailed examples - Found your Domain? Use **[Url Discovery](/scraping-products-instructions/scraper-api/tools/url-discovery/)** to discover urls from the domain and **[Scraper API](/scraping-products-instructions/scraper-api/)** to scrape them Search results are unique per domain - each domain appears only once even if multiple pages match. --- ## Search Tool Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/search/usage-examples/ Practical examples for using the Search API. ## Basic Search Find domains related to a topic: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "best CRM software", "max_urls": 10 }' ``` ## Multiple Queries Search multiple topics in one request: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": ["CRM software", "sales automation tools", "customer management platforms"], "max_urls": 30 }' ``` **Response includes which query matched each result:** ```json { "success": true, "queries": ["CRM software", "sales automation tools", "customer management platforms"], "discovered_count": 30, "results": [ { "domain": "www.salesforce.com", "url": "https://www.salesforce.com/crm/", "title": "What is CRM? CRM Defined - Salesforce.com", "query": "CRM software" }, { "domain": "www.hubspot.com", "url": "https://www.hubspot.com/products/crm", "title": "Free CRM Software - HubSpot CRM", "query": "CRM software" } ], "credits_used": 3.0, "credits_remaining": 9997.0 } ``` ## Region-Specific Search Get results from a specific region. The region also determines the proxy location: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "job boards", "max_urls": 15, "region": "de-de" }' ``` ### Available Regions | Region Code | Description | |-------------|-------------| | `us-en` | United States (English) | | `uk-en` | United Kingdom (English) | | `de-de` | Germany (German) | | `fr-fr` | France (French) | | `es-es` | Spain (Spanish) | | `it-it` | Italy (Italian) | | `jp-jp` | Japan (Japanese) | | `au-en` | Australia (English) | ## Async Mode For large searches, process in the background: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "cloud computing providers", "max_urls": 100, "async": true }' ``` **Check task status:** ```bash curl "https://scrape.evomi.com/api/v1/scraper/search/tasks/YOUR_TASK_ID" \ -H "x-api-key: YOUR_API_KEY" ``` ## Python Example ```python import requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://scrape.evomi.com/api/v1/scraper/search" def search_domains(query, max_urls=20, region="us-en"): """Search for domains related to a query.""" headers = { "x-api-key": API_KEY, "Content-Type": "application/json" } payload = { "query": query, "max_urls": max_urls, "region": region } response = requests.post(BASE_URL, json=payload, headers=headers) return response.json() # Example usage result = search_domains("e-commerce platforms", max_urls=10) for item in result.get("results", []): print(f"{item['domain']}: {item['title']}") ``` ## Python with Multiple Queries ```python import requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://scrape.evomi.com/api/v1/scraper/search" def search_multiple(queries, max_urls=50): """Search for multiple queries at once.""" headers = { "x-api-key": API_KEY, "Content-Type": "application/json" } payload = { "query": queries, # Pass list of queries "max_urls": max_urls } response = requests.post(BASE_URL, json=payload, headers=headers) return response.json() # Search for multiple competitors queries = [ "email marketing tools", "marketing automation software", "newsletter platforms" ] result = search_multiple(queries, max_urls=30) print(f"Found {result['discovered_count']} domains") for item in result.get("results", []): print(f" {item['domain']} (from: {item['query']})") ``` ## Python Async Handling ```python import requests import time API_KEY = "YOUR_API_KEY" BASE_URL = "https://scrape.evomi.com/api/v1/scraper/search" def search_async(query, max_urls=100): """Start async search and poll for results.""" headers = { "x-api-key": API_KEY, "Content-Type": "application/json" } # Start async search payload = { "query": query, "max_urls": max_urls, "async": True } response = requests.post(BASE_URL, json=payload, headers=headers) task_id = response.json().get("task_id") if not task_id: return response.json() # Poll for results check_url = f"{BASE_URL}/tasks/{task_id}" while True: time.sleep(2) # Wait 2 seconds between checks result = requests.get(check_url, headers=headers).json() if result.get("status") == "success": return result elif result.get("status") == "failure": raise Exception("Search failed") # Usage result = search_async("SaaS companies", max_urls=50) print(f"Found {result['discovered_count']} domains") ``` ## Combined with Scraper Search for domains, then scrape them: ```python import requests API_KEY = "YOUR_API_KEY" SEARCH_URL = "https://scrape.evomi.com/api/v1/scraper/search" SCRAPER_URL = "https://scrape.evomi.com/api/v1/scraper/realtime" headers = { "x-api-key": API_KEY, "Content-Type": "application/json" } # Step 1: Search for domains search_payload = { "query": "productivity apps", "max_urls": 10 } search_result = requests.post(SEARCH_URL, json=search_payload, headers=headers).json() # Step 2: Scrape each discovered URL for item in search_result.get("results", []): scrape_payload = { "url": item["url"], "mode": "request" } scrape_result = requests.post(SCRAPER_URL, json=scrape_payload, headers=headers).json() print(f"Scraped {item['domain']}:") print(f" Title: {scrape_result.get('title', 'N/A')}") print() ``` ## Common Use Cases ### Competitor Research ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": ["project management software", "task management tools"], "max_urls": 20 }' ``` ### Market Research ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "artificial intelligence startups", "max_urls": 50, "region": "us-en" }' ``` ### Local Business Discovery ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "web design agencies", "max_urls": 30, "region": "uk-en" }' ``` ## Tips 1. **Start small** — Test with low `max_urls` values to understand results before scaling. 2. **Use multiple queries** — Different search terms may find different domains. 3. **Choose the right region** — Use the appropriate region for locally relevant results. 4. **Combine with scraping** — Use discovered URLs with the Scraper API for complete data extraction. --- ## Url Discovery Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/url-discovery/ Our Url Discovery API discovers URLs from any domain. Find and filter thousands of URLs in seconds and optionally validate them all in one request—perfect for content audits, competitor analysis, and bulk data collection. ## What is Url Discovery? Url Discovery finds URLs from a domain without scraping them. Choose how you want to discover URLs: from sitemaps, Common Crawl archive, or by actively crawling the site. **Returns URLs only** — no content is scraped. Use this when you just need the URLs. View "Domain Crawling" Tab for domain scraping. ## Discovery Methods Choose one or more discovery sources: | Source | Description | |--------|-------------| | `sitemap` | Fast and structured. Automatically discovers sitemaps from common locations, robots.txt, and sitemap indexes. | | `commoncrawl` | Historical archive with billions of URLs. Great for finding pages not in sitemaps. | | `crawl` | Actively browse the site by following links. Discovers pages dynamically. | You can use one source or combine multiple sources for maximum coverage. ## Parameters Overview | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `domain` | string | Yes | Domain to discover (e.g., "example.com") | | `sources` | array | No | Sources to use: `["sitemap"]`, `["commoncrawl"]`, `["crawl"]`, or combinations. Default: `["sitemap", "commoncrawl"]` | | `max_urls` | integer | Yes | Maximum URLs to discover (1-10,000) | | `check_if_live` | boolean | No | Validate URLs with HEAD checks. Default: true | | `url_pattern` | string | No | Regex pattern to filter discovered URLs | | `depth` | integer | No | Crawl depth when using `crawl` source. Default: 1 | | `async` | boolean | No | Process in background and return task ID. Default: false | | `webhook` | object | No | [Webhooks](/scraping-products-instructions/scraper-api/webhooks/) configuration for real-time notifications | ## Pricing | Operation | Cost | |-----------|------| | Sitemap discovery | 2 credits | | Common Crawl discovery | 2 credits | | Crawl source | Scraper's rates apply | | URL validation (per URL) | 0.5 credits | **Examples:** - Discover 100 URLs from sitemap + validate = 2 + (100 × 0.5) = **52 credits** - Discover 500 URLs from both sources + validate = 4 + (500 × 0.5) = **254 credits** - Discover 1000 URLs, no validation = **2-4 credits** **Smart Refunds**: If fewer URLs are discovered than your `max_urls` limit, you're only charged for actual validation performed. ## Quick Start Discover URLs from a domain: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 100 }' ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 87, "results": [ { "url": "https://example.com/", "source": "sitemap" }, { "url": "https://example.com/about", "source": "sitemap" } ], "credits_used": 45.5, "credits_remaining": 954.5, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:13.855Z", "finished_at": "2026-09-28T20:31:44.100Z", "elapsed_seconds": 31.6 } ``` ## Filter with URL Patterns Use regex to find specific URLs: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "sources": ["sitemap"], "url_pattern": "/blog/.*", "max_urls": 50 }' ``` ## Use Specific Sources **Sitemap only:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "sources": ["sitemap"], "max_urls": 100 }' ``` **Common Crawl only:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "sources": ["commoncrawl"], "max_urls": 100 }' ``` **Active crawl:** ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "sources": ["crawl"], "depth": 2, "max_urls": 100 }' ``` ## Async Mode For large discoveries, use async mode: ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 5000, "async": true }' ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Map task submitted for background processing", "check_url": "/api/v1/scraper/map/tasks/550e8400-e29b-41d4-a716-446655440000", "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check status:** ```bash curl "https://scrape.evomi.com/api/v1/scraper/map/tasks/550e8400-e29b-41d4-a716-446655440000" \ -H "x-api-key: YOUR_API_KEY" ``` While the task runs, polling returns `202` with the [task timing fields](/scraping-products-instructions/scraper-api/result-polling/#task-timing) (also mirrored as `X-Task-*` headers); a finished poll returns the full result with the final timing. ## Common Use Cases **Content Audits** Discover all pages on your site to ensure completeness or check for broken links. **Competitor Analysis** Map out competitor websites—find all product pages, blog posts, or landing pages. **SEO Monitoring** Track which URLs are indexed and validate they're accessible. **Bulk Collection** Build a list of URLs to use with other tools or processes. ## Base URL ``` https://scrape.evomi.com ``` All API requests use this base URL with the `/api/v1/scraper/map` endpoint. ## Error Handling | Status | Meaning | |--------|---------| | 200 | Success | | 202 | Async task processing | | 400 | Bad request | | 401 | Unauthorized | | 402 | Insufficient credits | | 404 | Task not found | | 429 | Rate limit exceeded | ## Next Steps - **[Usage Examples](/scraping-products-instructions/scraper-api/tools/url-discovery/usage-examples/)** - More detailed examples - Need to scrape too? Use **[Domain Crawling](/scraping-products-instructions/scraper-api/tools/domain-crawling/)** instead Start with small `max_urls` values to understand costs. Use `check_if_live: false` for initial discovery. --- ## URL Discovery Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/tools/url-discovery/usage-examples/ Practical examples showing request payloads and response formats for common Url Discovery scenarios. Use the endpoint: ``` POST https://scrape.evomi.com/api/v1/scraper/map ``` Include your API key in the header: `x-api-key: YOUR_API_KEY` ## Basic URL Discovery ### Example 1: Discover URLs from Sitemap Find all URLs from a domain's sitemap without validation. **Request:** ```json { "domain": "example.com", "sources": ["sitemap"], "max_urls": 100, "check_if_live": false } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 87, "results": [ { "url": "https://example.com/", "source": "sitemap" }, { "url": "https://example.com/about", "source": "sitemap" }, { "url": "https://example.com/products", "source": "sitemap" }, { "url": "https://example.com/blog/post-1", "source": "sitemap" } ], "credits_used": 2.0, "credits_remaining": 998.0 } ``` ### Example 2: Discover from Both Sources Use both sitemap and Common Crawl for maximum coverage. **Request:** ```json { "domain": "example.com", "sources": ["sitemap", "commoncrawl"], "max_urls": 500, "check_if_live": false } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 463, "results": [ { "url": "https://example.com/", "source": "sitemap" }, { "url": "https://example.com/old-page", "source": "commoncrawl" }, { "url": "https://example.com/archived-content", "source": "commoncrawl" } ], "credits_used": 4.0, "credits_remaining": 996.0 } ``` ## URL Validation ### Example 3: Discover and Validate URLs Check that discovered URLs are still live and accessible. **Request:** ```json { "domain": "example.com", "sources": ["sitemap"], "max_urls": 100, "check_if_live": true } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 94, "results": [ { "url": "https://example.com/", "source": "sitemap" }, { "url": "https://example.com/about", "source": "sitemap" }, { "url": "https://example.com/products", "source": "sitemap" } ], "credits_used": 49.0, "credits_remaining": 951.0 } ``` **Note:** Only 94 of 100 discovered URLs passed the live check. Validation costs: 2 credits (sitemap) + 47 credits (94 × 0.5) = 49 credits total. ## Pattern Filtering ### Example 4: Filter for Blog Posts Only Use regex to find only blog post URLs. **Request:** ```json { "domain": "example.com", "sources": ["sitemap"], "url_pattern": "/blog/[^/]+/?$", "max_urls": 50, "check_if_live": true } ``` **Response:** ```json { "success": true, "domain": "example.com", "discovered_count": 42, "results": [ { "url": "https://example.com/blog/introduction-to-apis", "source": "sitemap" }, { "url": "https://example.com/blog/advanced-scraping-techniques", "source": "sitemap" }, { "url": "https://example.com/blog/proxy-best-practices", "source": "sitemap" } ], "credits_used": 23.0, "credits_remaining": 977.0 } ``` ### Example 5: Filter for Product Pages Target product URLs with specific patterns. **Request:** ```json { "domain": "shop.example.com", "sources": ["sitemap", "commoncrawl"], "url_pattern": "/products?/[a-z0-9-]+", "max_urls": 200, "check_if_live": true } ``` **Response:** ```json { "success": true, "domain": "shop.example.com", "discovered_count": 187, "results": [ { "url": "https://shop.example.com/product/laptop-stand", "source": "sitemap" }, { "url": "https://shop.example.com/products/wireless-mouse", "source": "sitemap" }, { "url": "https://shop.example.com/product/usb-hub", "source": "commoncrawl" } ], "credits_used": 97.5, "credits_remaining": 902.5 } ``` ## Asynchronous Processing ### Example 6: Large Domain (Async Mode) Process large domains in the background and poll for results. **Request:** ```json { "domain": "largecorp.com", "sources": ["sitemap", "commoncrawl"], "max_urls": 5000, "check_if_live": true, "async": true } ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Map task submitted for background processing", "check_url": "/api/v1/scraper/map/tasks/550e8400-e29b-41d4-a716-446655440000", "credits_reserved": 2504.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` **Check Status:** ``` GET https://scrape.evomi.com/api/v1/scraper/map/tasks/550e8400-e29b-41d4-a716-446655440000 ``` **Status Response (Still Processing):** ```json { "success": true, "task_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "message": "Task is still processing", "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:13.855Z", "finished_at": null, "elapsed_seconds": 21.4 } ``` **Status Response (Completed):** ```json { "success": true, "domain": "largecorp.com", "discovered_count": 4732, "results": [ { "url": "https://largecorp.com/", "source": "sitemap" } ], "credits_used": 2370.0, "credits_remaining": 7630.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": "2026-09-28T20:31:13.855Z", "finished_at": "2026-09-28T20:31:44.100Z", "elapsed_seconds": 31.6 } ``` ## Real-World Scenarios ### Example 7: Content Audit - Find All Site Pages Comprehensive site audit for SEO and content management. **Request:** ```json { "domain": "mycompany.com", "sources": ["sitemap", "commoncrawl"], "max_urls": 1000, "check_if_live": true, "async": true } ``` **Response (202 Accepted):** ```json { "success": true, "task_id": "e5f6a7b8-c9d0-12e3-4f56-789abcdef012", "status": "processing", "message": "Map task submitted for background processing", "check_url": "/api/v1/scraper/map/tasks/e5f6a7b8-c9d0-12e3-4f56-789abcdef012", "credits_reserved": 504.0, "created_at": "2026-09-28T20:31:12.482Z", "started_at": null, "finished_at": null, "elapsed_seconds": 0.1 } ``` ### Example 8: Competitor Analysis - Product Pages Discover all product pages from a competitor's site. **Request:** ```json { "domain": "competitor.com", "sources": ["sitemap"], "url_pattern": "/(products?|shop|store)/", "max_urls": 500, "check_if_live": true } ``` **Response:** ```json { "success": true, "domain": "competitor.com", "discovered_count": 387, "results": [ { "url": "https://competitor.com/products/item-1", "source": "sitemap" }, { "url": "https://competitor.com/shop/category/electronics", "source": "sitemap" } ], "credits_used": 195.5, "credits_remaining": 804.5 } ``` ### Example 9: Archive Research - Historical URLs Find historical URLs no longer in active sitemaps. **Request:** ```json { "domain": "oldsite.com", "sources": ["commoncrawl"], "max_urls": 1000, "check_if_live": false } ``` **Response:** ```json { "success": true, "domain": "oldsite.com", "discovered_count": 856, "results": [ { "url": "https://oldsite.com/archived-page-2020", "source": "commoncrawl" }, { "url": "https://oldsite.com/removed-content", "source": "commoncrawl" } ], "credits_used": 2.0, "credits_remaining": 998.0 } ``` ## Error Responses ### Insufficient Credits **Response (402 Payment Required):** ```json { "success": false, "error": "Insufficient credits", "error_code": "INSUFFICIENT_CREDITS", "message": "Your account has insufficient credits. Required: 254.0, Available: 100.0", "credits_required": 254.0, "credits_available": 100.0 } ``` ### Invalid Domain **Response (400 Bad Request):** ```json { "success": false, "error": "Invalid request parameters", "error_code": "VALIDATION_ERROR", "message": "domain is required and must be a valid domain name" } ``` ### Invalid Pattern **Response (400 Bad Request):** ```json { "success": false, "error": "Invalid regex pattern", "error_code": "VALIDATION_ERROR", "message": "url_pattern contains invalid regex syntax" } ``` ## Best Practices ### 1. Start Small Test with small `max_urls` values to understand costs: ```json { "domain": "example.com", "sources": ["sitemap"], "max_urls": 10, "check_if_live": false } ``` ### 2. Selective Validation Discover first without validation, then validate only needed URLs: ```json { "domain": "example.com", "sources": ["sitemap"], "max_urls": 1000, "check_if_live": false } ``` ### 3. Use Patterns Efficiently Filter during discovery, not after: ```json { "domain": "blog.example.com", "url_pattern": "/blog/[0-9]{4}/", "max_urls": 100 } ``` ### 4. Async for Large Jobs Use async mode for 1000+ URLs or when scraping: ```json { "domain": "largecorp.com", "max_urls": 5000, "async": true } ``` ### 5. Monitor Credits Check response headers and JSON for credit usage: ``` X-Credits-Used: 52.0 X-Credits-Remaining: 948.0 ``` Always test discovery costs before enabling scraping. Discovery is cheap (2-4 credits + validation), but discovering and validating 1000 URLs can consume significant credits. --- ## Scraper API Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/usage-examples/ Complete code examples in Python, Node.js, and more showing how to integrate the Scraper API into your applications. ## Python Examples ### Basic Request ```python import requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://scrape.evomi.com/api/v1/scraper/realtime" def scrape_url(url): response = requests.get( f"{BASE_URL}?url={url}&api_key={API_KEY}" ) if response.status_code == 200: return response.text else: print(f"Error: {response.status_code}") return None # Use it html = scrape_url("https://example.com") print(html[:200]) ``` ### JSON Response with Metadata ```python import requests def scrape_with_metadata(url): response = requests.get( BASE_URL, params={ "url": url, "delivery": "json", "include_content": True }, headers={"x-api-key": API_KEY} ) data = response.json() print(f"Title: {data['title']}") print(f"Status: {data['status_code']}") print(f"Credits Used: {data['credits_used']}") print(f"Mode: {data['mode_used']}") return data result = scrape_with_metadata("https://example.com") ``` ### Browser Mode with Screenshot ```python import requests import json def scrape_with_screenshot(url): response = requests.post( BASE_URL, headers={ "x-api-key": API_KEY, "Content-Type": "application/json" }, json={ "url": url, "mode": "browser", "screenshot": True, "delivery": "json" } ) data = response.json() screenshot_url = data.get("screenshot_uri") print(f"Screenshot available at: {screenshot_url}") # Download screenshot img_response = requests.get(screenshot_url) with open("screenshot.png", "wb") as f: f.write(img_response.content) return data scrape_with_screenshot("https://example.com") ``` ### Markdown Output for AI Processing ```python import requests def get_markdown(url): response = requests.get( f"{BASE_URL}?url={url}&content=markdown&delivery=raw&api_key={API_KEY}" ) return response.text # Get clean text markdown = get_markdown("https://blog.example.com/post") # Feed to your AI model # summary = openai.complete(f"Summarize: {markdown}") ``` ### JavaScript Automation ```python import requests import json def automated_search(search_term): response = requests.post( BASE_URL, headers={ "x-api-key": API_KEY, "Content-Type": "application/json" }, json={ "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": "#search-input"}, {"fill": ["#search-input", search_term]}, {"click": "#search-button"}, {"wait": 2000}, {"wait_for": ".search-results"} ], "delivery": "json", "include_content": True } ) data = response.json() return data['content'] results = automated_search("web scraping") print(results[:500]) ``` ### AI Enhancement ```python import requests import json def extract_article_data(url): response = requests.post( BASE_URL, headers={ "x-api-key": API_KEY, "Content-Type": "application/json" }, json={ "url": url, "ai_enhance": True, "ai_source": "markdown", "ai_prompt": "Extract headline, author, date, and create 2-sentence summary as JSON", "delivery": "json" } ) data = response.json() return data['ai_response'] article_data = extract_article_data("https://news.example.com/article") print(json.dumps(article_data, indent=2)) ``` ### Async Processing with Polling ```python import requests import time def submit_async_scrape(url): response = requests.post( BASE_URL, headers={ "x-api-key": API_KEY, "Content-Type": "application/json" }, json={ "url": url, "async": True } ) data = response.json() return data['task_id'] def check_task_status(task_id): url = f"https://scrape.evomi.com/api/v1/scraper/tasks/{task_id}" response = requests.get(url, headers={"x-api-key": API_KEY}) return response.json() def wait_for_result(task_id, max_wait=120): start_time = time.time() while time.time() - start_time < max_wait: status_data = check_task_status(task_id) if status_data['status'] == 'completed': return status_data['result'] elif status_data['status'] == 'failed': raise Exception(f"Task failed: {status_data.get('error')}") time.sleep(2) # Poll every 2 seconds raise TimeoutError("Task did not complete in time") # Use it task_id = submit_async_scrape("https://example.com") print(f"Task submitted: {task_id}") result = wait_for_result(task_id) print(f"Content length: {len(result['content'])}") ``` ### Retry with Exponential Backoff ```python import requests import time def scrape_with_retry(url, max_retries=3): for attempt in range(max_retries): try: response = requests.get( f"{BASE_URL}?url={url}&api_key={API_KEY}", timeout=60 ) if response.status_code == 200: return response.text elif response.status_code == 429: # Rate limited data = response.json() wait_time = data.get("reset", 60) print(f"Rate limited. Waiting {wait_time}s...") time.sleep(wait_time) continue elif response.status_code >= 500: # Server error - retry with backoff if attempt < max_retries - 1: wait_time = 2 ** attempt print(f"Server error. Retrying in {wait_time}s...") time.sleep(wait_time) continue # Other errors - don't retry response.raise_for_status() except requests.exceptions.Timeout: if attempt < max_retries - 1: wait_time = 2 ** attempt print(f"Timeout. Retrying in {wait_time}s...") time.sleep(wait_time) else: raise return None content = scrape_with_retry("https://example.com") ``` ## Node.js Examples ### Basic Request ```javascript const axios = require('axios'); const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://scrape.evomi.com/api/v1/scraper/realtime'; async function scrapeUrl(url) { try { const response = await axios.get( `${BASE_URL}?url=${encodeURIComponent(url)}&api_key=${API_KEY}` ); console.log('Credits used:', response.headers['x-credits-used']); return response.data; } catch (error) { console.error('Error:', error.response?.status, error.message); return null; } } // Use it scrapeUrl('https://example.com').then(html => { console.log(html.substring(0, 200)); }); ``` ### POST with JSON Configuration ```javascript const axios = require('axios'); async function scrapeWithConfig(url) { try { const response = await axios.post(BASE_URL, { url: url, mode: 'browser', proxy_type: 'residential', proxy_country: 'US', wait_until: 'networkidle', delivery: 'json' }, { headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' } }); const data = response.data; console.log(`Title: ${data.title}`); console.log(`Credits used: ${data.credits_used}`); console.log(`Mode used: ${data.mode_used}`); return data; } catch (error) { console.error('Error:', error.response?.data); throw error; } } scrapeWithConfig('https://example.com'); ``` ### Batch Scraping with Promise.all ```javascript const axios = require('axios'); async function scrapeBatch(urls) { const promises = urls.map(url => axios.get( `${BASE_URL}?url=${encodeURIComponent(url)}&delivery=json&api_key=${API_KEY}` ).then(res => ({ url, title: res.data.title, credits: res.data.credits_used })).catch(err => ({ url, error: err.message })) ); return Promise.all(promises); } const urls = [ 'https://example.com/page1', 'https://example.com/page2', 'https://example.com/page3' ]; scrapeBatch(urls).then(results => { results.forEach(result => { if (result.error) { console.log(`❌ ${result.url}: ${result.error}`); } else { console.log(`✅ ${result.url}: ${result.title} (${result.credits} credits)`); } }); }); ``` ### Async with Polling ```javascript const axios = require('axios'); async function submitAsyncScrape(url) { const response = await axios.post(BASE_URL, { url, async: true }, { headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' } }); return response.data.task_id; } async function waitForResult(taskId, maxWait = 120000) { const startTime = Date.now(); const taskUrl = `https://scrape.evomi.com/api/v1/scraper/tasks/${taskId}`; while (Date.now() - startTime < maxWait) { const response = await axios.get(taskUrl, { headers: { 'x-api-key': API_KEY } }); const data = response.data; if (data.status === 'completed') { return data.result; } else if (data.status === 'failed') { throw new Error(`Task failed: ${data.error}`); } // Wait 2 seconds before next check await new Promise(resolve => setTimeout(resolve, 2000)); } throw new Error('Task did not complete in time'); } // Use it (async () => { const taskId = await submitAsyncScrape('https://example.com'); console.log(`Task submitted: ${taskId}`); const result = await waitForResult(taskId); console.log(`Content length: ${result.content.length}`); })(); ``` ## cURL Examples ### Simple GET ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" ``` ### Save to File ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" \ -o page.html ``` ### JSON Delivery ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&delivery=json&api_key=YOUR_API_KEY" \ | jq '.' ``` ### POST with Configuration ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "proxy_type": "residential", "proxy_country": "US", "screenshot": true }' \ -o screenshot.png ``` ### Markdown Output ```bash curl "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://blog.example.com/post&content=markdown&api_key=YOUR_API_KEY" \ -o article.md ``` ### With JavaScript Automation ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "js_instructions": [ {"wait_for": "#button"}, {"click": "#button"}, {"wait": 2000} ] }' ``` ### Show Response Headers ```bash curl -v "https://scrape.evomi.com/api/v1/scraper/realtime?url=https://example.com&api_key=YOUR_API_KEY" \ 2>&1 | grep -i "x-" ``` ## Best Practices ### 1. Use Environment Variables ```python import os API_KEY = os.environ.get('EVOMI_API_KEY') if not API_KEY: raise ValueError("EVOMI_API_KEY environment variable not set") ``` ### 2. Implement Error Handling ```python try: response = scrape(url) response.raise_for_status() except requests.exceptions.HTTPError as e: if e.response.status_code == 402: alert("Insufficient credits!") elif e.response.status_code == 429: time.sleep(60) # Rate limited except requests.exceptions.Timeout: log("Request timed out") ``` ### 3. Monitor Credits ```python credits_remaining = float(response.headers.get('X-Credits-Remaining', 0)) if credits_remaining < 100: send_alert(f"Low credits: {credits_remaining}") ``` ### 4. Use Appropriate Modes ```python # For static sites with datacenter proxy scrape(url, mode='request', proxy_type='datacenter') # 1 credit # For static sites with residential proxy (better success rate) scrape(url, mode='request', proxy_type='residential') # 2 credits # For JS-heavy sites (requires residential proxy) scrape(url, mode='browser', proxy_type='residential') # 5 credits # Let API decide (requires residential proxy) scrape(url, mode='auto', proxy_type='residential') # 2-6 credits depending on needs ``` **Note:** Browser and auto modes only work with residential (premium) proxies. Datacenter proxies are restricted to request mode only. ### 5. Cache Results ```python import redis import hashlib cache = redis.Redis() def scrape_cached(url, ttl=3600): cache_key = hashlib.md5(url.encode()).hexdigest() cached = cache.get(cache_key) if cached: return cached.decode() content = scrape(url) cache.setex(cache_key, ttl, content) return content ``` For more examples and language-specific SDKs, check our [GitHub repository](https://github.com/evomi) or contact support for assistance with your specific use case. --- ## Webhooks Source: https://docs.evomi.com/scraping-products-instructions/scraper-api/webhooks/ **Webhooks** allow you to receive real-time notifications when scraping operations complete, fail, or start. Instead of polling for results, configure a webhook endpoint to receive instant updates about your scraping jobs. --- ## Overview Webhooks are HTTP callbacks that deliver event notifications to your configured URL. Use webhooks to: - **Get notified when scrapes complete** — Receive results automatically - **Handle failures gracefully** — React to errors in real-time - **Integrate with chat platforms** — Send notifications to Discord or Slack - **Build event-driven workflows** — Trigger downstream processes automatically --- ## Supported Platforms | Platform | Description | | :--- | :--- | | **Discord** | Send rich embeds to Discord channels via webhook URLs | | **Slack** | Post messages to Slack channels using incoming webhooks | | **Custom** | Send JSON payloads to any HTTP endpoint with HMAC signature verification | --- ## Webhook Configuration ### Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `url` | string | Yes | Your webhook endpoint URL | | `type` | string | Yes | Webhook type: `"discord"`, `"slack"`, or `"custom"` | | `events` | array | Yes | List of events to subscribe to | | `secret` | string | No | Secret key for HMAC signature (custom webhooks only) | ### Available Events | Event | Description | | :--- | :--- | | `started` | Job has been queued and started | | `completed` | Job finished successfully | | `failed` | Job encountered an error and failed | --- ## Usage Examples ### With Scrape Requests ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/realtime" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "mode": "browser", "webhook": { "url": "https://your-server.com/webhook", "type": "custom", "events": ["completed", "failed"], "secret": "your-secret-key" } }' ``` ### With Crawl Requests ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "max_urls": 100, "webhook": { "url": "https://discord.com/api/webhooks/...", "type": "discord", "events": ["completed"] } }' ``` ### With Map Requests ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/map" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "sources": ["sitemap", "commoncrawl"], "webhook": { "url": "https://hooks.slack.com/services/...", "type": "slack", "events": ["completed", "failed"] } }' ``` ### With Search Requests ```bash curl -X POST "https://scrape.evomi.com/api/v1/scraper/search" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "e-commerce platforms", "max_urls": 20, "webhook": { "url": "https://your-server.com/webhook", "type": "custom", "events": ["completed"] } }' ``` ### With Schedules ```bash curl -X POST "https://scrape.evomi.com/api/v1/account/schedule" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Daily Price Monitor", "config_id": "cfg_abc123", "interval_minutes": 1440, "webhook": { "url": "https://discord.com/api/webhooks/...", "type": "discord", "events": ["completed", "failed"] } }' ``` --- ## Platform-Specific Integration ### Discord Webhooks Discord webhooks post rich embeds to a channel. Get your webhook URL from channel settings: 1. Go to **Channel Settings** → **Integrations** → **Webhooks** 2. Click **New Webhook** and copy the URL ```bash # Discord webhook example "webhook": { "url": "https://discord.com/api/webhooks/123456789/abc123...", "type": "discord", "events": ["completed", "failed"] } ``` Discord embeds include: - Job status with color coding (green for success, red for failure) - URL scraped - Credits used - Timestamp ### Slack Webhooks Slack webhooks post messages using incoming webhook URLs. Create one in your Slack app: 1. Create a **Slack App** or use an existing one 2. Enable **Incoming Webhooks** 3. Create a new webhook URL ```bash # Slack webhook example "webhook": { "url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX", "type": "slack", "events": ["completed"] } ``` Slack messages include: - Status emoji (✅ or ❌) - Job details in attachment format - Link to view results ### Custom Webhooks Custom webhooks send JSON payloads to any HTTP endpoint. Use the `secret` parameter for HMAC signature verification: ```bash # Custom webhook with HMAC signature "webhook": { "url": "https://your-server.com/api/webhooks/evomi", "type": "custom", "events": ["started", "completed", "failed"], "secret": "your-webhook-secret" } ``` --- ## Payload Structure ### Custom Webhook Payload When using custom webhooks, Evomi sends a POST request with the following JSON structure: ```json { "event": "completed", "timestamp": "2026-03-09T12:00:00Z", "data": { "task_id": "task_abc123", "task_type": "scrape", "url": "https://example.com", "status": "success", "credits_used": 5, "result": { "content": "...", "metadata": {} } } } ``` ### Payload Fields | Field | Type | Description | | :--- | :--- | :--- | | `event` | string | Event type: `started`, `completed`, or `failed` | | `timestamp` | string | ISO 8601 timestamp of the event | | `data.task_id` | string | Unique identifier for the task | | `data.task_type` | string | Type: `scrape`, `crawl`, `map`, or `search` | | `data.url` | string | URL that was scraped (for scrape tasks) | | `data.status` | string | `success` or `failed` | | `data.credits_used` | number | Credits consumed by the operation | | `data.result` | object | Scraping results (for completed events) | | `data.error` | string | Error message (for failed events) | --- ## HMAC Signature Verification For custom webhooks, verify the request authenticity using HMAC-SHA256 signatures. ### How It Works 1. Evomi creates a signature using your `secret` and the request body 2. The signature is sent in the `X-Evomi-Signature` header 3. Your server verifies the signature before processing ### Verification Example (Node.js) ```javascript const crypto = require('crypto'); function verifyWebhook(req, secret) { const signature = req.headers['x-evomi-signature']; const payload = JSON.stringify(req.body); const expectedSignature = 'sha256=' + crypto.createHmac('sha256', secret) .update(payload) .digest('hex'); return signature === expectedSignature; } // Usage app.post('/webhooks/evomi', (req, res) => { if (!verifyWebhook(req, 'your-webhook-secret')) { return res.status(401).send('Invalid signature'); } // Process webhook console.log('Event:', req.body.event); res.status(200).send('OK'); }); ``` ### Verification Example (Python) ```python import hmac import hashlib import json def verify_webhook(headers, body, secret): signature = headers.get('X-Evomi-Signature', '') expected = 'sha256=' + hmac.new( secret.encode(), json.dumps(body).encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) # Usage in Flask @app.route('/webhooks/evomi', methods=['POST']) def handle_webhook(): if not verify_webhook(request.headers, request.json, 'your-webhook-secret'): return 'Invalid signature', 401 # Process webhook event = request.json.get('event') print(f'Event: {event}') return 'OK', 200 ``` --- ## Best Practices ### 1. Respond Quickly Return a `200 OK` response within 5 seconds. Long-running processes should be handled asynchronously: ```python @app.route('/webhook', methods=['POST']) def webhook(): # Queue the work for background processing process_webhook_async.delay(request.json) return 'OK', 200 ``` ### 2. Implement Retry Logic If your endpoint is temporarily unavailable, Evomi will retry: - Up to 5 attempts - Exponential backoff - 24-hour retention ### 3. Verify Signatures Always verify HMAC signatures for custom webhooks to ensure requests come from Evomi. ### 4. Handle Duplicate Events Events may be delivered more than once. Use the `task_id` to deduplicate: ```python processed_tasks = set() def handle_webhook(payload): task_id = payload['data']['task_id'] if task_id in processed_tasks: return # Already processed processed_tasks.add(task_id) # Process the event ``` ### 5. Use HTTPS Webhook URLs must use HTTPS with a valid SSL certificate. --- ## Troubleshooting ### Webhooks Not Received 1. **Check firewall settings** — Ensure your server accepts connections from Evomi IPs 2. **Verify SSL certificate** — HTTPS is required with a valid certificate 3. **Review server logs** — Look for incoming requests from Evomi 4. **Test manually** — Use curl to test your endpoint ### Test Your Endpoint ```bash curl -X POST "https://your-server.com/webhook" \ -H "Content-Type: application/json" \ -H "X-Evomi-Signature: sha256=..." \ -d '{ "event": "completed", "timestamp": "2026-03-09T12:00:00Z", "data": { "task_id": "test_123", "task_type": "scrape", "status": "success" } }' ``` --- ## What's Next? - **[Schedules](/scraping-products-instructions/scraper-api/schedules/)** — Create scheduled scraping jobs - **[Scraper Configs](/scraping-products-instructions/scraper-api/scraper-config/)** — Save reusable configurations - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** — See complete code examples --- ## Scraping Browser Source: https://docs.evomi.com/scraping-products-instructions/scraping-browser/ Evomi Scraping Browser gives you access to a managed remote Chromium environment with integrated challenge handling and proxy routing. Connect through a single WebSocket CDP URL and control the browser using your preferred automation framework. ## Quick Start Connect using a Chrome DevTools Protocol (CDP) WebSocket URL: ```text wss://browser.evomi.com?key=YOUR_API_KEY&os=windows&proxy_country=US ``` **Optional parameters**: `os`, `proxy_country`, `adblock=true`. To disable automatic Turnstile solving: `solve_cloudflare=false`. ## What you get - **Managed Chromium environment**: Provides consistent browser characteristics for automated workflows - **Automatic challenge handling**: Integrated CAPTCHA and Turnstile support; toggle as needed - **Integrated proxies**: Route traffic with geo targeting directly from the session URL - **Scale by default**: Stateless sessions, horizontal scaling, and efficient resource usage Use only the provided `wss://browser.evomi.com` endpoint. Your API key authorizes browser sessions and usage. ## Next Steps - Usage Examples (Puppeteer, Playwright, Selenium, Pyppeteer) - WebSocket Parameters (full reference) --- ## Automatic CAPTCHA Support Source: https://docs.evomi.com/scraping-products-instructions/scraping-browser/automatic-captcha-support/ Evomi Scraping Browser includes built‑in, automatic solving for popular CAPTCHA providers. Solving runs transparently during navigation and form submits. You can disable specific solvers via URL parameters (e.g., `solve_cloudflare=false`). ## Supported Providers | Provider | Type | Cost | Logo | |---------|------|------|------| | Cloudflare Turnstile | Turnstile | Free | ![Cloudflare](/images/captcha/cloudflare.png) | | GeeTest | Slider/Behavioral | Free | ![GeeTest](/images/captcha/geetest.png) | | PerimeterX | Challenge | Free | ![PerimeterX](/images/captcha/perimeterx.png) | | Lemin Captcha | Puzzle | Free | ![Lemin Captcha](/images/captcha/lemincaptcha.png) | | reCAPTCHA | v2/v3/Enterprise | Free | ![reCAPTCHA](/images/captcha/recaptcha.png) | | AWS Captcha | WAF Captcha | Free | ![AWS](/images/captcha/aws.png) | Availability may vary by target site integration and challenge variant. The browser attempts safe fallbacks where possible. ## How it works - Detection occurs at runtime; challenges are solved inline with page actions. - No external solver keys or credits needed. - Solvers respect page security policies and emulate genuine user flows. ## Toggling solvers Use URL query parameters to control solvers globally per session. ```text wss://browser.evomi.com?key=YOUR_API_KEY&solve_cloudflare=false ``` ## Visual Examples The logos above are served locally from the docs static directory for reliability. Add real challenge screenshots later if needed. --- ## Scraping Browser Usage Examples Source: https://docs.evomi.com/scraping-products-instructions/scraping-browser/usage-examples/ Connect to Evomi Scraping Browser using the Chrome DevTools Protocol (CDP) WebSocket. Use your preferred framework and language; examples below show idiomatic setups. CDP URL format: ```text wss://browser.evomi.com?key=YOUR_API_KEY&os=windows&proxy_country=US&adblock=true ``` Optional: `os`, `proxy_country`, `adblock=true`. Disable Turnstile solving with `solve_cloudflare=false`. #### Puppeteer (Node.js) ```javascript const puppeteer = require('puppeteer'); (async () => { const cdpUrl = 'wss://browser.evomi.com?key=YOUR_API_KEY&os=windows&proxy_country=US&adblock=true'; const browser = await puppeteer.connect({ browserWSEndpoint: cdpUrl, defaultViewport: null }); const page = await browser.newPage(); await page.goto('https://check.evomi.com', { waitUntil: 'networkidle2' }); console.log(await page.title()); await browser.close(); })(); ``` #### Playwright (Node.js) Playwright JavaScript/TypeScript is NOT compatible with the Bun runtime. Use Node.js (`node`, `npm`, or `npx`) instead. This incompatibility with the CDP WS protocol is on the Bun side. ```javascript // WARNING: Do not run under Bun. Playwright JS/TS requires the Node.js runtime. const { chromium } = require('playwright'); (async () => { const ws = 'wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US'; const browser = await chromium.connectOverCDP(ws); const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://check.evomi.com'); console.log(await page.textContent('body')); await browser.close(); })(); ``` #### Patchright (Python) ```python from patchright.sync_api import sync_playwright cdp = 'wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US&adblock=true' with sync_playwright() as p: browser = p.chromium.connect_over_cdp(cdp) context = browser.contexts[0] if browser.contexts else browser.new_context() page = context.new_page() page.goto('https://check.evomi.com') print(page.title()) browser.close() ``` #### Rod (Go) ```go package main import ( "fmt" "github.com/go-rod/rod" ) func main() { cdp := "wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US&solve_cloudflare=false" browser := rod.New().ControlURL(cdp).MustConnect() page := browser.MustPage("https://check.evomi.com") fmt.Println(page.MustEval("() => document.title")) browser.MustClose() } ``` ## Tips for scale - Reuse a single session to perform multiple tasks sequentially when possible. - Use `proxy_country` to route traffic geographically without code changes. --- ## WebSocket Parameters Source: https://docs.evomi.com/scraping-products-instructions/scraping-browser/websocket-parameters/ Use these query parameters on the CDP WebSocket URL to configure your Evomi Scraping Browser sessions. ## Base Format ```text wss://browser.evomi.com?key=YOUR_API_KEY[¶m=value...] ``` Always keep your API key secret. Rotate keys from your dashboard if exposed. ## Parameters | Name | Type | Example | Default | Description | |------|------|---------|---------|-------------| | `key` | string | `key=evm_123...` | — (required) | Your Evomi API key authorizing the browser session | | `os` | enum | `os=windows` | provider default | Target OS fingerprint: `windows`, `macos`, `android` | | `proxy_country` | ISO 3166-1 alpha-2 | `proxy_country=US` | dynamic | Route traffic via integrated proxy for the specified country | | `proxy_state` | string | `proxy_state=california` | none | Target a state/province within the proxy country. Lowercase, dots for spaces (`new.york`, `southern.cape`); display names like `New York` are normalized automatically. Any well-formed value is passed through — unsupported locations fail at the proxy. Supported countries, regions and cities are listed in the [Evomi settings API](https://api.evomi.com/public/settings) | | `proxy_city` | string | `proxy_city=san.jose` | none | Target a specific city. Same format rules as `proxy_state`. Any well-formed value is passed through — unsupported locations fail at the proxy. Supported values are listed in the [Evomi settings API](https://api.evomi.com/public/settings) | | `language` | string (tag list) | `language=de-CH,de` | follows proxy IP | Browser languages & locale as comma-separated tags (max 128 chars, no q-values). Default: the browser follows the proxy IP / profile locale automatically | | `timezone` | string (IANA zone) | `timezone=Asia/Tokyo` | derived from `proxy_country` | Browser clock as an IANA timezone name (max 64 chars, must be a real zone — invalid values reject the connection). Default: the proxy country's primary zone, e.g. `US` → `America/New_York`, `DE` → `Europe/Berlin`, `JP` → `Asia/Tokyo` | | `adblock` | boolean | `adblock=true` | `false` | Enable built‑in ad/tracker blocking for cleaner pages and fewer popups | | `solve_cloudflare` | boolean | `solve_cloudflare=false` | `true` | Toggle automatic Cloudflare Turnstile solving | Parameters are optional unless marked required. Unrecognized parameters are ignored; `language` and `timezone` are validated and invalid values reject the connection. ## Examples ```text # Windows fingerprint, US proxy routing, adblock on wss://browser.evomi.com?key=YOUR_API_KEY&os=windows&proxy_country=US&adblock=true # Disable Turnstile solving for debugging wss://browser.evomi.com?key=YOUR_API_KEY&solve_cloudflare=false # German (Switzerland) locale regardless of proxy location wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US&language=de-CH,de # Tokyo clock regardless of proxy location wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US&timezone=Asia/Tokyo # City-level routing: San Jose, California exit wss://browser.evomi.com?key=YOUR_API_KEY&proxy_country=US&proxy_state=california&proxy_city=san.jose # Minimal: just your key wss://browser.evomi.com?key=YOUR_API_KEY ``` ## Behavior Notes - Sessions are ephemeral; reconnect to create a fresh environment. - Managed browser compatibility features are enabled by default and require no code changes. - Integrated proxy usage and CAPTCHA solving may affect usage accounting. - With your own proxy (`proxy=`) and no `timezone` given, the clock defaults to US Eastern (`America/New_York`) — pass `timezone` or `proxy_country` to set it explicitly. - `proxy_country`, `proxy_state` and `proxy_city` do not apply when using your own proxy (`proxy=`). Any well-formed value is passed through; unsupported locations fail at the proxy. Supported countries, regions and cities are listed in the [Evomi settings API](https://api.evomi.com/public/settings). Combine parameters to match your testing environment. Example: `os=windows&proxy_country=US&adblock=true`. --- ## Public API Source: https://docs.evomi.com/public-api/ Welcome to the **Evomi Proxy Provider API**. This API allows you to manage and access your proxy credentials programmatically using a secure API key. You can use this API to: - Retrieve your proxy credentials. - Access detailed information about your proxy products. Our API is designed to be easy to integrate into your applications, providing you with the flexibility to handle proxy management and data access seamlessly. ## Who is this API for? This API is intended for users who have an account with Evomi and wish to automate the management of their proxy services. Whether you are a developer, system administrator, or advanced user, this API allows you to access and manage your proxies programmatically. To use the API, you must have an active account with Evomi and an API key. If you're new to the platform, please visit our [dashboard](https://my.evomi.com) and sign up to get started. ## Key Features - **Retrieve Proxy Credentials:** Get detailed information about your proxy credentials for different proxy types (e.g., Residential Proxies, Mobile Proxies). - **Balance Information:** Monitor the available bandwidth for each of your proxies. - **Endpoint Details:** Access proxy endpoint and port details for integration with your applications. ## Getting Started To get started with the Evomi Public API, you need to obtain an API key from your dashboard. Follow the steps below: 1. **Log in** to your account at [Evomi Dashboard](https://my.evomi.com/settings/api). 2. **Navigate to Settings** > **API**. 3. Copy your **API Key**. Once you have your API key, you can authenticate and access the proxy data available in your account. For more details on how to authenticate and use the API, visit the [Authentication](/public-api/authentication/) page. If you need help with any part of the process, our support team is available to assist you. For more information, check out our documentation on: - [Authentication](/public-api/authentication/) - [Get Proxy Data](/public-api/endpoints/default/) --- ## Public API Authentication Source: https://docs.evomi.com/public-api/authentication/ To access the Evomi Public API, you must authenticate using an **API Key**. Evomi supports two key types: ## Personal API Key Tied to your user account. Use it only for your **personal** proxy resources. It does not follow the team selected in the dashboard. ### How to obtain your personal API key 1. **Log in** to your account at [Evomi Dashboard](https://my.evomi.com/settings/api). 2. **Navigate to Settings** > **API**. 3. Copy the **API Key** provided on this page. ## Team API Key Tied to a **team** (organization workspace). Use it when automating access to shared team proxy balances, credentials, and usage. A team key always acts on that team’s proxy identity, independent of which team is selected in the dashboard. ### How to obtain your team API key 1. **Log in** and open **Settings** > **Team**. 2. You must be a team **owner** or **admin**. 3. Copy the **Team API Key** shown in the team information section. Team members with the **member** role cannot view or rotate the team API key. Keep API keys secure. They grant access to proxy data and related API operations for their scope. ## Authentication Methods You can authenticate API requests using one of two methods: 1. **In the URL Parameters:** Add the API key to your request URL as a query parameter. Example: ``` https://api.evomi.com/public?apikey=YOUR_API_KEY ``` 2. **In the Request Header:** Add the API key to the request headers as `x-apikey`. Example (using curl): ```bash curl -H "x-apikey: YOUR_API_KEY" https://api.evomi.com/public ``` ## API Key Expiration API keys do not expire by default. If a key may have been compromised: - **Personal key:** regenerate from **Settings** > **API** in the dashboard. - **Team key:** regenerate from **Settings** > **Team** (owners and admins only). For more information on how to use the API to get proxy data, visit the [Get Proxy Data](/public-api/endpoints/default/) page. --- ## Endpoints Source: https://docs.evomi.com/public-api/endpoints/ Here you will find detailed information about the available endpoints in the Evomi Public API. These endpoints allow you to access various data related to your proxy products, scraper, and browser services, including credentials, balance, usage statistics, and endpoint details. - [Get Proxy Data](/public-api/endpoints/default/) - [Bandwidth Usage](/public-api/endpoints/bandwidth-usage/) - [Proxy Settings](/public-api/endpoints/settings/) - [Rotate Session](/public-api/endpoints/rotate_session/) - [Generate Proxies](/public-api/endpoints/generate/) - [Scraper](/public-api/endpoints/scraper/) - [Browser](/public-api/endpoints/browser/) --- ## Bandwidth Usage Source: https://docs.evomi.com/public-api/endpoints/bandwidth-usage/ The **Bandwidth Usage** endpoint returns the same proxy bandwidth and request statistics as the Evomi dashboard (**GET /products/proxy-history**), including Core Residential `extra` / `default` / `total` breakdowns. ## Endpoint ``` GET https://api.evomi.com/public/usage ``` ## Query Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `period` | `24h`, `3d`, or `7d` | No | `3d` | Time range preset. `24h` uses hourly buckets; `3d` and `7d` use daily buckets (same as the dashboard). | | `product` | string | Yes | — | Proxy product code: `rp`, `rpc`, `dcp`, `mp`, or `sdc`. | ### Product Codes | Code | Product | |------|---------| | `rp` | Premium Residential | | `rpc` | Core Residential | | `sdc` | Datacenter Proxies | | `mp` | Mobile Proxies | | `sdc` | Static Datacenter | ## Authentication Use your **personal** or **team** API key. See [Authentication](/public-api/authentication/). - A **personal API key** reports usage for your personal proxy account only. - A **team API key** reports usage for that team's shared proxy identity. ## Example Request Match the dashboard default (**Last 3 days**, Core Residential): ```bash curl -H "x-apikey: YOUR_API_KEY" \ "https://api.evomi.com/public/usage?period=3d&product=rpc" ``` Last 7 days (daily): ```bash curl -H "x-apikey: YOUR_API_KEY" \ "https://api.evomi.com/public/usage?period=7d&product=rpc" ``` Last 24 hours (hourly): ```bash curl -H "x-apikey: YOUR_API_KEY" \ "https://api.evomi.com/public/usage?period=24h&product=rpc" ``` ## Response Format ```json { "success": true, "data": { "bandwidth": { "products": [ { "name": "Residential", "code": "rpc", "totalBandwidth": 43971237.38, "stats": { "2026-05-26 00:00": { "default": 19072705.92, "extra": 0.26, "total": 19072706.18 } } } ] }, "requests": {} }, "meta": { "period": "3d", "product": "rpc", "duration": "3d", "granularity": "day", "ownership_scope": "personal", "auth_type": "personal", "proxy_username": "example_core_user", "bandwidth_unit": "MB", "total_bandwidth_mb": 43971237.38, "total_bandwidth_gb": 43971.23738, "total_bandwidth_tb": 43.97123738 } } ``` ### Fields - **data.bandwidth.products** — Only the requested `product`. Values are in **megabytes (MB)**, same as the dashboard chart total. - **data.bandwidth.products[].stats** — Per bucket (`hour` or `day`). For Core Residential (`rpc`), each bucket may include `default`, `extra`, and `total` (MB). - **data.bandwidth.products[].totalBandwidth** — Period total in MB (matches the dashboard bandwidth chart total). - **meta.total_bandwidth_mb** — Same as `totalBandwidth` for the requested product (MB). - **meta.total_bandwidth_gb** — `total_bandwidth_mb ÷ 1,000` - **meta.total_bandwidth_tb** — `total_bandwidth_mb ÷ 1,000,000` (e.g. `43971237` MB ≈ `43.97` TB) - **requests** — Recent request statistics (last 12 hours, hourly), same as the dashboard. ## Common Errors - **400 Bad Request** — Invalid `period` or `product`, or upstream stats error. - **401 Unauthorized** — Missing or invalid API key. - **403 Forbidden** — Email verification required (personal keys only). - **500 Internal Server Error** — Server-side failure. For authentication details, see [Authentication](/public-api/authentication/). --- ## Get Browser Data Source: https://docs.evomi.com/public-api/endpoints/browser/ The **Get Browser Data** endpoint returns information about your Browser API access, including your browser API key, remaining credits, concurrency limit, maximum session length, and the browser WebSocket endpoint URL. ## Endpoint `GET https://api.evomi.com/public/browser` ## Authentication Requires API key in either: - `x-apikey` header - `apikey` query parameter ## Example Request ```bash curl -X GET "https://api.evomi.com/public/browser" \ -H "x-apikey: YOUR_API_KEY" ``` Or via query parameter: ```bash curl -X GET "https://api.evomi.com/public/browser?apikey=YOUR_API_KEY" ``` ## Response (User Has Access) ```json { "success": true, "has_access": true, "credits": 1000, "concurrency": 3, "api_key": "browser_key_xyz", "max_session_length_seconds": 60, "endpoint_url": "wss://browser.evomi.com" } ``` ## Response (User Has No Access) ```json { "success": true, "has_access": false, "credits": 0, "concurrency": 0, "api_key": null, "max_session_length_seconds": 0, "endpoint_url": "wss://browser.evomi.com" } ``` ## Fields in the Response | Field | Type | Description | |-------|------|-------------| | `success` | boolean | Whether the request was processed successfully | | `has_access` | boolean | Whether the user has access to the Browser API | | `credits` | integer | Remaining browser credits | | `concurrency` | integer | Maximum number of concurrent browser sessions | | `api_key` | string \| null | Your Browser API key, or `null` if you don't have access | | `max_session_length_seconds` | integer | Maximum allowed duration for a single browser session, in seconds | | `endpoint_url` | string | The Browser API WebSocket endpoint URL | The `api_key` returned here is your **Browser API key**, used to authenticate browser sessions against the browser endpoint. It is distinct from the Evomi public API key used to call this endpoint. ## Error Responses - **401 Unauthorized:** The API key provided is either missing or invalid. Ensure that you are passing the correct API key either in the URL or in the request header. - **500 Internal Server Error:** This indicates a problem on our server side. Please contact support if you encounter this error. For more information on how to authenticate your requests, check out the [Authentication](/public-api/authentication/) page. --- ## Get Proxy Data Source: https://docs.evomi.com/public-api/endpoints/default/ The **Get Proxy Data** endpoint allows you to retrieve detailed information about your proxy products, including credentials, balance, and endpoint details. This data is essential for integrating your proxies into applications and monitoring their usage. ## Endpoint ``` GET https://api.evomi.com/public ``` ### Response Format The API will return a **JSON** response with details about the proxy products linked to your account. Here is an example of the response structure: ```json { "success": true, "products": { "rp": { "username": "proxy_username", "password": "proxy_password", "balance_mb": 0, "endpoint": "premium-residential.evomi.com", "ports": { "http": 1000, "socks5": 1002 } }, "rpc": { "username": "proxy_username", "password": "proxy_password", "balance_mb": 133700.848432, "endpoint": "core-residential.evomi.com", "ports": { "http": 1000, "socks5": 1002 } }, "mp": { "username": "proxy_username", "password": "proxy_password", "balance_mb": 0, "endpoint": "mp.evomi.com", "ports": { "http": 3000, "socks5": 3002 } }, "dcp": { "username": "proxy_username", "password": "proxy_password", "balance_mb": 0, "endpoint": "dcp.evomi.com", "ports": { "http": 2000, "socks5": 2002 } }, "static_residential": { "packages": [], "ports": { "http": 12345, "socks5": 12346 } } } } ``` ### Fields in the Response - **success:** Indicates whether the request was successful. It will always be `true` if the request was processed correctly. - **products:** An object containing detailed information about each proxy product available in your account. - **username:** The proxy username. - **password:** The proxy password. - **balance_mb:** The available data balance for the proxy in megabytes (MB). - **endpoint:** The endpoint for the proxy server (e.g., `premium-residential.evomi.com`). - **ports:** The available ports for the proxy, including `http` and `socks5`. ### Product Codes | Shortcode | Product Name | |--------------------|---------------------| | rp | Premium Residential | | rpc | Core Residential | | dcp | Datacenter Proxies | | mp | Mobile Proxies | | static_residential | Static Residential | ## Example Request Here is an example of how you can make a request using **curl**: ```bash curl -H "x-apikey: YOUR_API_KEY" https://api.evomi.com/public ``` Our you could also pass the API key as a query parameter in the URL: ```bash https://api.evomi.com/public?apikey=YOUR_API_KEY ``` This will return the proxy data in JSON format, which you can then use in your application. ## Common Errors - **401 Unauthorized:** The API key provided is either missing or invalid. Ensure that you are passing the correct API key either in the URL or in the request header. - **500 Internal Server Error:** This indicates a problem on our server side. Please contact support if you encounter this error. For more information on how to authenticate your requests, check out the [Authentication](/public-api/authentication/) page. Have feedback? Please let us know via our livechat which api endpoints you would like to see added to our API. --- ## Generate Proxies Source: https://docs.evomi.com/public-api/endpoints/generate/ Automatically generate proxy strings with specific targeting parameters and connection preferences. This can be helpful for tools that require an URL to retrieve proxies. ## Endpoint `GET https://api.evomi.com/public/generate` ## Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `product` | string | Yes | Proxy product type | | `countries` | string | No | ISO country codes seperated by comma (e.g. `US,DE,NL`) | | `city` | string | No | Target city name | | `region` | string | No | Target region | | `isp` | string | No | Target ISP name | | `session` | string | No | `sticky` (normal session) or `hard` (keep as long as possible) | | `amount` | number | No | Number of proxies to generate (1-100) | | `format` | string | No | Output format (`1`, `2`, or `3`) | | `prepend_protocol` | boolean | No | set to false to prevent protocol prepend. default: `true` | | `protocol` | string | No | `http` or `socks5` default: `http` | | `lifetime` | number | No | Session duration in minutes (1-1440, default 30) Unavailable for hard sessions | | `adblock` | boolean | No | Enable ad-blocking (Unavailable for rpc) | ## Products | Code | Product Name | Comment | |------|-----------------------| ------- | rp | Premium Residential | | | rpc | Core Residential | Adblock and ISP targetting unavailable | | sdc | Shared Datacenter | | | mp | Mobile | | | static-residential | Static Residential | No filter options work and you will receive all your proxies | ## Format Options 1. **Standard Format** `username:password@host:port` `user123:pass456@proxy.example.com:3000` 2. **Host-First Format** `host:port:username:password` `proxy.example.com:3000:user123:pass456` 3. **Standard Format with : instead of @** `username:password:host:port` `user123:pass456:proxy.example.com:3000` ## Example Proxy List via your Browser ```bash https://api.evomi.com/public/generate?product=rpc&countries=DE,US&format=2&protocol=socks5&amount=3&apikey=YOUR_API_KEY ``` ## Success Response The response is plain text with the generated proxies, one per line: ```md http://rp.evomi.com:1000:proxy_username:proxy_password-US,DE http://rp.evomi.com:1000:proxy_username:proxy_password-US,DE http://rp.evomi.com:1000:proxy_username:proxy_password-US,DE ``` ## Targeting Rules 1. You cant use cities or regions if you specify more than one country 2. Multiple countries can be passed as a comma-separated list and will be chosen at random 3. City/region targeting requires exact match from `/settings` endpoint 4. ISP targeting and adblocking is not available for `rpc` product ## Session Types | Type | Behavior | Lifetime | |------|----------|----------| | None | IP changes every request | - | | Sticky | Fixed IP for duration | Default 30 min, max 1440 min (24 h) | | Hard | Permanent IP | As long as IP is connected | ## Error Responses ```json { "error": "Invalid product for user", "success": false } ``` `400 Bad Request` - User doesn't have access to requested product, or no balance ```json { "error": "Exceeded maximum proxy count", "success": false } ``` `429 Too Many Requests` - Maximum is 500 Proxies per generation --- ## Session Rotation Source: https://docs.evomi.com/public-api/endpoints/rotate_session/ Force an IP address change for an existing proxy session while maintaining the same session identifier. ## Endpoint `GET https://api.evomi.com/public/rotate_session` ## Authentication Requires API key in either: - `x-apikey` header - `apikey` query parameter ## Parameters | Parameter | Required | Description | Valid Values | |-----------|----------|-------------|--------------| | `sessionid` | Yes | Current session identifier | Active session ID | | `product` | Yes | Proxy product type | `rpc`, `rp`, `sdc`, `mp` | ## Example Request ```bash curl -X GET "https://api.evomi.com/public/rotate_session?sessionid=AAAAA&product=rpc" \\ -H "x-apikey: YOUR_API_KEY" ``` Or, without headers: ```bash curl -X GET "https://api.evomi.com/public/rotate_session?sessionid=AAAAA&product=rpc&apikey=YOUR_API_KEY" ``` ## Success Response ```json { "success": true, "message": "Session reset successfully" } ``` ## Error Responses ```json { "error": "Session ID not provided", "success": false } ``` `400 Bad Request` - Missing required parameters ```json { "error": "Invalid product", "success": false } ``` `400 Bad Request` - Invalid product type specified ```json { "error": "Failed to reset session", "success": false } ``` `500 Internal Server Error` - Rotation failure ## Rotation Behavior 1. Immediate termination of existing connection 2. Release of current IP address 3. New IP assignment from same geographic pool 4. Session ID remains identical 5. Authentication credentials stay unchanged ## Usage Notes - Previous IP becomes immediately unavailable - Session locks to new IP after first connection ## Python Example ```python import requests def rotate_session(api_key, session_id, product_type): url = "https://api.evomi.com/public/rotate_session" headers = {"x-apikey": api_key} params = { "sessionid": session_id, "product": product_type } response = requests.get(url, headers=headers, params=params) return response.json() # Usage print(rotate_session("YOUR_KEY", "SESSION_123", "rpc")) ``` --- ## Get Scraper Data Source: https://docs.evomi.com/public-api/endpoints/scraper/ The **Get Scraper Data** endpoint returns information about your Scraper API access, including your scraper API key, remaining credits, concurrency limit, and the scraper endpoint URL. ## Endpoint `GET https://api.evomi.com/public/scraper` ## Authentication Requires API key in either: - `x-apikey` header - `apikey` query parameter ## Example Request ```bash curl -X GET "https://api.evomi.com/public/scraper" \ -H "x-apikey: YOUR_API_KEY" ``` Or via query parameter: ```bash curl -X GET "https://api.evomi.com/public/scraper?apikey=YOUR_API_KEY" ``` ## Response (User Has Access) ```json { "success": true, "has_access": true, "credits": 847.5, "concurrency": 5, "api_key": "abc123def456ghi", "endpoint_url": "https://scrape.evomi.com/api/v1" } ``` ## Response (User Has No Access) ```json { "success": true, "has_access": false, "credits": 0, "concurrency": 0, "api_key": null, "endpoint_url": "https://scrape.evomi.com/api/v1" } ``` ## Fields in the Response | Field | Type | Description | |-------|------|-------------| | `success` | boolean | Whether the request was processed successfully | | `has_access` | boolean | Whether the user has access to the Scraper API | | `credits` | number (decimal) | Remaining scraper credits | | `concurrency` | integer | Maximum number of concurrent scraper requests | | `api_key` | string \| null | Your Scraper API key, or `null` if you don't have access | | `endpoint_url` | string | The Scraper API endpoint URL | The `api_key` returned here is your **Scraper API key**, used to authenticate requests against the scraper endpoint. It is distinct from the Evomi public API key used to call this endpoint. ## Error Responses - **401 Unauthorized:** The API key provided is either missing or invalid. Ensure that you are passing the correct API key either in the URL or in the request header. - **500 Internal Server Error:** This indicates a problem on our server side. Please contact support if you encounter this error. For more information on how to authenticate your requests, check out the [Authentication](/public-api/authentication/) page. --- ## Targeting Options Source: https://docs.evomi.com/public-api/endpoints/settings/ This Endpoint returns available targeting parameters for different proxy types. Use these options to build precise proxy configuration requests. ## Endpoint `GET https://api.evomi.com/public/settings` ## Authentication Requires API key in either: - `x-apikey` header - `apikey` query parameter ## Example Request ```bash curl -X GET "https://api.evomi.com/public/settings" -H "x-apikey: YOUR_API_KEY" ``` ## Response Structure ```json { "success": true, "data": { "rp": { "countries": { ... }, "cities": { "data": [...] }, "regions": { "data": [...] }, "isp": { ... }, "continents": { "europe": { "name": "Europe", "countries": { ... } } } } } } ``` ## Proxy Types | Code | Proxy Type | |------|---------------------| | rp | Premium Residential | | rpc | Core Residential | | mp | Mobile | | dcp | Datacenter | ## Field Breakdown ### Countries - Two-letter ISO country codes mapped to full names - Example: `"IN": "India"` ### Cities - Nested structure with: - City names - Regional codes ### ISP Targeting ```json "ISP_NAME": { "value": "network_identifier", "countryCode": "ISO_COUNTRY_CODE" } ``` ### Continents - Pre-grouped country sets - Contains: - Continent name - Country name-to-code mapping - Example: ```json "europe": { "name": "Europe", "countries": { "Germany": "DE", "France": "FR" } } ``` ## Usage Guidelines 1. Use values exactly as returned in API responses 2. Combine multiple parameters for granular targeting: ```json { "country": "DE", "city": "berlin", } ``` 3. Missing parameters? Contact support for network expansions ## Error States ```json { "error": "Internal server error", "success": false } ``` - `401 Unauthorized`: Invalid/missing API key - `500 Internal Error`: Temporary data unavailability --- ## Frequently Asked Questions Source: https://docs.evomi.com/frequently-asked-questions/ Welcome to our FAQ section! Here you'll find answers to the most common questions our customers ask. These answers are designed to provide quick solutions and help you better understand our proxy services. ## Pricing and Plans ### What's the difference between Core Residential and Premium Residential proxies? **Core Residential Proxies:** - Cost-effective at $0.49/GB - High-quality for general tasks - Standard proxy pool size - **[NEW]** Expert Settings: TCP/IP fingerprinting, latency filter and more (increases bandwidth usage) **Premium Residential Proxies:** - For advanced needs, from $3.98/GB pay-as-you-go down to $2.20/GB at volume - Includes extras like a double-sized proxy pool, inbuilt ISP selection, adblocker, etc. Both provide HTTPS & SOCKS5 support, 99.9% uptime, and dedicated support for reliable and high-quality service. ### What are Static Residential Proxies and what categories do you offer? We offer Static Residential Proxies in three distinct categories, each with different pricing and availability: **Virgin Static Residential:** - Dedicated IPs that have never been sold as proxies before - Highest quality and reliability - Premium pricing due to their pristine status **Private Static Residential:** - Dedicated IPs that were previously sold but have expired from the old customer - Good quality at a more affordable price point - Still dedicated to you once purchased **Shared Static Residential:** - IPs shared with up to 3 customers maximum - Most cost-effective option - Shared usage while maintaining quality **Important purchasing details:** - Each category has a **minimum quantity requirement** - you cannot buy less than the minimum - When Private or other categories are **out of stock**, you need to wait for them to come back into stock - You can **resell them via our API** if required - After your first month, you can **renew for as long as you want** and keep the same IP addresses ### What is your IP replacement policy for Static Residential Proxies? We have a strict but fair replacement policy for Static Residential Proxies: **We provide replacements only if IPs don't meet our quality claims:** - We use [IPQualityScore.com](https://www.ipqualityscore.com/) to measure fraud scores - Replacements are provided if IPs don't meet our advertised quality standards **We do NOT provide replacements for:** - IPs that don't work for your specific use case - IPs that get banned after a few days or weeks of use - General compatibility issues with particular websites or services This policy ensures we maintain high-quality IP pools while setting clear expectations about proxy performance and longevity. ### Can I choose the location for Static Residential Proxies? **Location options are limited for Static Residential Proxies:** - Currently, Static Residential Proxies are only available in the **United States** - Within the USA, there is **no state or city selection** — you receive an IP and its location is not adjustable - You cannot request a specific geographic location before or after purchase **Need specific geo-targeting?** If your use case requires targeting specific countries, states, or cities, use our **Rotating Residential Proxies** instead. They offer: - Country-level targeting (worldwide) - State-level targeting - City-level targeting - Full flexibility to change locations as needed 📘 [View Geo Targeting Documentation](/proxy-instructions/residential-proxies/geo-targetting/) ### Why don't you offer unlimited bandwidth proxies? Unlimited residential plans with unlimited bandwidth and an unlimited IP pool do exist, but they come with a catch—those IPs are heavily overused, repeatedly resold, and often flagged, making them unreliable. Providers offering such plans are just trying to squeeze as much money as possible out of already-exploited IPs. We prioritize quality and reliability, which is why we don't operate that way. ### Do you provide enterprise pricing and features? Yes, we offer enterprise solutions with custom commitment contracts. For enterprise customers, we can provide: - **Special pricing** - Volume discounts based on your usage requirements - **Custom features** - Tailored functionality to meet your specific needs - **Service Level Agreements (SLA)** - Guaranteed uptime and performance metrics - **Non-Disclosure Agreements (NDA)** - Enterprise-grade confidentiality protection - **Dedicated support** - Priority technical assistance and account management **Ready to discuss enterprise options?** Contact our sales team who will work with you to create a custom solution: - 📧 **Email:** Reach out through your dashboard or our website - 💬 **Live Chat:** Connect with our sales team directly on our website Our sales team will assess your requirements and provide a tailored proposal that fits your business needs. --- ## Proxy Sessions ### What are the different types of sessions available? Let me give you a quick rundown of sessions. You can also check our [Proxy Sessions Documentation](/proxy-instructions/residential-proxies/proxy-sessions/). **_session:** Creates a sticky session - Example: `http://rp.evomi.com:1000:username:password_session-sgn34f3e` - Keeps the same IP for 30 minutes - You can call the session anytime in those 30 minutes and receive the same IP **_lifetime:** Extends/Shortens the duration of the sticky session - Example: `http://rp.evomi.com:1000:username:password_session-sgn34f3e_lifetime-120` - `_lifetime-120` sets session duration to 120 minutes (default is 30, maximum is 1440 which equals 24 hours) **_hardsession:** Ensures the IP does not change for as long as possible - Example: `http://rp.evomi.com:1000:username:password_hardsession-JS9nsq2n` - No automatic rotation; the IP remains as long as possible (e.g., until the device disconnects) **_lockedsession:** Locks traffic to one specific IP and never falls back - Example: `http://rp.evomi.com:1000:username:password_lockedsession-k8m2pQ1x` - If the bound IP goes offline, the request fails instead of assigning a new IP **No session parameter:** IP changes with every new request - Example: `http://rp.evomi.com:1000:username:password` - Each request receives a new IP ### Need on-demand IP rotation? If you prefer to control session/IP changes programmatically rather than relying on fixed time durations, you have two options: 1. **Change the Session ID** of your string by using random 6–10 character strings (e.g., `_session-abc1234`) to trigger a fresh IP 2. **Use our Rotation API** to instantly rotate the IP of an existing session without changing the session ID 📘 [View the Rotation API Documentation](/public-api/endpoints/) ## Payment and Billing ### My card was declined when trying to recharge. What should I do? If your card is declined when recharging your balance, first ensure that your card is funded, as insufficient funds can lead to a transaction being declined. The card from your last subscription payment is used for top-ups. If you encounter persistent issues and there are no errors from the bank, we can cancel your current subscription, and you can resubscribe with a different card. Your current balance will be added on top of the new balance. ### I entered my card details but haven't received anything yet. What's happening? Most likely your order is in a waiting state. Our payment provider PayPro Global is verifying your purchase. Please check your email 📧 inbox - you might need to reply to them. We cannot speed up this process, but you can contact PayPro Global 24/7: - **Toll-free:** +1-888-317-4868 - **International:** +1-647-977-7769 - **Email:** Reply to any email you received from them ### My order shows as void or cancelled. What does this mean? If your order is void or cancelled, it means that PayPro Global has rejected the transaction. This can be caused by several factors: - **Inaccurate billing information** - Incorrect billing address, name, or other details - **Using a VPN** during the payment process - **Payment method issues** - Card verification failures or security concerns **What you can do:** - Contact PayPro Global by replying to their email and asking for help with your specific case - Ensure all billing details exactly match your payment method - Try the payment process without using a VPN - **Alternative:** Cryptocurrency may be available through a separate checkout flow, subject to the applicable purchase requirements ### Can I get the Core Residential subscription ($0.49/GB for 100GB) with cryptocurrency? No, this is not possible. The Core Residential subscription with unlimited top-ups at $0.49/GB is only available through traditional payment methods (credit/debit cards). **Why subscriptions aren't available with crypto:** - The subscription model can only be so affordable because it's a recurring subscription - Cryptocurrency payments don't support automatic recurring billing - Without the subscription model, we cannot offer the same pricing structure **Your crypto options:** - **Pay-as-you-go plans** - Available with cryptocurrency at standard rates - **One-time purchases** - Buy credits in advance using crypto The subscription pricing is specifically designed around the predictable revenue model that only traditional payment methods can provide. ### How does bandwidth renewal work with subscriptions? When you have an active subscription, your bandwidth operates on a renewal cycle that resets at your renewal date: **At renewal date, your bandwidth will be set to either:** - **Your subscription amount** (e.g., 100GB) - if your subscription is set to renew and payment succeeds - **0GB** - if your subscription fails to renew or payment is declined **Important note about additional bandwidth:** Any extra bandwidth you purchase will also expire at your renewal date. **Example scenario:** - You have a 100GB subscription - You buy 200GB extra bandwidth 2 days before your renewal date - At renewal: Your bandwidth resets to 100GB (your subscription amount) - The unused portion of the extra 200GB bandwidth **expires and is lost** **Best practice:** Plan your additional bandwidth purchases carefully. If you're close to your renewal date, consider waiting until after renewal to purchase extra bandwidth, or use it quickly before the renewal date. This renewal system ensures clean billing cycles and prevents bandwidth accumulation across billing periods. ## Getting Started ### How can I try your proxies before purchasing? You can try our products by going to the [pricing page](https://evomi.com/pricing) and clicking the **Free Trial** button. **Important:** You will need to attach a valid payment method and provide accurate billing details to prevent any delays in processing your trial. --- ## API Keys and Authentication ### Where do I find my Scraper API key? **Important:** The Scraper API key is **NOT** the same as the API key in your profile! The Scraper API key can **only** be found on the Scraper API playground page: - 🔗 **[https://my.evomi.com/my/products/scraper-api-universal/playground](https://my.evomi.com/my/products/scraper-api-universal/playground)** **Requirements to access the Scraper API key:** - You must have an **active trial**, **active subscription**, or **pay-as-you-go credits** specifically for the Scraper API - The key will only appear on the playground page once you have one of the above If you don't see your API key on the playground page, you likely need to start a trial or purchase a Scraper API plan first. ### What's the difference between the Public API key and the Scraper API key? These are two completely different API keys with different purposes: **Public API Key (Profile API Key):** - Found in your profile/account settings - Used for the [Public API](/public-api/) endpoints - Allows you to: check your balance, view account info, manage settings programmatically - **NOT used for scraping** **Scraper API Key:** - Found **only** on the [Scraper API Playground](https://my.evomi.com/my/products/scraper-api-universal/playground) - Used for the [Scraper API](/scraping-products-instructions/scraper-api/) and [Scraping Browser](/scraping-products-instructions/scraping-browser/) - Allows you to: send scraping requests, use the scraping browser, render JavaScript, etc. - Requires an active Scraper API trial, subscription, or credits **Common mistake:** Many users look in their profile for the Scraper API key—this is incorrect. The profile API key is only for the Public API (account management), not for scraping. ## Need More Help? If you can't find the answer to your question here, please don't hesitate to reach out to our support team. We're here to help you get the most out of your proxy service. - 📧 **Email Support:** Available through your dashboard - 💬 **Live Chat:** Available on our website - 📚 **Documentation:** Browse our comprehensive guides in this documentation --- ## Integrations Source: https://docs.evomi.com/integrations/ Integrate Evomi's scraping capabilities directly into your applications, workflows, and automation tools. This section covers programmatic access to Evomi's features through multiple integration options. --- ## Integration Options | Integration | Description | | :--- | :--- | | [Agent API](/integrations/agent-requests/) | AI-powered scraping via REST API — describe tasks in natural language | | [n8n Integration](/integrations/n8n/) | Visual workflow automation — connect Evomi with 1000+ apps | | [Python Client](/integrations/python-client/) | Python SDK with full Evomi Api Coverage | | [JavaScript Client](/integrations/js-client/) | Promise-based Node.js client with full Evomi Api Coverage | | [Rust Client](/integrations/rust-client/) | Async Rust client with full Evomi Api Coverage | | [MCP Server](/integrations/mcp/) | Model Context Protocol server for AI assistants, like Claude, with Evomi Public & Scraper Api Coverage | --- ## Agent API The **Agent API** provides an AI-powered interface for web scraping tasks. Instead of configuring every scraping parameter manually, describe what you want in natural language and let the agent handle the details. ### Capabilities | Capability | Description | | :--- | :--- | | **Scrape URLs** | Extract data from any webpage with AI-generated extraction schemas | | **Generate Configs** | Create reusable scraper configurations from natural language descriptions | | **Discover URLs** | Find matching URLs on a domain for products, articles, and more | | **Manage Schedules** | Create, list, and control scheduled scraping jobs | | **Account Management** | Check credit balance and resource usage | --- ## Official Client Libraries Evomi provides official client libraries for popular programming languages. These clients provide type-safe, idiomatic interfaces to the full Evomi API. ### Python Client The **Evomi Python Client** provides both synchronous and asynchronous interfaces with full type hints. ```python from evomi_client import EvomiClient client = EvomiClient(api_key="your-api-key") result = await client.scrape("https://example.com") ``` - **Install**: `pip install evomi-client` - **Features**: Async/sync support, type hints, proxy string builder - **Package**: [pypi.org/project/evomi-client](https://pypi.org/project/evomi-client/) ### JavaScript Client The **Evomi JavaScript Client** is a promise-based client for Node.js with native fetch support. ```javascript import { EvomiClient } from 'evomi-client'; const client = new EvomiClient({ apiKey: 'your-api-key' }); const result = await client.scrape('https://example.com'); ``` - **Install**: `npm install evomi-client` - **Features**: Promise-based, zero dependencies, Node.js >= 18 - **Package**: [npmjs.com/package/evomi-client](https://www.npmjs.com/package/evomi-client) ### Rust Client The **Evomi Rust Client** provides an async client with full type safety and idiomatic Rust patterns. ```rust use evomi_client::{Client, ScrapeOptions}; let client = Client::new("your-api-key", None, None)?; let result = client.scrape("https://example.com", Some(ScrapeOptions::default())).await?; println!("{:?}", result); ``` - **Install**: `cargo add evomi-client` - **Features**: Async/await, full type safety, idiomatic Rust - **Package**: [crates.io/crates/evomi-client](https://crates.io/crates/evomi-client) --- ## n8n Integration **n8n** is a popular workflow automation platform that allows you to connect Evomi with over 1,000 apps and services. The Evomi node is **verified by n8n**, meaning it's solid, safe, and ready to use in your automation workflows. ### Capabilities | Capability | Description | | :--- | :--- | | **Scrape** | Scrape content from a web page | | **Workflow Automation** | Chain scraping with other apps and services | | **Scheduling** | Run scraping jobs on automated schedules | --- ## Getting Started To use any of Evomi's integrations, you'll need: 1. **An Evomi Account** — [Sign up](https://evomi.com) if you don't have one 2. **API Key** — Find your API key in the dashboard 3. **Credits** — Operations consume credits based on complexity Agent API requests start at 30 credits per AI step. Tool operations (scrapes, URL discovery) may add additional costs. n8n usage follows the same credit structure. --- ## Agent Requests Source: https://docs.evomi.com/integrations/agent-requests/ The **Agent Request API** provides a stateless, AI-powered interface for web scraping tasks. Describe what you want in natural language, and the agent autonomously executes the necessary operations. --- ## Endpoint Reference | Endpoint | Method | Description | | :--- | :--- | :--- | | `/agent/request` | `POST` | Execute a stateless AI agent request | **Authentication**: Include your API key in the `x-api-key` header. **Base URL**: `https://scrape.evomi.com/api/v1` ## Your First Request The simplest way to use the Agent API is to describe what you want: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Scrape https://evomi.com and extract the page title and navigation links" }' ``` **Response:** ```json { "success": true, "response": "I scraped evomi.com and extracted the page title 'Evomi - Premium Proxies' and found 6 navigation links including Home, Pricing, Dashboard, and Documentation.", "actions_taken": [ { "tool": "generate_schema", "params": {"url": "https://evomi.com", "extraction_prompt": "extract page title and navigation links"}, "result": {"success": true, "scheme_id": "sch_abc123"} }, { "tool": "scrape_url", "params": {"url": "https://evomi.com", "scheme_id": "sch_abc123"}, "result": {"success": true, "status_code": 200} } ], "credits_used": 90, "credits_remaining": 910 } ``` --- ## How It Works ``` Your Message → AI Analysis → Tool Execution → Results Summary ``` 1. **Send a Request** — Describe your scraping task in natural language 2. **AI Processing** — The agent determines the best approach and tools to use 3. **Tool Execution** — Actions are performed (scraping, schema generation, etc.) 4. **Response** — Receive a clear summary with all results Each request is **stateless**—no conversation history is maintained between requests. This makes it ideal for integrations, automation, and one-off tasks. --- ## Available Actions The agent can perform these actions autonomously: | Action | Description | | :--- | :--- | | `scrape_url` | Scrape a URL and extract data | | `generate_schema` | AI-generate an extraction schema for a URL | | `generate_config` | Create a saved scraper configuration | | `discover_urls` | Find URLs on a domain matching a pattern | | `search_domains` | Find domains by searching the web | | `list_configs` | List your saved scraper configs | | `get_config` | Retrieve details of a specific config | | `create_schedule` | Set up a recurring scrape job | | `list_schedules` | List your scheduled jobs | | `toggle_schedule` | Pause or activate a schedule | | `get_account_info` | Check your credit balance | --- ## Request Parameters | Parameter | Type | Required | Description | | :--- | :--- | :--- | :--- | | `message` | string | Yes | Natural language description of your task (3-10,000 characters) | --- ## Examples ### Quick Scrape with Auto-Extraction Let the AI figure out what to extract: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Scrape https://example.com/product/123 and extract all product information" }' ``` ### Generate a Saved Config Create a reusable configuration: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Create a config for scraping Amazon product pages - extract title, price, rating, and review count" }' ``` **Response:** ```json { "success": true, "response": "I've created a config named 'Amazon Products' with ID cfg_xyz789. It extracts product title, price, rating, and review count from Amazon product pages.", "actions_taken": [ { "tool": "generate_config", "params": { "name": "Amazon Products", "prompt": "Scrape Amazon product pages - extract title, price, rating, and review count" }, "result": { "success": true, "config_id": "cfg_xyz789" } } ], "credits_used": 60, "credits_remaining": 940 } ``` ### Multi-Task Request Ask for multiple things in one request: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Show me my credit balance, list all my configs, and list all my schedules" }' ``` ### Discover URLs on a Domain Find specific page types: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Find all blog post URLs on evomi.com" }' ``` ### Search for Domains Find websites matching a search query: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Find e-commerce websites in Germany" }' ``` ### Run an Existing Config Execute a saved configuration: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Run my config named \"Product Scraper\" and show me the results" }' ``` ### Create a Schedule Set up automated recurring scraping: ```bash curl -X POST "https://scrape.evomi.com/api/v1/agent/request" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "Schedule my \"Product Scraper\" config to run daily at 9 AM UTC" }' ``` --- ## Response Format ### Success Response ```json { "success": true, "response": "A human-readable summary of what was accomplished", "actions_taken": [ { "tool": "tool_name", "params": {...}, "result": {...} } ], "credits_used": 90, "credits_remaining": 910 } ``` ### Response Fields | Field | Type | Description | | :--- | :--- | :--- | | `success` | boolean | Whether the request completed successfully | | `response` | string | Human-readable summary of results | | `actions_taken` | array | List of tools executed and their results | | `credits_used` | number | Total credits consumed by this request | | `credits_remaining` | number | Your remaining credit balance | --- ## Credit Usage ### How Credits Are Calculated | Component | Cost | | :--- | :--- | | AI step | 30 credits each | | Scraping | Base scrape cost (varies by mode/proxy) | | Schema generation | Includes scrape cost + AI processing | | URL discovery | Includes crawl cost | ### Credit Reserve Each request reserves up to **300 credits** (10 AI steps × 30 credits). Unused credits are automatically refunded after the request completes. ### Monitoring Credits Every response includes credit information: ```json { "credits_used": 90, "credits_remaining": 910 } ``` Response headers also include: ``` X-Credits-Used: 90 X-Credits-Remaining: 910 ``` --- ## Error Responses ### Insufficient Credits (402) ```json { "success": false, "error": { "code": "INSUFFICIENT_CREDITS", "message": "Insufficient credits: operation costs 300 credits but only 50 available" }, "credits_remaining": 50 } ``` ### Invalid Request (400) ```json { "success": false, "error": "'message' is required" } ``` ### Message Too Short/Long (400) ```json { "success": false, "error": "Message is too short" } ``` --- ## Best Practices ### Be Specific The more specific your request, the better the results: ```bash # Good — clear intent and target "Scrape https://example.com/products and extract product name, price, and availability" # Less clear "Get products from example.com" ``` ### Use for One-Off Tasks The Agent API is ideal for: - Quick data extraction tasks - Automated workflows - Testing and prototyping - Integration with other tools ### Check Actions Taken Review the `actions_taken` array to understand exactly what the agent did: ```json { "actions_taken": [ {"tool": "generate_schema", "params": {...}, "result": {...}}, {"tool": "scrape_url", "params": {...}, "result": {...}} ] } ``` ### Handle Rate Limits If you receive a 429 response, wait before retrying: ```json { "success": false, "error": "Rate limit exceeded. Please wait before making more requests." } ``` --- ## Response Codes | Status | Meaning | Action | | :--- | :--- | :--- | | 200 | Success | Process the response | | 400 | Bad request | Check your message format | | 401 | Unauthorized | Verify your API key | | 402 | Insufficient credits | Add credits to your account | | 429 | Rate limit exceeded | Wait and retry | | 500 | Server error | Retry or contact support | --- ## What's Next? - **[Scraper Configs](/scraping-products-instructions/scraper-api/scraper-config/)** — Learn about saved configurations - **[Schedules](/scraping-products-instructions/scraper-api/schedules/)** — Automate recurring scraping - **[Usage Examples](/scraping-products-instructions/scraper-api/usage-examples/)** — See code examples in multiple languages --- ## JS Client Source: https://docs.evomi.com/integrations/js-client/ The **Evomi JavaScript Client** provides a production-ready, promise-based interface to Evomi's API. It is designed to work seamlessly in Node.js environments with native fetch support. --- ## Installation Install the package from npm: ```bash npm install evomi-client ``` --- ## Quick Start ```javascript import { EvomiClient } from 'evomi-client'; const client = new EvomiClient({ apiKey: 'your-api-key' }); // Scrape a webpage const result = await client.scrape('https://example.com'); console.log(result.content); ``` --- ## Authentication Set your API key via environment variable: ```bash export EVOMI_API_KEY="your-api-key" ``` Or pass it directly: ```javascript const client = new EvomiClient({ apiKey: 'your-api-key' }); ``` Custom base URL (for testing): ```javascript const client = new EvomiClient({ apiKey: 'your-api-key', baseUrl: 'https://custom.evomi.com' }); ``` --- ## Scraping Operations ### scrape(url, options) Scrape a single URL with configurable options. ```javascript const result = await client.scrape('https://example.com', { mode: 'auto', // 'request', 'browser', or 'auto' output: 'markdown', // 'html', 'markdown', 'screenshot', 'pdf' device: 'windows', // 'windows', 'macos', 'android' proxyType: 'residential', proxyCountry: 'US', proxySessionId: 'abc123', waitUntil: 'domcontentloaded', aiEnhance: true, aiPrompt: 'Extract product data', aiSource: 'markdown', jsInstructions: [{ click: '.load-more' }], executeJs: 'window.scrollTo(0, document.body.scrollHeight)', waitSeconds: 2, screenshot: false, pdf: false, excludedTags: ['nav', 'footer'], excludedSelectors: ['.ads'], blockResources: ['image', 'stylesheet'], additionalHeaders: { 'X-Custom': 'value' }, captureHeaders: true, networkCapture: [{ url_pattern: '/api/.*' }], asyncMode: false, configId: 'cfg_abc123', schemeId: 'sch_abc123', extractScheme: [{ label: 'title', type: 'content', selector: 'h1' }], storageId: 'stor_abc123', useDefaultStorage: false }); ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `url` | string | required | URL to scrape | | `mode` | string | `'auto'` | Scraping mode: `'request'`, `'browser'`, `'auto'` | | `output` | string | `'markdown'` | Output format: `'html'`, `'markdown'`, `'screenshot'`, `'pdf'` | | `device` | string | `'windows'` | Device type: `'windows'`, `'macos'`, `'android'` | | `proxyType` | string | `'residential'` | Proxy type: `'datacenter'`, `'residential'` | | `proxyCountry` | string | `'US'` | Two-letter country code | | `proxySessionId` | string | — | Proxy session ID (6-8 chars) | | `waitUntil` | string | `'domcontentloaded'` | Wait condition | | `aiEnhance` | boolean | `false` | Enable AI extraction | | `aiPrompt` | string | — | Prompt for AI extraction | | `aiSource` | string | — | AI source: `'markdown'`, `'screenshot'` | | `aiForceJson` | boolean | `true` | Force AI response to valid JSON | | `jsInstructions` | array | — | JS actions: click, wait, fill, wait_for | | `executeJs` | string | — | Raw JavaScript to execute | | `waitSeconds` | number | `0` | Seconds to wait after page load | | `screenshot` | boolean | `false` | Capture screenshot | | `pdf` | boolean | `false` | Capture PDF | | `excludedTags` | array | — | HTML tags to remove | | `excludedSelectors` | array | — | CSS selectors to remove | | `blockResources` | array | — | Resource types to block | | `additionalHeaders` | object | — | Extra HTTP headers | | `captureHeaders` | boolean | `false` | Capture response headers | | `networkCapture` | array | — | Network capture filters | | `asyncMode` | boolean | `false` | Return immediately with task ID | | `configId` | string | — | Saved config ID | | `schemeId` | string | — | Saved extraction schema ID | | `extractScheme` | array | — | Inline extraction schema | | `storageId` | string | — | Storage config ID | | `useDefaultStorage` | boolean | `false` | Use default storage | | `delivery` | string | `'json'` | Response format: `'raw'` or `'json'` | | `includeContent` | boolean | `true` | Include content in response | | `webhook` | `WebhookConfig` | — | Webhook configuration | ### crawl(domain, options) Crawl a website to discover and scrape multiple pages. ```javascript const result = await client.crawl('example.com', { maxUrls: 100, depth: 2, urlPattern: '/blog/.*', scraperConfig: { mode: 'browser', output: 'markdown' }, asyncMode: false, }); ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `domain` | string | required | Domain to crawl | | `maxUrls` | number | `100` | Maximum URLs to crawl | | `depth` | number | `2` | Crawl depth | | `urlPattern` | string | — | Regex pattern to filter URLs | | `scraperConfig` | object | — | Config for scraping each page | | `asyncMode` | boolean | `false` | Return immediately with task ID | ### mapWebsite(domain, options) Discover URLs from a website via sitemaps, CommonCrawl, or crawling. ```javascript const result = await client.mapWebsite('example.com', { sources: ['sitemap', 'commoncrawl'], maxUrls: 500, urlPattern: '/products/.*', checkIfLive: false, depth: 1, asyncMode: false, }); ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `domain` | string | required | Domain to map | | `sources` | array | `['sitemap', 'commoncrawl']` | Sources: `'sitemap'`, `'commoncrawl'`, `'crawl'` | | `maxUrls` | number | `500` | Maximum URLs to discover | | `urlPattern` | string | — | Regex pattern to filter URLs | | `checkIfLive` | boolean | `false` | Check if URLs are live | | `depth` | number | `1` | Crawl depth if using crawl source | | `asyncMode` | boolean | `false` | Return immediately with task ID | ### searchDomains(query, options) Find domains by searching the web. ```javascript // Single query const result = await client.searchDomains('e-commerce platforms', { maxUrls: 20, region: 'us-en', }); // Multiple queries (up to 10) const result = await client.searchDomains( ['web scraping tools', 'data extraction services'], { maxUrls: 20, region: 'us-en' } ); ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `query` | string or array | required | Search query or list of up to 10 queries | | `maxUrls` | number | `20` | Max domains per query (max: 100) | | `region` | string | `'us-en'` | Region for results | ### agentRequest(message) Send a natural language request to the AI agent. ```javascript const result = await client.agentRequest( 'Scrape example.com and extract all product prices' ); ``` ### getTaskStatus(taskId, taskType) Check the status of an async task. ```javascript const result = await client.getTaskStatus('abc123', 'scrape'); // taskType: 'scrape' | 'crawl' | 'map' | 'config_generate' | 'schema' ``` --- ## Config Management Save and reuse scrape configurations. ### listConfigs(options) ```javascript const configs = await client.listConfigs({ page: 1, perPage: 20, sortBy: 'created_at', sortOrder: 'desc', }); ``` ### createConfig(name, config) ```javascript const config = await client.createConfig('Product Scraper', { mode: 'browser', output: 'markdown', }); ``` ### getConfig(configId) ```javascript const config = await client.getConfig('cfg_abc123'); ``` ### updateConfig(configId, options) ```javascript const config = await client.updateConfig('cfg_abc123', { name: 'New Name', config: { mode: 'request' }, }); ``` ### deleteConfig(configId) ```javascript await client.deleteConfig('cfg_abc123'); ``` ### generateConfig(name, prompt) Generate a scrape config from natural language using AI. ```javascript const config = await client.generateConfig( 'Amazon Scraper', 'Scrape product title and price from Amazon product pages' ); ``` --- ## Schema Management Define reusable structured data extraction schemas. ### listSchemas(options) ```javascript const schemas = await client.listSchemas({ page: 1, perPage: 20, sortBy: 'created_at', sortOrder: 'desc', }); ``` ### createSchema(name, config, options) ```javascript const schema = await client.createSchema( 'Product Schema', { url: 'https://example.com/product', extract_scheme: [ { label: 'title', type: 'content', selector: 'h1' }, { label: 'price', type: 'content', selector: '.price' }, ], }, { test: true, fix: false } ); ``` ### getSchema(schemeId) ```javascript const schema = await client.getSchema('sch_abc123'); ``` ### updateSchema(schemeId, name, config, options) ```javascript const schema = await client.updateSchema( 'sch_abc123', 'Updated Schema', { url: '...', extract_scheme: [...] }, { test: true } ); ``` ### deleteSchema(schemeId) ```javascript await client.deleteSchema('sch_abc123'); ``` ### getSchemaStatus(schemeId) ```javascript const status = await client.getSchemaStatus('sch_abc123'); ``` --- ## Schedule Management Run scrape configs on a recurring schedule. ### listSchedules(options) ```javascript const schedules = await client.listSchedules({ page: 1, perPage: 20, activeOnly: false, }); ``` ### createSchedule(name, configId, intervalMinutes, options) ```javascript const schedule = await client.createSchedule( 'Daily Price Check', 'cfg_abc123', 1440, // Daily (in minutes) { startTime: '09:00', stopOnError: true } ); ``` ### getSchedule(scheduleId) ```javascript const schedule = await client.getSchedule('sched_abc123'); ``` ### updateSchedule(scheduleId, options) ```javascript const schedule = await client.updateSchedule('sched_abc123', { name: 'New Name', intervalMinutes: 720, }); ``` ### deleteSchedule(scheduleId) ```javascript await client.deleteSchedule('sched_abc123'); ``` ### toggleSchedule(scheduleId) ```javascript await client.toggleSchedule('sched_abc123'); ``` ### listScheduleRuns(scheduleId, options) ```javascript const runs = await client.listScheduleRuns('sched_abc123', { page: 1, perPage: 20, }); ``` --- ## Storage Management Connect cloud storage to automatically save scrape results. ### listStorageConfigs() ```javascript const configs = await client.listStorageConfigs(); ``` ### createStorageConfig(name, storageType, config, options) ```javascript // S3-compatible storage const storage = await client.createStorageConfig( 'My S3', 's3_compatible', { bucket: 'my-bucket', region: 'us-east-1', access_key: '...', secret_key: '...', }, { setAsDefault: true } ); // Google Cloud Storage const storage = await client.createStorageConfig( 'My GCS', 'gcs', { bucket: 'my-bucket', credentials_json: '...' } ); // Azure Blob Storage const storage = await client.createStorageConfig( 'My Azure', 'azure_blob', { container: 'my-container', connection_string: '...' } ); ``` ### updateStorageConfig(storageId, options) ```javascript const storage = await client.updateStorageConfig('stor_abc123', { name: 'Renamed Storage', setAsDefault: true, }); ``` ### deleteStorageConfig(storageId) ```javascript await client.deleteStorageConfig('stor_abc123'); ``` --- ## Public API Access proxy credentials and related data. ### getPublicApi() Access the Evomi Public API to get proxy credentials and related data. ```javascript const data = await client.getPublicApi(); // Returns proxy credentials and product information ``` ### getProxyData() Get detailed information about your proxy products. ```javascript const data = await client.getProxyData(); // Returns: { products: { rp: {...}, sdc: {...}, mp: {...} }, ... } ``` ### getTargetingOptions() Get available targeting parameters for different proxy types. ```javascript const options = await client.getTargetingOptions(); ``` ### getScraperData() Get information about your Scraper API access. ```javascript const data = await client.getScraperData(); ``` ### getBrowserData() Get information about your Browser API access. ```javascript const data = await client.getBrowserData(); ``` ### rotateSession(sessionId, product) Force an IP address change for an existing proxy session. ```javascript const result = await client.rotateSession('abc12345', 'rp'); // product: 'rpc', 'rp', 'sdc', 'mp' ``` ### generateProxies(options) Generate proxy strings with specific targeting parameters. ```javascript const proxies = await client.generateProxies({ product: 'rp', countries: 'US,GB,DE', city: 'New York', session: 'sticky', amount: 10, protocol: 'http', lifetime: 30, adblock: true, }); // Returns plain text, one proxy per line ``` --- ## Account Info ### getAccountInfo() ```javascript const info = await client.getAccountInfo(); console.log(info.credits); ``` --- ## Webhooks Webhooks allow you to receive notifications when scraping operations complete, fail, or start. Supports Discord, Slack, and custom endpoints. ### WebhookConfig ```javascript import { WebhookConfig } from 'evomi-client'; // Discord webhook const webhook = new WebhookConfig({ url: 'https://discord.com/api/webhooks/...', webhookType: 'discord', events: ['completed', 'failed'], }); // Custom webhook with HMAC signature const webhook = new WebhookConfig({ url: 'https://your-server.com/webhook', webhookType: 'custom', events: ['completed'], secret: 'your-secret-key', }); ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `url` | string | required | Your webhook endpoint URL | | `webhookType` | string | `'custom'` | Type: `'discord'`, `'slack'`, or `'custom'` | | `events` | string[] | `['completed']` | Events to subscribe to | | `secret` | string | — | Secret key for HMAC signature (custom only) | ### Using Webhooks with Scrape ```javascript import { EvomiClient, WebhookConfig } from 'evomi-client'; const client = new EvomiClient({ apiKey: 'your-api-key' }); const webhook = new WebhookConfig({ url: 'https://discord.com/api/webhooks/...', webhookType: 'discord', events: ['completed', 'failed'], }); const result = await client.scrape('https://example.com', { mode: 'browser', webhook, }); ``` ### Using Webhooks with Crawl/Map/Search ```javascript // With crawl const result = await client.crawl('example.com', { maxUrls: 100, webhook, }); // With mapWebsite const result = await client.mapWebsite('example.com', { webhook, }); // With searchDomains const result = await client.searchDomains('e-commerce platforms', { webhook, }); ``` ### Using Webhooks with Schedules ```javascript const schedule = await client.createSchedule( 'Daily Price Check', 'cfg_abc123', 1440, { startTime: '09:00', stopOnError: true, webhook } ); ``` --- ## Proxy String Builder Evomi provides a proxy network you can use with any HTTP client. Build proxy strings for `fetch`, `axios`, or any other library: ### Automatic Proxy Configuration ```javascript import { EvomiClient, ProxyType, ProxyProtocol } from 'evomi-client'; const client = new EvomiClient({ apiKey: 'your-api-key' }); // Build a proxy string for US residential proxy const proxyString = await client.buildProxyString({ proxyType: ProxyType.RESIDENTIAL, country: 'US', session: 'abc12345' }); console.log(proxyString); // Output: http://user:pass_country-US_session-abc12345@rp.evomi.com:1000 ``` ### Manual Proxy Configuration ```javascript import { ProxyConfig, ProxyType, ProxyProtocol } from 'evomi-client'; const config = new ProxyConfig({ proxyType: ProxyType.RESIDENTIAL, protocol: ProxyProtocol.HTTP, country: 'US', city: 'New York', username: 'your-username', password: 'your-password' }); const proxyString = config.buildProxyString(); ``` ### Proxy Configuration Options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `proxyType` | string | `'residential'` | Type: `'residential'`, `'datacenter'`, `'mobile'` | | `protocol` | string | `'http'` | Protocol: `'http'`, `'https'`, `'socks5'` | | `country` | string | — | Two-letter ISO country code (e.g., "US", "DE") | | `city` | string | — | City name (spaces replaced with dots) | | `region` | string | — | State/region name | | `continent` | string | — | Continent: africa, asia, europe, north.america, oceania, south.america | | `isp` | string | — | ISP shortcode (e.g., "att", "comcast") | | `session` | string | — | Sticky session ID (6-10 alphanumeric chars) | | `hardsession` | string | — | Hard session ID (6-10 alphanumeric chars) | | `lifetime` | number | — | Session duration in minutes (default 30, max 1440) | | `mode` | string | — | Residential mode: `'speed'`, `'quality'` | ### Expert Settings | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `latency` | number | — | Max latency in ms | | `fraudscore` | number | — | Max fraud score | | `device` | string | — | Device type: "windows", "unix", "apple" | | `activesince` | number | — | Min connection minutes | | `asn` | string | — | ASN filter | | `zip` | string | — | Zipcode targeting | | `http3` | boolean | `false` | Enable HTTP3/QUIC | | `udp` | boolean | `false` | UDP support (Enterprise only) | | `extended` | boolean | `false` | Extended pool (cannot combine with other expert filters) | ### Proxy Endpoints & Ports | Type | HTTP | HTTPS | SOCKS5 | |------|------|-------|--------| | Residential | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | Datacenter | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | | Mobile | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ### Proxy String Format ``` {protocol}://{username}:{password}_{params}@{endpoint}:{port} ``` Example: `http://user:pass_country-US_session-abc12345@rp.evomi.com:1000` ### Proxy Types | Type | Endpoint | Use Case | |------|----------|----------| | Residential | `rp.evomi.com:1000` | Browser-compatible workflows and broad site coverage | | Datacenter | `dcp.evomi.com:2000` | Fast, high-volume requests | | Mobile | `mp.evomi.com:3000` | Highest trust, mobile-specific targets | --- ## Error Handling ```javascript try { const result = await client.scrape('https://example.com'); } catch (error) { console.error('Scraping failed:', error.message); } ``` --- ## Credits & Pricing All operations consume credits: | Operation | Cost | | :--- | :--- | | Base request | 1 credit | | Browser mode | 5x multiplier | | Residential proxy | 2x multiplier | | AI enhancement | +30 credits | | Screenshot/PDF | +1 credit each | Credit usage is returned in the result: ```javascript console.log(result._credits_used); console.log(result._credits_remaining); ``` --- ## Requirements - Node.js >= 18 (for native `fetch`) --- ## Resources | Resource | Link | | :--- | :--- | | npm Package | [npmjs.com/package/evomi-client](https://www.npmjs.com/package/evomi-client) | | Evomi Website | [evomi.com](https://evomi.com) | | API Documentation | [docs.evomi.com](https://docs.evomi.com/overview/) | --- ## Benefits - **Promise-based** — Uses modern async/await for clean flow - **Full API Coverage** — All Evomi endpoints supported - **Node.js Ready** — Built with native fetch support --- ## MCP Server Source: https://docs.evomi.com/integrations/mcp/ The **Evomi MCP Server** exposes Evomi's Public API and Scraper API over the Model Context Protocol. AI assistants like Claude can scrape and crawl websites, and manage the proxies on your account — credentials, geo targeting, sticky sessions, usage and balances — from the same conversation. 39 tools by default, 40 with `EVOMI_ENABLE_SPENDING=1`. --- ## Installation ```bash pip install evomi-mcp ``` Needs Python 3.10 or newer. The server runs on both major versions of the `mcp` SDK (`>=1.8.0,<3`), so it installs into an environment already pinned to `mcp` 1.x as well as a fresh one that resolves 2.x. `evomi-mcp --version` prints the installed version and `evomi-mcp --help` lists the environment variables it reads. With no arguments it speaks MCP over stdio, which is how an MCP client starts it. --- ## Configuration One credential is enough: ```bash export EVOMI_PUBLIC_API_KEY="your-public-api-key" ``` Take it from [Settings > API](https://my.evomi.com/settings/api) for a personal account, or Settings > Team for a team one. It authenticates the proxy and account tools directly. The scraping tools authenticate with the account's scraper key, which the server reads from the Public API on the first scraping call and holds in memory for the process. That key is never written to disk and never appears in tool output. | Variable | Key | Used by | | :--- | :--- | :--- | | `EVOMI_PUBLIC_API_KEY` | Public API key, from [Settings > API](https://my.evomi.com/settings/api) | Proxy credentials, usage, targeting, sessions, and indirectly everything else | | `EVOMI_SCRAPER_API_KEY` | Scraper API key, from the same page | Optional. Pins a specific key for scraping, crawling, configs, schemas, storage and schedules, and skips the lookup | | `EVOMI_API_KEY` | Fallback for either of the above | Both, wherever the specific variable is unset | Where a specific variable is set, it wins over `EVOMI_API_KEY`. The API endpoints can be pointed elsewhere, and default to: ```bash export EVOMI_PUBLIC_BASE_URL="https://api.evomi.com" export EVOMI_BASE_URL="https://scrape.evomi.com" ``` ### Safety Settings Both of these change which tools exist or what they return, so decide on them when you set the server up rather than later: | Variable | Effect | | :--- | :--- | | `EVOMI_HIDE_PROXY_PASSWORDS=1` | Masks every proxy password and service API key. The mask is a fixed string that carries none of the secret's characters | | `EVOMI_ENABLE_SPENDING=1` | Registers `order_browser_profile`, the one tool that spends account balance. Without it the tool is not registered at all, so the connected model never sees it | With `EVOMI_HIDE_PROXY_PASSWORDS=1` set: | Tool | Behaviour | | :--- | :--- | | `get_proxy_credentials` | **Masked.** Username, gateway, ports and balance are unchanged | | `get_api_access` | **Masked**, even when `include_api_key: true` is passed explicitly | | `build_proxy_connection_string` | **Refuses**, naming the variable | | `generate_proxy_list` | **Refuses** before the API is called, so the bulk credentials are never minted | | Every other tool | Unchanged. None of them returns a credential | The two that refuse do so because their entire output is the credential. The refusal points at `list_proxy_products` and `list_proxy_targeting_options`, which give the gateway hostname, ports and targeting values with no credential in them. ### Credentials in Tool Output `get_proxy_credentials`, `build_proxy_connection_string` and `generate_proxy_list` return live proxy passwords, and `get_api_access` returns a service API key when asked with `include_api_key`. Those values become part of the conversation with whatever model is connected, and part of whatever that client logs. Every other tool returns balances, endpoints, usage and targeting data only. Three limits apply either way. The four tools above carry an instruction in their descriptions not to repeat the value back unless it was asked for directly. `build_proxy_connection_string` and `generate_proxy_list` return at most 50 entries per call, and refuse a larger request with the limit rather than clamping it. And the `curl_example` that comes with a connection string has its password masked, since it is the field most likely to be pasted into a terminal, a ticket or a screenshot — pass `runnable_curl_example: true` for the form that can be run. The connection strings themselves carry the real password either way. API keys are read from the environment and sent as headers. They are scrubbed from tool output and error messages. --- ## Usage with Claude Desktop Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "evomi": { "command": "evomi-mcp", "env": { "EVOMI_PUBLIC_API_KEY": "your-public-api-key" } } } } ``` Or if installed from source: ```json { "mcpServers": { "evomi": { "command": "python", "args": ["-m", "evomi_mcp.server"], "env": { "EVOMI_PUBLIC_API_KEY": "your-public-api-key" } } } } ``` --- ## Available Tools (39 Total) Thirty-nine tools are registered by default. `order_browser_profile` brings that to forty, and only when `EVOMI_ENABLE_SPENDING=1` is set. ### Proxy & Account (9 tools, Evomi Public API, + 1 opt-in) | Tool | Description | Returns credentials | | :--- | :--- | :--- | | `list_proxy_products` | Products on the account with endpoints, ports, balance and username | No | | `get_proxy_credentials` | Proxy username and password for one product | **Yes** | | `build_proxy_connection_string` | Connection strings with geo targeting, sessions and expert filters, plus a curl check | **Yes** | | `generate_proxy_list` | Bulk proxy list (up to 50 per call) from the Public API generator | **Yes** | | `get_proxy_usage` | Bandwidth used over 24h / 3d / 7d, total and per bucket | No | | `list_proxy_targeting_options` | Searchable countries, regions, cities, ISPs and continents per product, each with the `id` the gateway accepts and its display name | No | | `rotate_proxy_session` | **Mutating** — force a sticky session onto a new exit IP | No | | `get_api_access` | Scraper and Browser access, credits, concurrency and endpoints (keys masked by default) | Only on request | | `list_browser_profiles` | Saved browser fingerprint profiles | No | | `order_browser_profile` | **Mutating, spends money** — orders a browser fingerprint profile, charged against the account's data balance. Only registered when `EVOMI_ENABLE_SPENDING=1` | No | `get_proxy_usage` covers the four products billed by bandwidth: Premium Residential, Core Residential, Mobile and Datacenter. Static Residential is billed per rented IP instead, so `list_proxy_products` is where its IPs and their expiry dates are. ### Scraping Operations (6 tools) | Tool | Description | | :--- | :--- | | `scrape_url` | Scrape a single URL with configurable options | | `crawl_website` | Crawl a website to discover and scrape multiple pages | | `map_website` | Discover URLs from a website | | `search_domains` | Find domains by searching the web | | `agent_request` | AI-powered conversational scraping assistant | | `get_task_status` | Check the status of an async task | ### Config Management (6 tools) | Tool | Description | | :--- | :--- | | `list_configs` | List all saved scrape configurations | | `create_config` | Create a new scrape configuration | | `get_config` | Get a saved scrape configuration by ID | | `update_config` | Update an existing scrape configuration | | `delete_config` | Delete a scrape configuration | | `generate_config` | Generate a scrape config from natural language using AI | ### Schema Management (6 tools) | Tool | Description | | :--- | :--- | | `list_schemas` | List all saved extraction schemas | | `create_schema` | Create a new extraction schema | | `get_schema` | Get a saved extraction schema by ID | | `update_schema` | Update an existing extraction schema | | `delete_schema` | Delete an extraction schema | | `get_schema_status` | Get the test status of an extraction schema | ### Storage Management (4 tools) | Tool | Description | | :--- | :--- | | `list_storage_configs` | List all storage configurations | | `create_storage_config` | Create a new storage configuration | | `update_storage_config` | Update an existing storage configuration | | `delete_storage_config` | Delete a storage configuration | ### Schedule Management (7 tools) | Tool | Description | | :--- | :--- | | `list_schedules` | List all scheduled scrape jobs | | `create_schedule` | Create a new scheduled scrape job | | `get_schedule` | Get a scheduled job by ID | | `update_schedule` | Update an existing scheduled job | | `delete_schedule` | Delete a scheduled job | | `toggle_schedule` | Toggle a scheduled job active/inactive | | `list_schedule_runs` | Get execution history for a scheduled job | ### Account (1 tool) | Tool | Description | | :--- | :--- | | `get_account_info` | Get account information including credit balance | ### Tool Annotations Every tool sets all four hints from the MCP spec — `readOnlyHint`, `destructiveHint`, `idempotentHint` and `openWorldHint` — so a client that gates on them can tell `list_configs` apart from `delete_config`. `openWorldHint` is true for the tools that reach an address the caller chose, and false for those that only talk to Evomi's own endpoints. `readOnlyHint` is false for anything that creates, updates, deletes, toggles, rotates or orders. The `MUTATING` prefix on `rotate_proxy_session` and `order_browser_profile` is in the tool description as well, since a description reaches every model where an annotation only reaches a client that reads it. A failed call comes back as a normal result with `isError=True` and the reason in the body, not as a transport-level error. Where the Scraper API rejected the request, that body carries the API's own explanation of the parameter it objected to along with the status, so the next attempt can correct it. --- ## Tool Examples ### Proxy Connection Strings ```json // A US residential proxy {"product": "rp", "countries": ["US"]} // Berlin, sticky session held for 30 minutes, three strings {"product": "rp", "countries": ["DE"], "city": "Berlin", "session": "sticky", "lifetime_minutes": 30, "count": 3} // Datacenter over SOCKS5, in host:port:user:pass format {"product": "dcp", "countries": ["GB"], "protocol": "socks5", "format": "2"} // With a curl example that can be pasted and run as it stands {"product": "rp", "countries": ["US"], "runnable_curl_example": true} ``` Call `list_proxy_targeting_options` first whenever a city, region or ISP is involved. Every entry it returns carries an `id`, which is what the gateway accepts, and a `name`, which is the display form. Cities, regions and continents take either — `North America` and `north.america` reach the same pool. Countries are two-letter ISO 3166-1 alpha-2 codes, so the United Kingdom is `GB`. An ISP is the one value that has to be the `id`, since the catalogue abbreviates its label. A value the gateway does not take is refused with a pointer to what it does, rather than encoded into a string that fails at connect time. City and region targeting take at most one country, and work without one. ### Scraping ```json // Basic scrape {"url": "https://example.com"} // AI extraction {"url": "https://example.com/products", "ai_enhance": true, "ai_prompt": "Extract product names and prices"} // Browser mode with actions {"url": "https://example.com", "mode": "browser", "js_instructions": [{"click": ".accept-cookies"}, {"wait": 1000}]} ``` ### Crawling ```json // Basic crawl {"domain": "example.com", "max_urls": 50} // With URL filter {"domain": "example.com", "url_pattern": "/products/", "depth": 3} ``` ### Domain Search ```json // Find domains {"query": "best e-commerce sites in Germany", "max_urls": 20, "region": "de-de"} // Up to 10 queries in one call, max_urls applying to each {"query": ["online bookstores", "book shops UK"], "max_urls": 50} ``` ### Config Management ```json // Create config {"name": "Product Scraper", "config": {"mode": "browser", "output": "markdown"}} // Generate config with AI {"name": "Amazon Scraper", "prompt": "Scrape product title, price, and reviews from Amazon"} ``` ### Scheduling ```json // Create daily schedule {"name": "Daily Prices", "config_id": "cfg_abc123", "interval_minutes": 1440, "start_time": "09:00"} ``` --- ## Running the Server Directly ```bash evomi-mcp # or python -m evomi_mcp.server ``` --- ## Credits Scraping, crawling, schema and schedule operations consume Scraper API credits, and what a call costs depends on the mode it runs in and the options it carries. Current rates are in [Pricing at a Glance](/scraping-products-instructions/scraper-api/#pricing-at-a-glance). Each scraping response carries `credits_used` and `credits_remaining`, so the cost of a call is visible in its own result. `get_api_access` reports the balance on the account. The proxy tools go through the Public API and spend no Scraper API credits. Reading credentials, usage or targeting options is free, and the traffic you then send through a proxy draws on the product's data balance, which `list_proxy_products` reports. `order_browser_profile` charges that balance directly, with no way to undo the charge, which is why it exists only under `EVOMI_ENABLE_SPENDING=1`. --- ## Resources | Resource | Link | | :--- | :--- | | GitHub Repository | [github.com/evomi/evomi-mcp](https://github.com/evomi/evomi-mcp) | | PyPI Package | [pypi.org/project/evomi-mcp](https://pypi.org/project/evomi-mcp/) | | Scraper API | [Scraper API docs](/scraping-products-instructions/scraper-api/) | | Public API | [Public API docs](/public-api/) | | MCP Specification | [modelcontextprotocol.io](https://modelcontextprotocol.io/) | --- ## n8n Integration Source: https://docs.evomi.com/integrations/n8n/ [n8n](https://n8n.io/) is a popular workflow automation platform that allows you to connect Evomi with over 1,000 apps and services. The Evomi node is verified by n8n, meaning it's solid, safe, and ready to use in your automation workflows. --- ## What You Can Do | Action | Description | | :--- | :--- | | **Scrape** | Scrape content from a web page | --- ## Installation The Evomi integration is a **verified node** in n8n. Verified nodes need a quick setup by an instance owner first, but after that, everyone on the instance can start using them in their workflows. ### Step 1: Sign in to n8n Sign in to your n8n instance or [create an account](https://app.n8n.cloud/register) if you don't have one. ### Step 2: Open the Editor Open the n8n workflow editor. ### Step 3: Add the Evomi Node Click **+** in the top right to open the **Nodes panel**, then search for "Evomi" and add it to your workflow. --- ## Authentication To use the Evomi node, you'll need to configure it with your Evomi API key: 1. Go to your [Evomi Dashboard](https://my.evomi.com/settings/api) to get your API key 2. In n8n, create a new credential for Evomi 3. Enter your API key in the credential configuration --- ## Usage Example Once configured, you can use the Evomi node in your workflows to scrape web pages: 1. Add an **Evomi** node to your workflow 2. Select the **Scrape** action 3. Enter the URL you want to scrape 4. Configure any additional parameters (extraction schema, proxy settings, etc.) 5. Connect the output to other nodes in your workflow --- ## Resources | Resource | Link | | :--- | :--- | | Evomi on GitHub | [github.com/evomi](https://github.com/evomi/) | | Using verified nodes in n8n | [n8n Documentation](https://docs.n8n.io/integrations/community-nodes/installation/verified-install/) | | n8n Integration Page | [n8n.io/integrations/evomi](https://n8n.io/integrations/evomi/) | --- ## Benefits of Using n8n with Evomi - **Visual Workflow Builder**: Create complex scraping workflows without code - **1000+ Integrations**: Connect scraped data to hundreds of apps and services - **Built-in AI Features**: Leverage n8n's AI capabilities alongside Evomi - **Scheduling**: Automate your scraping workflows to run on a schedule - **Error Handling**: Built-in retry logic and error handling for robust workflows --- ## Python Client Source: https://docs.evomi.com/integrations/python-client/ The **Evomi Python Client** provides a production-ready, typed interface to Evomi's API. It supports both synchronous and asynchronous operations with minimal dependencies. --- ## Installation Install the package from PyPI: ```bash pip install evomi-client ``` --- ## Quick Start ### Async Client ```python import asyncio from evomi_client import EvomiClient async def main(): # Initialize with API key (or set EVOMI_API_KEY env var) client = EvomiClient(api_key="your-api-key") # Scrape a URL result = await client.scrape("https://example.com") print(result["content"]) asyncio.run(main()) ``` ### Sync Client ```python from evomi_client import EvomiClientSync client = EvomiClientSync(api_key="your-api-key") result = client.scrape("https://example.com") print(result["content"]) ``` --- ## Authentication Set your API key via environment variable: ```bash export EVOMI_API_KEY="your-api-key" ``` Or pass it directly: ```python client = EvomiClient(api_key="your-api-key") ``` --- ## Scraping Operations ### scrape(url, ...) Scrape a single URL with configurable options. ```python result = await client.scrape( "https://example.com", mode="auto", # "request", "browser", or "auto" output="markdown", # "html", "markdown", "screenshot", "pdf" device="windows", # "windows", "macos", "android" proxy_type="residential", proxy_country="US", proxy_session_id="abc123", wait_until="domcontentloaded", ai_enhance=True, ai_prompt="Extract product data", ai_source="markdown", js_instructions=[{"click": ".load-more"}], execute_js="window.scrollTo(0, document.body.scrollHeight)", wait_seconds=2, screenshot=False, pdf=False, excluded_tags=["nav", "footer"], excluded_selectors=[".ads"], block_resources=["image", "stylesheet"], additional_headers={"X-Custom": "value"}, capture_headers=True, network_capture=[{"url_pattern": "/api/.*"}], async_mode=False, config_id="cfg_abc123", scheme_id="sch_abc123", extract_scheme=[{"label": "title", "type": "content", "selector": "h1"}], storage_id="stor_abc123", use_default_storage=False, ) ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `url` | str | required | URL to scrape | | `mode` | str | "auto" | Scraping mode: "request" (fast), "browser" (JS), "auto" (detect) | | `output` | str | "markdown" | Output format: "html", "markdown", "screenshot", "pdf" | | `device` | str | "windows" | Device type: "windows", "macos", "android" | | `proxy_type` | str | "residential" | Proxy type: "datacenter", "residential" | | `proxy_country` | str | "US" | Two-letter country code | | `proxy_session_id` | str | None | Proxy session ID (6-8 chars) | | `wait_until` | str | "domcontentloaded" | Wait condition | | `ai_enhance` | bool | False | Enable AI enhancement | | `ai_prompt` | str | None | Prompt for AI extraction | | `ai_source` | str | None | AI source: "markdown", "screenshot" | | `ai_force_json` | bool | True | Force AI response to valid JSON | | `js_instructions` | list | None | JS actions: click, wait, fill, wait_for | | `execute_js` | str | None | Raw JavaScript to execute | | `wait_seconds` | int | 0 | Seconds to wait after load | | `screenshot` | bool | False | Capture screenshot | | `pdf` | bool | False | Capture PDF | | `excluded_tags` | list | None | HTML tags to remove | | `excluded_selectors` | list | None | CSS selectors to remove | | `block_resources` | list | None | Resource types to block | | `additional_headers` | dict | None | Extra HTTP headers | | `capture_headers` | bool | False | Capture response headers | | `network_capture` | list | None | Network capture filters | | `async_mode` | bool | False | Return immediately with task ID | | `config_id` | str | None | Saved config ID | | `scheme_id` | str | None | Saved extraction schema ID | | `extract_scheme` | list | None | Inline extraction schema | | `storage_id` | str | None | Storage config ID | | `use_default_storage` | bool | False | Use default storage | | `delivery` | str | "json" | Response format: "raw" or "json" | | `include_content` | bool | True | Include content in JSON response | | `webhook` | WebhookConfig | None | Webhook configuration | ### crawl(domain, ...) Crawl a website to discover and scrape multiple pages. ```python result = await client.crawl( domain="example.com", max_urls=100, depth=2, url_pattern="/products/.*", scraper_config={"mode": "browser", "output": "markdown"}, async_mode=False, ) ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `domain` | str | required | Domain to crawl | | `max_urls` | int | 100 | Maximum URLs to crawl | | `depth` | int | 2 | Crawl depth | | `url_pattern` | str | None | Regex pattern to filter URLs | | `scraper_config` | dict | None | Configuration for scraping each page | | `async_mode` | bool | False | Return immediately with task ID | ### map_website(domain, ...) Discover URLs from a website via sitemaps, CommonCrawl, or crawling. ```python result = await client.map_website( domain="example.com", sources=["sitemap", "commoncrawl"], max_urls=500, url_pattern="/products/.*", check_if_live=False, depth=1, async_mode=False, ) ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `domain` | str | required | Domain to map | | `sources` | list | ["sitemap", "commoncrawl"] | Sources: "sitemap", "commoncrawl", "crawl" | | `max_urls` | int | 500 | Maximum URLs to discover | | `url_pattern` | str | None | Regex pattern to filter URLs | | `check_if_live` | bool | False | Check if URLs are live | | `depth` | int | 1 | Crawl depth if using crawl source | | `async_mode` | bool | False | Return immediately with task ID | ### search_domains(query, ...) Find domains by searching the web. ```python # Single query result = await client.search_domains( query="e-commerce platforms", max_urls=20, region="us-en", ) # Multiple queries (up to 10) result = await client.search_domains( query=["web scraping tools", "data extraction services"], max_urls=20, region="us-en", ) ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `query` | str or list | required | Search query or list of up to 10 queries | | `max_urls` | int | 20 | Max domains per query (max: 100) | | `region` | str | "us-en" | Region for results (e.g., "us-en", "de-de") | ### agent_request(message) Send a natural language request to the AI agent. ```python result = await client.agent_request( "Scrape example.com and extract all product prices" ) ``` ### get_task_status(task_id, task_type) Check the status of an async task. ```python result = await client.get_task_status( task_id="abc123", task_type="scrape" # "scrape", "crawl", "map", "config_generate", "schema" ) ``` --- ## Config Management Save and reuse scrape configurations. ### list_configs(...) ```python configs = await client.list_configs( page=1, per_page=20, sort_by="created_at", sort_order="desc", ) ``` ### create_config(name, config) ```python config = await client.create_config( name="Product Scraper", config={"mode": "browser", "output": "markdown"} ) ``` ### get_config(config_id) ```python config = await client.get_config("cfg_abc123") ``` ### update_config(config_id, ...) ```python config = await client.update_config( "cfg_abc123", name="New Name", config={"mode": "request"} ) ``` ### delete_config(config_id) ```python await client.delete_config("cfg_abc123") ``` ### generate_config(name, prompt) Generate a scrape config from natural language using AI. ```python config = await client.generate_config( name="Amazon Scraper", prompt="Scrape product title and price from Amazon product pages" ) ``` --- ## Schema Management Define reusable structured data extraction schemas. ### list_schemas(...) ```python schemas = await client.list_schemas( page=1, per_page=20, sort_by="created_at", sort_order="desc", ) ``` ### create_schema(name, config, ...) ```python schema = await client.create_schema( name="Product Schema", config={ "url": "https://example.com/product", "extract_scheme": [ {"label": "title", "type": "content", "selector": "h1"}, {"label": "price", "type": "content", "selector": ".price"} ] }, test=True, fix=False, ) ``` ### get_schema(scheme_id) ```python schema = await client.get_schema("sch_abc123") ``` ### update_schema(scheme_id, name, config, ...) ```python schema = await client.update_schema( "sch_abc123", name="Updated Schema", config={"url": "...", "extract_scheme": [...]}, test=True, ) ``` ### delete_schema(scheme_id) ```python await client.delete_schema("sch_abc123") ``` ### get_schema_status(scheme_id) ```python status = await client.get_schema_status("sch_abc123") ``` --- ## Schedule Management Run scrape configs on a recurring schedule. ### list_schedules(...) ```python schedules = await client.list_schedules( page=1, per_page=20, active_only=False, ) ``` ### create_schedule(name, config_id, interval_minutes, ...) ```python schedule = await client.create_schedule( name="Daily Price Check", config_id="cfg_abc123", interval_minutes=1440, # Daily start_time="09:00", # UTC stop_on_error=True, ) ``` ### get_schedule(schedule_id) ```python schedule = await client.get_schedule("sched_abc123") ``` ### update_schedule(schedule_id, ...) ```python schedule = await client.update_schedule( "sched_abc123", name="New Name", interval_minutes=720, ) ``` ### delete_schedule(schedule_id) ```python await client.delete_schedule("sched_abc123") ``` ### toggle_schedule(schedule_id) ```python await client.toggle_schedule("sched_abc123") ``` ### list_schedule_runs(schedule_id, ...) ```python runs = await client.list_schedule_runs( "sched_abc123", page=1, per_page=20, ) ``` --- ## Storage Management Connect cloud storage to automatically save scrape results. ### list_storage_configs() ```python configs = await client.list_storage_configs() ``` ### create_storage_config(name, storage_type, config, ...) ```python # S3-compatible storage storage = await client.create_storage_config( name="My S3", storage_type="s3_compatible", config={ "bucket": "my-bucket", "region": "us-east-1", "access_key": "...", "secret_key": "...", }, set_as_default=True, ) # Google Cloud Storage storage = await client.create_storage_config( name="My GCS", storage_type="gcs", config={ "bucket": "my-bucket", "credentials_json": "...", }, ) # Azure Blob Storage storage = await client.create_storage_config( name="My Azure", storage_type="azure_blob", config={ "container": "my-container", "connection_string": "...", }, ) ``` ### update_storage_config(storage_id, ...) ```python storage = await client.update_storage_config( "stor_abc123", name="Renamed Storage", set_as_default=True, ) ``` ### delete_storage_config(storage_id) ```python await client.delete_storage_config("stor_abc123") ``` --- ## Public API Access proxy credentials and related data. ### get_public_api() Access the Evomi Public API to get proxy credentials and related data. ```python data = await client.get_public_api() # Returns proxy credentials and product information ``` ### get_proxy_data() Get detailed information about your proxy products. ```python data = await client.get_proxy_data() # Returns: {"products": {"rp": {...}, "sdc": {...}, "mp": {...}}, ...} ``` ### get_targeting_options() Get available targeting parameters for different proxy types. ```python options = await client.get_targeting_options() ``` ### get_scraper_data() Get information about your Scraper API access. ```python data = await client.get_scraper_data() ``` ### get_browser_data() Get information about your Browser API access. ```python data = await client.get_browser_data() ``` ### rotate_session(session_id, product) Force an IP address change for an existing proxy session. ```python result = await client.rotate_session( session_id="abc12345", product="rp" # "rpc", "rp", "sdc", "mp" ) ``` ### generate_proxies(product, ...) Generate proxy strings with specific targeting parameters. ```python proxies = await client.generate_proxies( product="rp", countries="US,GB,DE", city="New York", session="sticky", amount=10, protocol="http", lifetime=30, adblock=True, ) # Returns plain text, one proxy per line ``` --- ## Account Info ### get_account_info() ```python info = await client.get_account_info() print(info.get("credits", "N/A")) ``` --- ## Webhooks Webhooks allow you to receive notifications when scraping operations complete, fail, or start. Supports Discord, Slack, and custom endpoints. ### WebhookConfig ```python from evomi_client import WebhookConfig # Discord webhook webhook = WebhookConfig( url="https://discord.com/api/webhooks/...", webhook_type="discord", events=["completed", "failed"] ) # Custom webhook with HMAC signature webhook = WebhookConfig( url="https://your-server.com/webhook", webhook_type="custom", events=["completed"], secret="your-secret-key" ) ``` | Parameter | Type | Default | Description | | :--- | :--- | :--- | :--- | | `url` | str | required | Your webhook endpoint URL | | `webhook_type` | str | "custom" | Type: "discord", "slack", or "custom" | | `events` | list | ["completed"] | Events to subscribe to | | `secret` | str | None | Secret key for HMAC signature (custom only) | ### Using Webhooks with Scrape ```python from evomi_client import EvomiClient, WebhookConfig client = EvomiClient(api_key="your-api-key") webhook = WebhookConfig( url="https://discord.com/api/webhooks/...", webhook_type="discord", events=["completed", "failed"] ) result = await client.scrape( "https://example.com", mode="browser", webhook=webhook ) ``` ### Using Webhooks with Crawl/Map/Search ```python # With crawl result = await client.crawl( domain="example.com", max_urls=100, webhook=webhook ) # With map_website result = await client.map_website( domain="example.com", webhook=webhook ) # With search_domains result = await client.search_domains( query="e-commerce platforms", webhook=webhook ) ``` ### Using Webhooks with Schedules ```python schedule = await client.create_schedule( name="Daily Price Check", config_id="cfg_abc123", interval_minutes=1440, webhook=webhook ) ``` --- ## Proxy String Builder Evomi provides a proxy network you can use with any HTTP client. Build proxy strings for tools like `requests`, `httpx`, or `aiohttp`: ### Automatic Proxy Configuration ```python from evomi_client import EvomiClient, ProxyType, ProxyProtocol client = EvomiClient(api_key="your-api-key") # Build a proxy string for US residential proxy proxy_string = await client.build_proxy_string( proxy_type=ProxyType.RESIDENTIAL, country="US", session="abc12345" ) print(proxy_string) # Output: http://user:pass_country-US_session-abc12345@rp.evomi.com:1000 ``` ### Manual Proxy Configuration ```python from evomi_client import ProxyConfig, ProxyType, ProxyProtocol config = ProxyConfig( proxy_type=ProxyType.RESIDENTIAL, protocol=ProxyProtocol.HTTP, country="US", city="New York", username="your-username", password="your-password" ) proxy_string = config.build_proxy_string() ``` ### Proxy Configuration Options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `proxy_type` | ProxyType | `RESIDENTIAL` | Type: `RESIDENTIAL`, `DATACENTER`, `MOBILE` | | `protocol` | ProxyProtocol | `HTTP` | Protocol: `HTTP`, `HTTPS`, `SOCKS5` | | `country` | str | None | Two-letter ISO country code (e.g., "US", "DE") | | `city` | str | None | City name (spaces replaced with dots) | | `region` | str | None | State/region name | | `continent` | str | None | Continent: africa, asia, europe, north.america, oceania, south.america | | `isp` | str | None | ISP shortcode (e.g., "att", "comcast") | | `session` | str | None | Sticky session ID (6-10 alphanumeric chars) | | `hardsession` | str | None | Hard session ID (6-10 alphanumeric chars) | | `lifetime` | int | None | Session duration in minutes (default 30, max 1440) | | `mode` | ResidentialMode | `STANDARD` | Residential mode: `STANDARD`, `SPEED`, `QUALITY` | ### Expert Settings | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `latency` | int | None | Max latency in ms | | `fraudscore` | int | None | Max fraud score | | `device` | str | None | Device type: "windows", "unix", "apple" | | `activesince` | int | None | Min connection minutes | | `asn` | str | None | ASN filter | | `zip` | str | None | Zipcode targeting | | `http3` | bool | False | Enable HTTP3/QUIC | | `udp` | bool | False | UDP support (Enterprise only) | | `extended` | bool | False | Extended pool (cannot combine with other expert filters) | ### Proxy Endpoints & Ports | Type | HTTP | HTTPS | SOCKS5 | |------|------|-------|--------| | Residential | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | Datacenter | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | | Mobile | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ### Proxy String Format ``` {protocol}://{username}:{password}_{params}@{endpoint}:{port} ``` Example: `http://user:pass_country-US_session-abc12345@rp.evomi.com:1000` ### Proxy Types | Type | Endpoint | Use Case | |------|----------|----------| | Residential | `rp.evomi.com:1000` | Browser-compatible workflows and broad site coverage | | Datacenter | `dcp.evomi.com:2000` | Fast, high-volume requests | | Mobile | `mp.evomi.com:3000` | Highest trust, mobile-specific targets | --- ## Error Handling ```python import httpx try: result = await client.scrape("https://example.com") except httpx.HTTPStatusError as e: print(f"API error: {e.response.status_code}") print(f"Details: {e.response.text}") ``` --- ## Credits & Pricing All operations consume credits: | Operation | Cost | | :--- | :--- | | Base request | 1 credit | | Browser mode | 5x multiplier | | Residential proxy | 2x multiplier | | AI enhancement | +30 credits | | Screenshot/PDF | +1 credit each | Credit usage is returned in response headers and in `_credits_used`, `_credits_remaining` fields. --- ## Resources | Resource | Link | | :--- | :--- | | PyPI Package | [pypi.org/project/evomi-client](https://pypi.org/project/evomi-client/) | | Evomi Website | [evomi.com](https://evomi.com) | | API Documentation | [docs.evomi.com](https://docs.evomi.com/overview/) | --- ## Benefits - **Async & Sync Support** — Use `EvomiClient` for async or `EvomiClientSync` for synchronous operations - **Full API Coverage** — All Evomi endpoints supported - **Type Hints** — Complete type annotations for IDE support --- ## Rust Client Source: https://docs.evomi.com/integrations/rust-client/ The **Evomi Rust Client** provides a production-ready, async interface to Evomi's API with full type safety and idiomatic Rust patterns. --- ## Installation Add to your `Cargo.toml`: ```toml [dependencies] evomi-client = "0.1.2" tokio = { version = "1", features = ["full"] } ``` --- ## Quick Start ```rust use evomi_client::Client; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize with API key (or set EVOMI_API_KEY env var) let client = Client::new("your-api-key", None, None)?; // Scrape a URL let result = client.scrape("https://example.com", None).await?; println!("{:?}", result); Ok(()) } ``` --- ## Authentication Set your API key via environment variable: ```bash export EVOMI_API_KEY="your-api-key" ``` Or pass it directly: ```rust let client = Client::new("your-api-key", None, None)?; ``` Custom base URL (for testing): ```rust let client = Client::new("your-api-key", Some("https://custom.evomi.com".to_string()), None)?; ``` --- ## Scraping Operations ### scrape(url, options) Scrape a single URL with configurable options. ```rust use evomi_client::{Client, ScrapeOptions}; let result = client.scrape("https://example.com", Some(ScrapeOptions { mode: Some("auto".to_string()), content: Some("markdown".to_string()), device: Some("windows".to_string()), proxy_type: Some("residential".to_string()), proxy_country: Some("US".to_string()), proxy_session_id: Some("abc123".to_string()), wait_until: Some("domcontentloaded".to_string()), ai_enhance: Some(true), ai_prompt: Some("Extract product data".to_string()), ai_source: Some("markdown".to_string()), js_instructions: Some(vec![serde_json::json!({"click": ".load-more"})]), execute_js: Some("window.scrollTo(0, document.body.scrollHeight)".to_string()), wait_seconds: Some(2), screenshot: Some(false), pdf: Some(false), excluded_tags: Some(vec!["nav".to_string(), "footer".to_string()]), excluded_selectors: Some(vec![".ads".to_string()]), block_resources: Some(vec!["image".to_string(), "stylesheet".to_string()]), capture_headers: Some(true), async_mode: Some(false), config_id: Some("cfg_abc123".to_string()), scheme_id: Some("sch_abc123".to_string()), extract_scheme: Some(vec![serde_json::json!({"label": "title", "type": "content", "selector": "h1"})]), storage_id: Some("stor_abc123".to_string()), use_default_storage: Some(false), webhook: None, ..Default::default() })).await?; ``` | Field | Type | Default | Description | | :--- | :--- | :--- | :--- | | `mode` | `Option` | `"auto"` | Scraping mode: `"request"`, `"browser"`, `"auto"` | | `content` | `Option` | `"markdown"` | Output format: `"html"`, `"markdown"`, `"screenshot"`, `"pdf"` | | `device` | `Option` | `"windows"` | Device type: `"windows"`, `"macos"`, `"android"` | | `proxy_type` | `Option` | `"residential"` | Proxy type: `"datacenter"`, `"residential"` | | `proxy_country` | `Option` | `"US"` | Two-letter country code | | `proxy_session_id` | `Option` | — | Proxy session ID (6-8 chars) | | `wait_until` | `Option` | `"domcontentloaded"` | Wait condition | | `ai_enhance` | `Option` | `false` | Enable AI extraction | | `ai_prompt` | `Option` | — | Prompt for AI extraction | | `ai_source` | `Option` | — | AI source | | `ai_force_json` | `Option` | `true` | Force AI response to JSON | | `js_instructions` | `Option>` | — | JS actions | | `execute_js` | `Option` | — | Raw JavaScript to execute | | `wait_seconds` | `Option` | `0` | Seconds to wait after page load | | `screenshot` | `Option` | `false` | Capture screenshot | | `pdf` | `Option` | `false` | Capture PDF | | `excluded_tags` | `Option>` | — | HTML tags to remove | | `excluded_selectors` | `Option>` | — | CSS selectors to remove | | `block_resources` | `Option>` | — | Resource types to block | | `additional_headers` | `Option>` | — | Extra HTTP headers | | `capture_headers` | `Option` | `false` | Capture response headers | | `network_capture` | `Option>` | — | Network capture filters | | `async_mode` | `Option` | `false` | Return immediately with task ID | | `config_id` | `Option` | — | Saved config ID | | `scheme_id` | `Option` | — | Saved extraction schema ID | | `extract_scheme` | `Option>` | — | Inline extraction schema | | `storage_id` | `Option` | — | Storage config ID | | `use_default_storage` | `Option` | `false` | Use default storage | | `delivery` | `Option` | `"json"` | Response format: `"raw"` or `"json"` | | `include_content` | `Option` | `true` | Include content in response | | `webhook` | `Option` | — | Webhook configuration | ### crawl(domain, options) Crawl a website to discover and scrape multiple pages. ```rust use evomi_client::CrawlOptions; let result = client.crawl("example.com", Some(CrawlOptions { max_urls: Some(100), depth: Some(2), url_pattern: Some("/blog/.*".to_string()), scraper_config: Some(serde_json::json!({"mode": "browser", "output": "markdown"})), async_mode: Some(false), webhook: None, })).await?; ``` | Field | Type | Default | Description | | :--- | :--- | :--- | :--- | | `max_urls` | `Option` | `100` | Maximum URLs to crawl | | `depth` | `Option` | `2` | Crawl depth | | `url_pattern` | `Option` | — | Regex pattern to filter URLs | | `scraper_config` | `Option` | — | Config for scraping each page | | `async_mode` | `Option` | `false` | Return immediately with task ID | ### map_website(domain, options) Discover URLs from a website via sitemaps, CommonCrawl, or crawling. ```rust use evomi_client::MapOptions; let result = client.map_website("example.com", Some(MapOptions { sources: Some(vec!["sitemap".to_string(), "commoncrawl".to_string()]), max_urls: Some(500), url_pattern: Some("/products/.*".to_string()), check_if_live: Some(false), depth: Some(1), async_mode: Some(false), webhook: None, })).await?; ``` | Field | Type | Default | Description | | :--- | :--- | :--- | :--- | | `sources` | `Option>` | `["sitemap", "commoncrawl"]` | Discovery sources | | `max_urls` | `Option` | `500` | Maximum URLs to discover | | `url_pattern` | `Option` | — | Regex pattern to filter URLs | | `check_if_live` | `Option` | `false` | Check if URLs are live | | `depth` | `Option` | `1` | Crawl depth if using crawl source | | `async_mode` | `Option` | `false` | Return immediately with task ID | ### search_domains(query, options) Find domains by searching the web. ```rust use evomi_client::SearchOptions; // Single query let result = client.search_domains("e-commerce platforms", Some(SearchOptions { max_urls: Some(20), region: Some("us-en".to_string()), webhook: None, })).await?; // Multiple queries (up to 10) let result = client.search_domains( vec!["web scraping tools".to_string(), "data extraction".to_string()], Some(SearchOptions { max_urls: Some(20), ..Default::default() }) ).await?; ``` | Field | Type | Default | Description | | :--- | :--- | :--- | :--- | | `max_urls` | `Option` | `20` | Max domains per query (max: 100) | | `region` | `Option` | `"us-en"` | Region for results | ### agent_request(message) Send a natural language request to the AI agent. ```rust let result = client.agent_request("Scrape example.com and extract all product prices").await?; ``` ### get_task_status(task_id, task_type) Check the status of an async task. ```rust let result = client.get_task_status("abc123", "scrape").await?; // task_type: "scrape" | "crawl" | "map" | "config_generate" | "schema" ``` --- ## Config Management Save and reuse scrape configurations. ### list_configs(options) ```rust use evomi_client::ListOptions; let configs = client.list_configs(Some(ListOptions { page: Some(1), per_page: Some(20), sort_by: Some("created_at".to_string()), sort_order: Some("desc".to_string()), })).await?; ``` ### create_config(name, config) ```rust let config = client.create_config("Product Scraper", serde_json::json!({ "mode": "browser", "output": "markdown" })).await?; ``` ### get_config(config_id) ```rust let config = client.get_config("cfg_abc123").await?; ``` ### update_config(config_id, name, config) ```rust let config = client.update_config("cfg_abc123", Some("New Name"), Some(serde_json::json!({ "mode": "request" }))).await?; ``` ### delete_config(config_id) ```rust let result = client.delete_config("cfg_abc123").await?; ``` ### generate_config(name, prompt) Generate a scrape config from natural language using AI. ```rust let config = client.generate_config( "Amazon Scraper", "Scrape product title and price from Amazon product pages" ).await?; ``` --- ## Schema Management Define reusable structured data extraction schemas. ### list_schemas(options) ```rust let schemas = client.list_schemas(Some(ListOptions { page: Some(1), per_page: Some(20), sort_by: Some("created_at".to_string()), sort_order: Some("desc".to_string()), })).await?; ``` ### create_schema(name, config, options) ```rust use evomi_client::CreateSchemaOptions; let schema = client.create_schema("Product Schema", serde_json::json!({ "url": "https://example.com/product", "extract_scheme": [ {"label": "title", "type": "content", "selector": "h1"}, {"label": "price", "type": "content", "selector": ".price"} ] }), Some(CreateSchemaOptions { test: Some(true), fix: Some(false), extract_prompt: None, })).await?; ``` ### get_schema(scheme_id) ```rust let schema = client.get_schema("sch_abc123").await?; ``` ### update_schema(scheme_id, name, config, options) ```rust let schema = client.update_schema("sch_abc123", "Updated Schema", serde_json::json!({ "url": "...", "extract_scheme": [...] }), Some(CreateSchemaOptions { test: Some(true), ..Default::default() })).await?; ``` ### delete_schema(scheme_id) ```rust let result = client.delete_schema("sch_abc123").await?; ``` ### get_schema_status(scheme_id) ```rust let status = client.get_schema_status("sch_abc123").await?; ``` --- ## Schedule Management Run scrape configs on a recurring schedule. ### list_schedules(options, active_only) ```rust let schedules = client.list_schedules(Some(ListOptions { page: Some(1), per_page: Some(20), ..Default::default() }), Some(false)).await?; ``` ### create_schedule(name, config_id, interval_minutes, options) ```rust use evomi_client::CreateScheduleOptions; let schedule = client.create_schedule( "Daily Price Check", "cfg_abc123", 1440, // Daily (in minutes) Some(CreateScheduleOptions { start_time: Some("09:00".to_string()), stop_on_error: Some(true), webhook: None, }) ).await?; ``` ### get_schedule(schedule_id) ```rust let schedule = client.get_schedule("sched_abc123").await?; ``` ### update_schedule(schedule_id, name, config_id, interval_minutes, stop_on_error) ```rust let schedule = client.update_schedule( "sched_abc123", Some("New Name"), None, Some(720), None ).await?; ``` ### delete_schedule(schedule_id) ```rust let result = client.delete_schedule("sched_abc123").await?; ``` ### toggle_schedule(schedule_id) ```rust let result = client.toggle_schedule("sched_abc123").await?; ``` ### list_schedule_runs(schedule_id, page, per_page) ```rust let runs = client.list_schedule_runs("sched_abc123", Some(1), Some(20)).await?; ``` --- ## Storage Management Connect cloud storage to automatically save scrape results. ### list_storage_configs() ```rust let configs = client.list_storage_configs().await?; ``` ### create_storage_config(name, storage_type, config, options) ```rust use evomi_client::CreateStorageOptions; // S3-compatible storage let storage = client.create_storage_config( "My S3", "s3_compatible", serde_json::json!({ "bucket": "my-bucket", "region": "us-east-1", "access_key": "...", "secret_key": "..." }), Some(CreateStorageOptions { set_as_default: Some(true), }) ).await?; // Google Cloud Storage let storage = client.create_storage_config( "My GCS", "gcs", serde_json::json!({ "bucket": "my-bucket", "credentials_json": "..." }), None ).await?; // Azure Blob Storage let storage = client.create_storage_config( "My Azure", "azure_blob", serde_json::json!({ "container": "my-container", "connection_string": "..." }), None ).await?; ``` ### update_storage_config(storage_id, name, config, set_as_default) ```rust let storage = client.update_storage_config( "stor_abc123", Some("Renamed Storage"), None, Some(true) ).await?; ``` ### delete_storage_config(storage_id) ```rust let result = client.delete_storage_config("stor_abc123").await?; ``` --- ## Public API Access proxy credentials and related data. ### get_public_api() Access the Evomi Public API to get proxy credentials and related data. ```rust let data = client.get_public_api().await?; // Returns proxy credentials and product information ``` ### get_proxy_data() Get detailed information about your proxy products. ```rust let data = client.get_proxy_data().await?; // Returns: {"products": {"rp": {...}, "sdc": {...}, "mp": {...}}, ...} ``` ### get_targeting_options() Get available targeting parameters for different proxy types. ```rust let options = client.get_targeting_options().await?; ``` ### get_scraper_data() Get information about your Scraper API access. ```rust let data = client.get_scraper_data().await?; ``` ### get_browser_data() Get information about your Browser API access. ```rust let data = client.get_browser_data().await?; ``` ### rotate_session(session_id, product) Force an IP address change for an existing proxy session. ```rust let result = client.rotate_session("abc12345", "rp").await?; // product: "rpc", "rp", "sdc", "mp" ``` ### generate_proxies(product, options) Generate proxy strings with specific targeting parameters. ```rust use evomi_client::GenerateProxiesOptions; let proxies = client.generate_proxies("rp", Some(GenerateProxiesOptions { countries: Some("US,GB,DE".to_string()), city: Some("New York".to_string()), session: Some("sticky".to_string()), amount: Some(10), protocol: Some("http".to_string()), lifetime: Some(30), adblock: Some(true), ..Default::default() })).await?; // Returns plain text, one proxy per line ``` --- ## Account Info ### get_account_info() ```rust let info = client.get_account_info().await?; println!("{:?}", info.get("credits_remaining")); ``` --- ## Webhooks Webhooks allow you to receive notifications when scraping operations complete, fail, or start. Supports Discord, Slack, and custom endpoints. ### WebhookConfig ```rust use evomi_client::WebhookConfig; // Discord webhook let webhook = WebhookConfig::new("https://discord.com/api/webhooks/...") .webhook_type("discord") .events(vec!["completed".to_string(), "failed".to_string()]); // Custom webhook with HMAC signature let webhook = WebhookConfig::new("https://your-server.com/webhook") .webhook_type("custom") .events(vec!["completed".to_string()]) .secret("your-secret-key"); ``` ### Using Webhooks with Scrape ```rust let webhook = WebhookConfig::new("https://discord.com/api/webhooks/...") .webhook_type("discord") .events(vec!["completed".to_string(), "failed".to_string()]); let result = client.scrape("https://example.com", Some(ScrapeOptions { mode: Some("browser".to_string()), webhook: Some(webhook), ..Default::default() })).await?; ``` ### Using Webhooks with Schedules ```rust let webhook = WebhookConfig::new("https://hooks.slack.com/services/...") .webhook_type("slack") .events(vec!["completed".to_string()]); let schedule = client.create_schedule( "Daily Monitor", "cfg_abc123", 1440, Some(CreateScheduleOptions { webhook: Some(webhook), ..Default::default() }) ).await?; ``` --- ## Proxy String Builder Evomi provides a proxy network you can use with any HTTP client. Build proxy strings for `reqwest` or any other library: ### Automatic Proxy Configuration ```rust use evomi_client::types::{ProxyType, ProxyProtocol, ProxyBuildOptions}; // Build a proxy string for US residential proxy let proxy_string = client.build_proxy_string( ProxyType::Residential, Some(ProxyBuildOptions::new() .country("US") .session("abc12345") .protocol(ProxyProtocol::Http)) ).await?; println!("{}", proxy_string); // Output: http://user:pass_country-US_session-abc12345@rp.evomi.com:1000 ``` ### Manual Proxy Configuration ```rust use evomi_client::proxy::ProxyConfig; use evomi_client::types::{ProxyType, ProxyProtocol, ResidentialMode}; let config = ProxyConfig::new() .proxy_type(ProxyType::Residential) .protocol(ProxyProtocol::Http) .country("US") .city("New York") .credentials("username", "password"); let proxy_string = config.build_proxy_string(); ``` ### Proxy Configuration Options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `proxy_type` | ProxyType | `Residential` | Type: `Residential`, `Datacenter`, `Mobile` | | `protocol` | ProxyProtocol | `Http` | Protocol: `Http`, `Https`, `Socks5` | | `country` | String | — | Two-letter ISO country code (e.g., "US", "DE") | | `city` | String | — | City name (spaces replaced with dots) | | `region` | String | — | State/region name | | `continent` | String | — | Continent: africa, asia, europe, north.america, oceania, south.america | | `isp` | String | — | ISP shortcode (e.g., "att", "comcast") | | `session` | String | — | Sticky session ID (6-10 alphanumeric chars) | | `hardsession` | String | — | Hard session ID (6-10 alphanumeric chars) | | `lifetime` | u32 | — | Session duration in minutes (default 30, max 1440) | | `mode` | ResidentialMode | — | Residential mode: `Speed`, `Quality` | ### Expert Settings | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `latency` | u32 | — | Max latency in ms | | `fraudscore` | u32 | — | Max fraud score | | `device` | String | — | Device type: "windows", "unix", "apple" | | `activesince` | u32 | — | Min connection minutes | | `asn` | String | — | ASN filter | | `zip` | String | — | Zipcode targeting | | `http3` | bool | `false` | Enable HTTP3/QUIC | | `udp` | bool | `false` | UDP support (Enterprise only) | | `extended` | bool | `false` | Extended pool (cannot combine with other expert filters) | ### Proxy Endpoints & Ports | Type | HTTP | HTTPS | SOCKS5 | |------|------|-------|--------| | Residential | `rp.evomi.com:1000` | `rp.evomi-proxy.com:1001` | `rp.evomi.com:1002` | | Datacenter | `dcp.evomi.com:2000` | `dcp.evomi-proxy.com:2001` | `dcp.evomi.com:2002` | | Mobile | `mp.evomi.com:3000` | `mp.evomi-proxy.com:3001` | `mp.evomi.com:3002` | HTTPS endpoints use the `evomi-proxy.com` domain — that is the hostname our TLS certificate covers, and it reaches the same servers. A client that verifies certificates will reject the `evomi.com` form on the HTTPS port. ### Proxy String Format ``` {protocol}://{username}:{password}_{params}@{endpoint}:{port} ``` Example: `http://user:pass_country-US_session-abc12345@rp.evomi.com:1000` ### Proxy Types | Type | Endpoint | Use Case | |------|----------|----------| | Residential | `rp.evomi.com:1000` | Browser-compatible workflows and broad site coverage | | Datacenter | `dcp.evomi.com:2000` | Fast, high-volume requests | | Mobile | `mp.evomi.com:3000` | Highest trust, mobile-specific targets | --- ## Error Handling ```rust use evomi_client::Error; match client.scrape("https://example.com", None).await { Ok(result) => println!("{:?}", result), Err(Error::MissingApiKey) => eprintln!("API key not set"), Err(Error::HttpError(e)) => eprintln!("HTTP error: {}", e), Err(Error::JsonError(e)) => eprintln!("JSON error: {}", e), Err(Error::ApiError(msg)) => eprintln!("API error: {}", msg), Err(e) => eprintln!("Other error: {}", e), } ``` --- ## Credits & Pricing All operations consume credits: | Operation | Cost | | :--- | :--- | | Base request | 1 credit | | Browser mode | 5x multiplier | | Residential proxy | 2x multiplier | | AI enhancement | +30 credits | | Screenshot/PDF | +1 credit each | Credit usage is returned in the result: ```rust println!("{:?}", result.get("_credits_used")); println!("{:?}", result.get("_credits_remaining")); ``` --- ## Requirements - Rust 1.70+ - Tokio runtime --- ## Resources | Resource | Link | | :--- | :--- | | crates.io Package | [crates.io/crates/evomi-client](https://crates.io/crates/evomi-client) | | Evomi Website | [evomi.com](https://evomi.com) | | API Documentation | [docs.evomi.com](https://docs.evomi.com/overview/) | --- ## Benefits - **Async/Await** — Built on Tokio for async operations - **Full API Coverage** — All Evomi endpoints supported - **Type Safe** — Strong typing with idiomatic Rust patterns - **Builder Pattern** — Ergonomic configuration with builder methods --- ## Overview Source: https://docs.evomi.com/overview/ Welcome to the Evomi Documentation This documentation is here to give you an overview over the details of how to use our services. This documentation is mainly for advanced or curious users who want to know the details. For most users, the dashboard will provide all the necessary information on how to use our products. If you feel that anything is missing from these docs, feel free to let us know via any of our communication channels. You can use the search on top to find any specific information.