<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Billy</title>
    <description>The latest articles on DEV Community by Billy (@devyjones).</description>
    <link>https://dev.to/devyjones</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3986902%2Fb8e99535-4b22-4d59-bcec-ffd433c964fc.png</url>
      <title>DEV Community: Billy</title>
      <link>https://dev.to/devyjones</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devyjones"/>
    <language>en</language>
    <item>
      <title>Finding Exposed APIs and Services: A Developer's Guide with ScanSearch</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Thu, 13 Aug 2026 04:00:13 +0000</pubDate>
      <link>https://dev.to/devyjones/finding-exposed-apis-and-services-a-developers-guide-with-scansearch-2fdo</link>
      <guid>https://dev.to/devyjones/finding-exposed-apis-and-services-a-developers-guide-with-scansearch-2fdo</guid>
      <description>&lt;p&gt;As developers, we're constantly building and deploying services. Sometimes, in the rush to get things out the door, or through a simple misconfiguration, an internal API or a development environment might accidentally be exposed to the public internet. This isn't just a theoretical risk; it's a very real vector for security vulnerabilities.&lt;/p&gt;

&lt;p&gt;So, how do we proactively check for these kinds of exposures? Manually scanning IP ranges is tedious and often incomplete. This is where tools designed for internet-wide reconnaissance come in handy. In this article, we'll explore how to use a service like &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt; to identify potentially exposed services, focusing on finding common API endpoints and documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Unintended Exposure
&lt;/h2&gt;

&lt;p&gt;Imagine you've deployed a new microservice. It's meant to be behind an API gateway, accessible only internally. But a firewall rule was misconfigured, or a load balancer setting was overlooked, and suddenly, your &lt;code&gt;dev&lt;/code&gt; environment's &lt;code&gt;/api/v1/admin&lt;/code&gt; endpoint is directly accessible to anyone on the internet. This isn't an uncommon scenario, and it can lead to serious data breaches or system compromises.&lt;/p&gt;

&lt;p&gt;As developers, it's beneficial to have a way to quickly check for these kinds of unintended exposures, not just for our own projects but also to understand the broader landscape of internet-facing services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing ScanSearch
&lt;/h2&gt;

&lt;p&gt;ScanSearch is an internet-wide search engine for network devices, services, and vulnerabilities. Think of it like Google, but for things connected to the internet. It constantly scans and indexes what's out there, allowing you to query for specific banners, ports, services, or even HTTP responses.&lt;/p&gt;

&lt;p&gt;While primarily used by security researchers, developers can leverage ScanSearch to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Verify external accessibility:&lt;/strong&gt; Ensure services &lt;em&gt;intended&lt;/em&gt; to be internal remain so.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Discover common misconfigurations:&lt;/strong&gt; Search for default credentials, exposed Git repositories, or &lt;code&gt;.env&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Identify shadow IT:&lt;/strong&gt; Find forgotten or unknown internet-facing assets.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Explore API landscapes:&lt;/strong&gt; See how common API patterns are exposed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive into some practical examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: Finding Exposed Swagger/OpenAPI Docs
&lt;/h2&gt;

&lt;p&gt;Many API frameworks automatically generate documentation endpoints, often at paths like &lt;code&gt;/swagger&lt;/code&gt;, &lt;code&gt;/api-docs&lt;/code&gt;, or &lt;code&gt;/redoc&lt;/code&gt;. If these endpoints are exposed without authentication, they can provide attackers with a complete blueprint of your API, including sensitive endpoint paths, request/response structures, and even authentication mechanisms.&lt;/p&gt;

&lt;p&gt;To search for exposed Swagger UI endpoints, we can use ScanSearch to look for specific HTTP response body content that indicates the presence of Swagger UI. A common string is the title tag or a script reference.&lt;/p&gt;

&lt;p&gt;Let's try searching for the HTML title associated with Swagger UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.title: "Swagger UI"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query will return a list of hosts that have the string "Swagger UI" in their HTTP title. You can further refine this by looking for specific paths or other indicators. For example, to find those specifically on an &lt;code&gt;/api-docs&lt;/code&gt; path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.title: "Swagger UI" AND http.url: "/api-docs"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combination is powerful. It allows you to quickly identify services that are not only exposing a web server but specifically exposing their API documentation through a well-known path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: Discovering Generic API Endpoints
&lt;/h2&gt;

&lt;p&gt;Sometimes, it's not about a specific documentation page, but just generally looking for common API patterns like &lt;code&gt;/api/v1&lt;/code&gt; or &lt;code&gt;/graphql&lt;/code&gt;. While these queries might return a lot of noise, they can sometimes highlight unexpected public-facing services.&lt;/p&gt;

&lt;p&gt;Let's search for servers that respond to common API path patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.url: "/api/v1/" OR http.url: "/graphql"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will show you a range of services that have these paths. Clicking on the results often reveals more details, including HTTP headers and the full response body, which can help you determine if it's a legitimate public API or an accidentally exposed internal one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: Identifying Development/Staging Environments
&lt;/h2&gt;

&lt;p&gt;Development or staging environments often contain sensitive data, debug information, or unpatched vulnerabilities. If these are accessible from the internet, they become prime targets. We can look for common indicators in HTTP titles or banners.&lt;/p&gt;

&lt;p&gt;For example, searching for common keywords in titles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.title: "dev environment" OR http.title: "staging server" OR http.title: "test system"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even looking for specific default server banners that might indicate a development-focused setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.html: "Welcome to Node.js" AND port: 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: The actual content for &lt;code&gt;http.html&lt;/code&gt; would be a more specific string from the page, or &lt;code&gt;http.body&lt;/code&gt; for the full body content. The example above is illustrative of looking for specific server-side technology indicators combined with a common development port.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Developers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Assume public by default:&lt;/strong&gt; When deploying anything, assume it's public until proven otherwise. Double-check firewall rules and load balancer configurations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Authentication and Authorization:&lt;/strong&gt; Ensure all API endpoints, even internal ones, are protected by robust authentication and authorization mechanisms.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Regular Audits:&lt;/strong&gt; Periodically use tools like ScanSearch to scan your own public IP ranges or domains for unintended exposures.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Least Privilege:&lt;/strong&gt; Configure services with the absolute minimum necessary permissions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Environment Variables:&lt;/strong&gt; Never hardcode sensitive credentials or API keys. Use environment variables or secure secret management services.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Proactively searching for exposed APIs and services is a critical step in maintaining the security posture of our applications. ScanSearch provides a powerful lens into the internet's observable surface, allowing developers to identify potential vulnerabilities before malicious actors do. By incorporating such tools into our development and deployment workflows, we can build more secure and resilient systems.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch.net&lt;/a&gt; to try out these queries yourself and explore the vast landscape of internet-connected devices and services.&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>apidocs</category>
      <category>tools</category>
    </item>
    <item>
      <title>Finding Exposed Services (and Vulnerabilities) on Your Network: A Practical ScanSearch Guide</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Wed, 05 Aug 2026 04:00:13 +0000</pubDate>
      <link>https://dev.to/devyjones/finding-exposed-services-and-vulnerabilities-on-your-network-a-practical-scansearch-guide-20e3</link>
      <guid>https://dev.to/devyjones/finding-exposed-services-and-vulnerabilities-on-your-network-a-practical-scansearch-guide-20e3</guid>
      <description>&lt;p&gt;Ever wondered what services on your network are truly exposed to the internet? Or perhaps you're trying to track down a specific type of device across your public-facing infrastructure? Manually scanning port by port across a large IP range can be tedious, slow, and often misses the bigger picture.&lt;/p&gt;

&lt;p&gt;This article isn't about setting up a traditional port scanner. Instead, we're going to explore how to leverage an internet-wide search engine for network devices and services, &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt;, to quickly identify and understand the public-facing footprint of your network. Think of it like Google, but for servers, routers, cameras, and everything else connected to the internet.&lt;/p&gt;

&lt;p&gt;We'll cover practical use cases, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Identifying unintentionally exposed services.&lt;/li&gt;
&lt;li&gt;  Searching for specific device types or software versions.&lt;/li&gt;
&lt;li&gt;  Spotting known vulnerabilities associated with your infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Blind Spots in Your Public-Facing Infrastructure
&lt;/h2&gt;

&lt;p&gt;It's common for development or operations teams to configure services, sometimes forgetting that a default configuration or an overlooked firewall rule might leave something accessible that shouldn't be. This isn't just about malicious actors; it's also about maintaining good security hygiene and understanding your own attack surface.&lt;/p&gt;

&lt;p&gt;Traditional internal network scans are crucial, but they don't always give you the external perspective. That's where a tool like ScanSearch comes in – it's constantly indexing the internet's public-facing devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with ScanSearch
&lt;/h2&gt;

&lt;p&gt;ScanSearch offers a powerful search syntax. The most straightforward way to begin is by searching for your organization's public IP ranges or domain names. For this tutorial, I'll use &lt;code&gt;example.com&lt;/code&gt; and a hypothetical IP range, but you should substitute these with your own public IPs or domains.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 1: Discovering All Services on Your IP Range
&lt;/h3&gt;

&lt;p&gt;Let's say your organization uses the IP range &lt;code&gt;203.0.113.0/24&lt;/code&gt;. To see everything ScanSearch has indexed for this range, you'd simply enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query will return a list of all devices, services, and associated information (like open ports, banners, and even HTTP response headers) found within that IP range. You might be surprised at what pops up – maybe an old development server you thought was offline, or a service running on a non-standard port.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 2: Finding Specific Device Types or Software Versions
&lt;/h3&gt;

&lt;p&gt;Perhaps you're concerned about older versions of Apache or Nginx that might still be running. You can combine the IP range with a keyword search for specific banners or technologies. For example, to find all Apache servers on our hypothetical range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24 product:apache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, to get more specific and look for an older, potentially vulnerable version (e.g., Apache 2.2):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24 product:apache/2.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This quickly highlights potential upgrade candidates or misconfigurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 3: Identifying Known Vulnerabilities on Your Infrastructure
&lt;/h3&gt;

&lt;p&gt;ScanSearch also indexes publicly known vulnerabilities (CVEs) associated with identified services. This is incredibly powerful for proactively assessing risk.&lt;/p&gt;

&lt;p&gt;Let's say you want to see if any of your devices on &lt;code&gt;example.com&lt;/code&gt; are running services with known vulnerabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host:example.com has_vulnerability:true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query would return any hosts under &lt;code&gt;example.com&lt;/code&gt; where ScanSearch has identified a service with an associated, known vulnerability. You can then drill down into the results to see the specific CVEs and affected services. This is a critical step in prioritizing patching and mitigation efforts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro-tip:&lt;/strong&gt; You can also search directly for a specific CVE. If you're tracking a new, critical vulnerability, you could search &lt;code&gt;cve:CVE-2023-XXXX&lt;/code&gt; to see if it appears anywhere on your network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case 4: Searching for Default Credentials or Common Misconfigurations
&lt;/h3&gt;

&lt;p&gt;While ScanSearch doesn't actively exploit systems, its indexing of service banners and HTTP responses can sometimes reveal clues about weak configurations. For instance, you might look for common default administrative interfaces or specific keywords that indicate a lack of proper setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24 title:"admin panel" 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, looking for specific HTTP response bodies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24 http.body:"Powered by WordPress"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While not a direct vulnerability, finding an exposed WordPress admin panel on a public IP range you own is certainly something you'd want to investigate further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Simple Queries: Combining Filters
&lt;/h2&gt;

&lt;p&gt;The real power of ScanSearch comes from combining these filters. You can search for specific ports, protocols, HTTP headers, and much more. For example, to find all devices on your range running an SSH server (port 22) that also have a known vulnerability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip:203.0.113.0/24 port:22 has_vulnerability:true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This quickly narrows down your focus to the most critical issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding your external attack surface is a fundamental part of maintaining secure infrastructure. Tools like &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt; provide an invaluable perspective by indexing the entire internet, allowing you to quickly query and identify exposed services, potential misconfigurations, and known vulnerabilities associated with your own public-facing assets.&lt;/p&gt;

&lt;p&gt;Instead of endless manual scans, integrate ScanSearch into your regular security audits. It's a powerful way to catch those overlooked services and proactively address potential risks before they become a problem. Give it a try with your own public IP ranges and domains – you might be surprised at what you find.&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Finding Exposed APIs and Services: A Practical Guide for Developers</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Tue, 28 Jul 2026 04:00:15 +0000</pubDate>
      <link>https://dev.to/devyjones/finding-exposed-apis-and-services-a-practical-guide-for-developers-59g</link>
      <guid>https://dev.to/devyjones/finding-exposed-apis-and-services-a-practical-guide-for-developers-59g</guid>
      <description>&lt;p&gt;As developers, we often build and deploy services, APIs, and applications that interact with the internet. While we strive for secure configurations, misconfigurations or overlooked details can expose sensitive information or create vulnerabilities. Manually auditing every network device and service for potential exposures can be a monumental task, especially in larger infrastructures.&lt;/p&gt;

&lt;p&gt;This article will walk through a practical approach to identify publicly accessible services and potential API endpoints, focusing on how a tool like ScanSearch can assist in this process. We'll look at common scenarios where this kind of reconnaissance is useful and how to interpret the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Unintended Exposure
&lt;/h2&gt;

&lt;p&gt;Imagine you're developing a new microservice. During development, you might expose a debug endpoint or an internal API without proper authentication, assuming it will only be accessible within your private network. However, a misconfigured firewall, an overlooked security group rule, or even a temporary test deployment left running can inadvertently expose these services to the entire internet.&lt;/p&gt;

&lt;p&gt;Similarly, older services or those deployed by other teams might have known vulnerabilities or default credentials that could be exploited if discovered. As developers, understanding what's &lt;em&gt;actually&lt;/em&gt; visible from the outside world is a crucial step in building more secure applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter: ScanSearch for External Visibility
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt; is an internet-wide search engine designed to discover network devices, services, and vulnerabilities. Think of it as a Google for network assets. Instead of crawling websites, it indexes information about what's listening on IP addresses across the globe.&lt;/p&gt;

&lt;p&gt;This makes it incredibly useful for developers who want to understand their external attack surface. You can use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Verify external accessibility:&lt;/strong&gt; Did that API you just deployed accidentally expose port 8080 to the world?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Discover forgotten services:&lt;/strong&gt; Are there old test servers or services running that you're unaware of?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Identify common vulnerabilities:&lt;/strong&gt; ScanSearch indexes known vulnerabilities, which can help you prioritize patching efforts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive into some practical examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Example 1: Finding Exposed API Endpoints by Port
&lt;/h2&gt;

&lt;p&gt;Many APIs run on specific ports. If you suspect an internal API might be exposed, you can search for services listening on that port within a specific IP range or for a particular organization.&lt;/p&gt;

&lt;p&gt;Let's say you're looking for an API that &lt;em&gt;should&lt;/em&gt; only be internal but might be exposed on port &lt;code&gt;8080&lt;/code&gt;. While I can't provide real-time interactive examples here, the conceptual query on ScanSearch would be straightforward.&lt;/p&gt;

&lt;p&gt;You'd typically use a query like &lt;code&gt;port:8080&lt;/code&gt; to find all services listening on port 8080. To narrow it down, you might add an organization name or an IP range. For instance, if your company's public IP range is &lt;code&gt;192.0.2.0/24&lt;/code&gt;, you could construct a more targeted search.&lt;/p&gt;

&lt;p&gt;The results would show IP addresses, associated hostnames (if available), and basic information about the service running on that port. This could quickly reveal if your internal API has an unintended public presence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Example 2: Discovering Services by Banners and Technologies
&lt;/h2&gt;

&lt;p&gt;Services often announce themselves with banners or specific HTTP headers. ScanSearch indexes this information, allowing you to search for particular technologies or patterns.&lt;/p&gt;

&lt;p&gt;Suppose you're developing with Nginx and want to ensure no old, unpatched Nginx instances are exposed from your infrastructure. A general query for &lt;code&gt;product:nginx&lt;/code&gt; might reveal many instances. To make it more relevant to your specific infrastructure, you'd combine this with an IP range or ASN (Autonomous System Number) associated with your organization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Conceptual ScanSearch query for Nginx instances within a specific IP range
product:nginx ip:192.0.2.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you find an unexpected Nginx instance, you can then investigate its configuration and purpose. Often, these findings lead to discovering forgotten development servers or misconfigured staging environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Example 3: Identifying Known Vulnerabilities
&lt;/h2&gt;

&lt;p&gt;One of ScanSearch's powerful features is its ability to index known vulnerabilities. This is invaluable for proactive security. If a new critical vulnerability is announced for a particular service (e.g., a specific version of Apache Struts), you can quickly check if any of your publicly exposed assets might be affected.&lt;/p&gt;

&lt;p&gt;You would search for the specific vulnerability identifier (e.g., a CVE number) or a combination of product and version that's known to be vulnerable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Conceptual ScanSearch query for a specific CVE
cve:CVE-2023-XXXX

# Or for a vulnerable product version
product:apache-struts version:2.5.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can help you prioritize patching efforts and ensure that critical vulnerabilities are addressed promptly, potentially before they are exploited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpreting the Results
&lt;/h2&gt;

&lt;p&gt;When you get results from ScanSearch, it's essential to understand what you're looking at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;IP Address/Hostname:&lt;/strong&gt; This tells you where the service is located.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Port:&lt;/strong&gt; The specific port number the service is listening on.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Product/Service:&lt;/strong&gt; Often, ScanSearch can identify the type of software running (e.g., Apache HTTPD, Nginx, OpenSSH).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Banners/Headers:&lt;/strong&gt; These provide additional details that the service itself advertises.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Vulnerabilities (CVEs):&lt;/strong&gt; If ScanSearch has identified known vulnerabilities associated with the detected service/version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each result is a data point requiring further investigation. A service showing up doesn't automatically mean it's vulnerable, but it does mean it's publicly accessible and should be intentionally so, with appropriate security controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Search: Actionable Steps
&lt;/h2&gt;

&lt;p&gt;Discovering exposed services is only the first step. Here's what you should do next:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Verify:&lt;/strong&gt; Confirm if the service genuinely belongs to your infrastructure and if its public exposure is intended.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Assess Risk:&lt;/strong&gt; If unintended, evaluate the potential impact of its exposure.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Remediate:&lt;/strong&gt; If it shouldn't be public, take steps to secure it. This might involve firewall rules, network ACLs, stronger authentication, or simply shutting it down.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Document:&lt;/strong&gt; Update your documentation to reflect intended exposures and security configurations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Automate:&lt;/strong&gt; Consider integrating such checks into your CI/CD pipeline or regular security audits.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding your external network footprint is a critical aspect of modern application development and security. Tools like ScanSearch provide developers with a practical way to gain this visibility, helping to prevent unintended exposures and proactively identify potential vulnerabilities. By integrating such reconnaissance into your development workflow, you can build more robust and secure applications from the ground up.&lt;/p&gt;

&lt;p&gt;Remember, what you don't know &lt;em&gt;can&lt;/em&gt; hurt you. Regularly checking what the internet sees of your infrastructure is a simple yet powerful security practice.&lt;/p&gt;

&lt;p&gt;For more details and to try it out, visit &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>devtools</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Check SPF, DKIM, and DMARC Records in Python</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Fri, 24 Jul 2026 21:42:16 +0000</pubDate>
      <link>https://dev.to/devyjones/how-to-check-spf-dkim-and-dmarc-records-in-python-143</link>
      <guid>https://dev.to/devyjones/how-to-check-spf-dkim-and-dmarc-records-in-python-143</guid>
      <description>&lt;p&gt;If your app sends email — transactional or marketing — three DNS records decide whether it lands in the inbox or the spam folder: &lt;strong&gt;SPF&lt;/strong&gt;, &lt;strong&gt;DKIM&lt;/strong&gt;, and &lt;strong&gt;DMARC&lt;/strong&gt;. Here's how to look them up and sanity-check them in Python, no third-party API required.&lt;/p&gt;

&lt;p&gt;Install the one dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;dnspython
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SPF: who is allowed to send
&lt;/h2&gt;

&lt;p&gt;SPF lives in a &lt;code&gt;TXT&lt;/code&gt; record on the domain itself and starts with &lt;code&gt;v=spf1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dns.resolver&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_spf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v=spf1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_spf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# v=spf1 ip4:... include:_spf.google.com ~all
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick gotcha worth checking: SPF allows at most &lt;strong&gt;10 DNS-querying mechanisms&lt;/strong&gt; (&lt;code&gt;include&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;mx&lt;/code&gt;, &lt;code&gt;ptr&lt;/code&gt;, &lt;code&gt;exists&lt;/code&gt;, &lt;code&gt;redirect&lt;/code&gt;). Go over and receivers return &lt;code&gt;permerror&lt;/code&gt;, which quietly breaks authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spf_lookup_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;include:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mx:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ptr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exists:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redirect=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_spf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;spf_lookup_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️ SPF exceeds the 10-lookup limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DMARC: the policy that ties it together
&lt;/h2&gt;

&lt;p&gt;DMARC is a &lt;code&gt;TXT&lt;/code&gt; record on the &lt;code&gt;_dmarc.&lt;/code&gt; subdomain and starts with &lt;code&gt;v=DMARC1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_dmarc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_dmarc.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v=DMARC1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NXDOMAIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_dmarc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# {'v': 'DMARC1', 'p': 'reject', 'rua': 'mailto:...'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key field is &lt;code&gt;p&lt;/code&gt;: &lt;code&gt;none&lt;/code&gt; (monitor only), &lt;code&gt;quarantine&lt;/code&gt; (spam folder), or &lt;code&gt;reject&lt;/code&gt; (bounce). If a domain sends real mail but has &lt;code&gt;p=none&lt;/code&gt;, it's not protected against spoofing yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  DKIM: the signature key
&lt;/h2&gt;

&lt;p&gt;DKIM is trickier because you need the &lt;strong&gt;selector&lt;/strong&gt; — a label chosen by the sender that lives at &lt;code&gt;SELECTOR._domainkey.DOMAIN&lt;/code&gt;. There's no way to enumerate selectors from DNS, so you try common ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;COMMON_SELECTORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selector1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selector2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dkim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_dkim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;COMMON_SELECTORS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;._domainkey.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v=DKIM1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;
        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NXDOMAIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoAnswer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;find_dkim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If none of the common selectors resolve, the domain may use a custom one — you'll need to read it from an actual signed message's &lt;code&gt;DKIM-Signature:&lt;/code&gt; header (&lt;code&gt;s=&lt;/code&gt; tag).&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_spf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dmarc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_dmarc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dkim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;find_dkim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  SPF   : &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✓&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✗ missing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  DMARC : &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dmarc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;p&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dmarc&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✗ missing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  DKIM  : &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dkim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dkim&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✗ none of the common selectors&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-domain.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run that against your sending domain before you ship a campaign. Missing DKIM or a &lt;code&gt;p=none&lt;/code&gt; DMARC are the two most common reasons legitimate mail gets filtered after Gmail and Yahoo tightened bulk-sender rules in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you need this at scale
&lt;/h2&gt;

&lt;p&gt;The snippets above are perfect for one domain in a script. If you're validating whole lists, warming a new sending domain, or want the checks plus blacklist and deliverability scoring in one pass, I built these into &lt;a href="https://bulko.io/en/tools/validation/deliverability-report/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=devto-bulko" rel="noopener noreferrer"&gt;Bulko's free email tools&lt;/a&gt; — same logic, no code to run. But for a quick programmatic check, &lt;code&gt;dnspython&lt;/code&gt; is all you need.&lt;/p&gt;

</description>
      <category>python</category>
      <category>email</category>
      <category>dns</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Rotating Residential Proxies in Python: requests, Scrapy &amp; Sticky Sessions</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Fri, 24 Jul 2026 21:42:06 +0000</pubDate>
      <link>https://dev.to/devyjones/rotating-residential-proxies-in-python-requests-scrapy-sticky-sessions-2obp</link>
      <guid>https://dev.to/devyjones/rotating-residential-proxies-in-python-requests-scrapy-sticky-sessions-2obp</guid>
      <description>&lt;p&gt;When you scrape at any real volume, the bottleneck is rarely your code — it's the target site's rate limiting and IP bans. Rotating residential proxies solve this by routing each request through a different real-user IP. Here's how to wire them into &lt;code&gt;requests&lt;/code&gt; and Scrapy in Python, including the sticky-session trick most tutorials skip.&lt;/p&gt;

&lt;h2&gt;
  
  
  The proxy URL format
&lt;/h2&gt;

&lt;p&gt;A residential proxy is just an authenticated HTTP/SOCKS endpoint. With a pool gateway, you target a country and control session behavior through the &lt;strong&gt;username&lt;/strong&gt;, not separate endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://USERNAME_country-us_session-a1b2c3_lifetime-30:PASSWORD@proxy.gproxy.net:1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;country-us&lt;/code&gt; — exit country (ISO code)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session-a1b2c3&lt;/code&gt; — a sticky-session id; reuse it to keep the &lt;strong&gt;same IP&lt;/strong&gt;, change it to rotate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lifetime-30&lt;/code&gt; — how many minutes that session's IP stays fixed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic request through a rotating proxy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PWD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lifetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_country-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_session-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_lifetime-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lifetime&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PWD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@proxy.gproxy.net:1000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# New IP on every call (no session id):
&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.ipify.org?format=json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it in a loop and you'll see a different IP each time — the gateway rotates automatically when no session id is present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sticky sessions: keep one IP across requests
&lt;/h2&gt;

&lt;p&gt;Some flows (login, multi-step checkouts, paginated results behind a cookie) break if your IP changes mid-session. Pin the IP by passing a stable &lt;code&gt;session&lt;/code&gt; id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;        &lt;span class="c1"&gt;# one id for the whole flow
&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lifetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{...})&lt;/span&gt;   &lt;span class="c1"&gt;# same exit IP
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you want a fresh IP, generate a new &lt;code&gt;session&lt;/code&gt; id.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrapy integration
&lt;/h2&gt;

&lt;p&gt;Scrapy reads the proxy off each request's &lt;code&gt;meta&lt;/code&gt;. A tiny middleware rotates a fresh session per request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# middlewares.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ResidentialProxyMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USERNAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;PWD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_country-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_session-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_lifetime-10:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PWD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@proxy.gproxy.net:1000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;DOWNLOADER_MIDDLEWARES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myproject.middlewares.ResidentialProxyMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;request.meta["country"] = "gb"&lt;/code&gt; in a spider to geo-target specific requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few practical notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry on proxy errors.&lt;/strong&gt; Residential IPs occasionally drop; wrap requests with retries (&lt;code&gt;urllib3&lt;/code&gt; &lt;code&gt;Retry&lt;/code&gt;, or Scrapy's &lt;code&gt;RETRY_ENABLED&lt;/code&gt;) so a bad exit doesn't kill a job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect the target.&lt;/strong&gt; Rotating IPs is for spreading legitimate load and geo-testing — not for hammering a site past its terms. Add delays and honor &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SOCKS5&lt;/strong&gt; works the same way on port &lt;code&gt;1002&lt;/code&gt; if you need it (&lt;code&gt;socks5://...&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting credentials
&lt;/h2&gt;

&lt;p&gt;You need a residential pool that supports country targeting and sticky sessions. I use &lt;a href="https://gproxy.net/en/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=devto-gproxy" rel="noopener noreferrer"&gt;GProxy&lt;/a&gt; — the username-based targeting above is exactly its format, and it's usage-based ($0.90/GB) rather than a fixed monthly seat, which suits burst scraping jobs. Grab a key from the dashboard, drop it into &lt;code&gt;USER&lt;/code&gt;/&lt;code&gt;PWD&lt;/code&gt;, and the snippets above run as-is.&lt;/p&gt;

&lt;p&gt;Happy (responsible) scraping.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>tutorial</category>
      <category>proxy</category>
    </item>
    <item>
      <title>Finding Network Device Info Programmatically: A Python Guide with ScanSearch</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Mon, 20 Jul 2026 04:00:16 +0000</pubDate>
      <link>https://dev.to/devyjones/finding-network-device-info-programmatically-a-python-guide-with-scansearch-2hgk</link>
      <guid>https://dev.to/devyjones/finding-network-device-info-programmatically-a-python-guide-with-scansearch-2hgk</guid>
      <description>&lt;p&gt;Ever needed to quickly check the exposure of a specific type of network device, or verify if a known vulnerability is present across your external-facing infrastructure? Manually probing IPs can be a tedious and time-consuming process. What if you could query the entire internet for devices, services, and even known vulnerabilities, all from a simple API call?&lt;/p&gt;

&lt;p&gt;This article will walk you through using Python to programmatically query ScanSearch, an internet-wide search engine for network devices, services, and vulnerabilities. We'll focus on practical examples that demonstrate how to find specific device types, filter results, and extract useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Manual Network Reconnaissance is Slow
&lt;/h2&gt;

&lt;p&gt;Imagine you're a security analyst, and a new vulnerability (CVE-2023-XXXX) has just been announced, affecting a specific version of a network-attached storage (NAS) device. Your immediate thought is: &lt;em&gt;Are any of our external-facing devices vulnerable? And what about other organizations I'm responsible for monitoring?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, you'd start with IP ranges, port scans, and banner grabbing – a process that's not only slow but also might miss devices outside your known ranges. This is where a tool like ScanSearch comes in handy, providing a fast way to query pre-indexed internet data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with ScanSearch
&lt;/h2&gt;

&lt;p&gt;ScanSearch (&lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;https://scansearch.net&lt;/a&gt;) offers an API that allows you to perform these queries programmatically. While the specifics of API key acquisition aren't covered here, assume you have an API key ready for the following examples.&lt;/p&gt;

&lt;p&gt;We'll use Python's &lt;code&gt;requests&lt;/code&gt; library to interact with the ScanSearch API. First, let's set up our basic request structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR_SCANSEARCH_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your actual API key
&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.scansearch.net/v1/search&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;query_scansearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Raise an exception for HTTP errors
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP error occurred: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Response body: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;query_scansearch&lt;/code&gt; function takes a &lt;code&gt;query_string&lt;/code&gt;, &lt;code&gt;page&lt;/code&gt;, and &lt;code&gt;limit&lt;/code&gt; and handles the API call, including basic error checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 1: Finding Specific Device Types
&lt;/h2&gt;

&lt;p&gt;Let's say we want to find all publicly exposed instances of &lt;code&gt;nginx&lt;/code&gt; web servers. The &lt;code&gt;query_string&lt;/code&gt; for ScanSearch is quite powerful and supports various filters. A simple query for &lt;code&gt;nginx&lt;/code&gt; will return services identified as such.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Searching for Nginx Servers ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nginx_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query_scansearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nginx_results&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nginx_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nginx_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Nginx instances (first &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nginx_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; results shown):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nginx_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="c1"&gt;# Print first 5 for brevity
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. IP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Port: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Banners: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;banners&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;banners&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# You can inspect the full result object for more details
&lt;/span&gt;        &lt;span class="c1"&gt;# print(json.dumps(result, indent=2))
&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No Nginx results found or an error occurred.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet sends a query for &lt;code&gt;nginx&lt;/code&gt; and then iterates through the results, printing out the IP, port, and the first banner string found. ScanSearch's data includes a lot more, so feel free to inspect the &lt;code&gt;result&lt;/code&gt; object more deeply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 2: Filtering by Port and Service
&lt;/h2&gt;

&lt;p&gt;What if you're interested in &lt;code&gt;SSH&lt;/code&gt; services running on non-standard ports, say port &lt;code&gt;2222&lt;/code&gt;? ScanSearch allows combining search terms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Searching for SSH on Port 2222 ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ssh_port_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query_scansearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service:ssh port:2222&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ssh_port_results&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ssh_port_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssh_port_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; SSH services on port 2222:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssh_port_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. IP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Port: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Country: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;country_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No SSH on port 2222 results found or an error occurred.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;service:ssh&lt;/code&gt; specifically targets services identified as SSH, and &lt;code&gt;port:2222&lt;/code&gt; narrows it down to that specific port. The results also contain geographical information under &lt;code&gt;location&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 3: Searching for Vulnerabilities (CVEs)
&lt;/h2&gt;

&lt;p&gt;ScanSearch also indexes known vulnerabilities. This is incredibly powerful for security auditing. Let's look for devices exposed to a hypothetical (or real, if you substitute a current one) CVE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Searching for Devices with CVE-2021-44228 (Log4Shell) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Note: ScanSearch indexes vulnerabilities; results will show devices associated with them.
&lt;/span&gt;&lt;span class="n"&gt;log4shell_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query_scansearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cve:CVE-2021-44228&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log4shell_results&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log4shell_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log4shell_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; devices potentially affected by CVE-2021-44228:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log4shell_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. IP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Port: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Service: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# For vulnerability queries, you might want to inspect the 'vulnerabilities' key in the full result.
&lt;/span&gt;        &lt;span class="c1"&gt;# print(json.dumps(result.get('vulnerabilities', []), indent=2))
&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No CVE-2021-44228 results found or an error occurred.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example demonstrates how to query for a specific CVE ID. ScanSearch's data includes an association between identified services/devices and known vulnerabilities, which can be invaluable for threat hunting and patch validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations and Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;API Key Security:&lt;/strong&gt; Never hardcode your API key directly into public repositories. Use environment variables or a secure configuration management system.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rate Limiting:&lt;/strong&gt; Be mindful of API rate limits. Implement delays or back-off strategies if you're making a large number of requests.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Result Paging:&lt;/strong&gt; The &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; parameters are crucial for fetching all results when your query returns more than the default limit. Implement a loop to fetch subsequent pages.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data Structure:&lt;/strong&gt; The exact structure of the results can vary slightly depending on the query. Always inspect the JSON response to understand the available fields.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Query Syntax:&lt;/strong&gt; Familiarize yourself with ScanSearch's query syntax for more precise searches. This often includes field-specific searches (e.g., &lt;code&gt;product:apache&lt;/code&gt;, &lt;code&gt;os:linux&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Programmatically querying an internet-wide search engine like ScanSearch significantly speeds up network reconnaissance, security auditing, and threat intelligence gathering. By leveraging its API with a few lines of Python, developers and security professionals can quickly gain insights into internet-facing infrastructure, identify specific device types, services, and even potential vulnerabilities without resorting to slow, active scanning.&lt;/p&gt;

&lt;p&gt;Explore the ScanSearch website (&lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;https://scansearch.net&lt;/a&gt;) to learn more about its capabilities and detailed query options.&lt;/p&gt;

</description>
      <category>python</category>
      <category>networking</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Finding Exposed Services &amp; Vulnerabilities: A Developer's Quick Guide with ScanSearch</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Sun, 12 Jul 2026 04:00:14 +0000</pubDate>
      <link>https://dev.to/devyjones/finding-exposed-services-vulnerabilities-a-developers-quick-guide-with-scansearch-hbb</link>
      <guid>https://dev.to/devyjones/finding-exposed-services-vulnerabilities-a-developers-quick-guide-with-scansearch-hbb</guid>
      <description>&lt;p&gt;Ever been in a situation where you need to quickly identify what services are exposed on a particular network range, or even across the internet? Maybe you're auditing your own infrastructure, or perhaps performing some ethical hacking. Traditional methods can involve port scanning specific IPs, which is fine for small scopes but quickly becomes impractical for broader reconnaissance.&lt;/p&gt;

&lt;p&gt;This article will walk you through a practical approach to quickly discover network devices, their services, and even potential vulnerabilities using a powerful internet-wide search engine called &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt;. We'll focus on common developer use cases, showing you how to formulate queries to get actionable insights without running a single &lt;code&gt;nmap&lt;/code&gt; command yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Blind Spots and Manual Scanning
&lt;/h2&gt;

&lt;p&gt;Imagine you're responsible for securing a fleet of IoT devices. You've configured them, but how do you verify they aren't accidentally exposing a web interface or an SSH port to the public internet? Or perhaps you're developing a new service and want to see what similar services are exposing. Manually scanning vast IP ranges is time-consuming and often requires dedicated infrastructure.&lt;/p&gt;

&lt;p&gt;This is where an internet-wide search engine comes in. Instead of actively scanning, it aggregates data from continuous scans, allowing you to query this massive dataset instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing ScanSearch: A Practical Tool
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch&lt;/a&gt; is essentially Google for network devices. It continuously scans the internet and indexes information about network devices, the services they run, and known vulnerabilities. This allows you to search for specific criteria, like devices running a particular version of Apache, or devices with an open RDP port.&lt;/p&gt;

&lt;p&gt;Let's dive into some practical examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 1: Discovering Exposed Web Servers
&lt;/h2&gt;

&lt;p&gt;Let's say you want to find all devices on the internet running an Apache web server on port 80. ScanSearch's query language is fairly intuitive.&lt;/p&gt;

&lt;p&gt;You'd use a query like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service.name:apache AND port:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query tells ScanSearch to look for devices where the service name contains "apache" and the port is 80. The results would show you IP addresses, along with other details about the discovered service. This is incredibly useful for understanding the landscape of publicly accessible web servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 2: Identifying Devices with Specific Vulnerabilities
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features for developers and security professionals is the ability to search for devices with known vulnerabilities. ScanSearch indexes information about vulnerabilities associated with discovered services.&lt;/p&gt;

&lt;p&gt;Let's say you're concerned about devices running services known to be vulnerable to a specific CVE (Common Vulnerabilities and Exposures) ID, for example, CVE-2021-44228 (Log4Shell).&lt;/p&gt;

&lt;p&gt;While ScanSearch doesn't let you query by CVE ID directly in the current iteration, you can achieve similar results by searching for specific service versions or patterns often associated with that vulnerability. For instance, if Log4Shell primarily affected specific versions of Java applications exposing certain services, you could try to narrow it down.&lt;/p&gt;

&lt;p&gt;For a more general approach to finding vulnerable services, you might search for a service known to have many common vulnerabilities, like certain versions of an older database or web server.&lt;/p&gt;

&lt;p&gt;Let's try to find devices running an older, potentially vulnerable version of &lt;strong&gt;nginx&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service.name:nginx AND service.version:"1.14.0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: Always check the specific version information against public CVE databases like NVD to determine actual vulnerability status.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This query helps you quickly pinpoint instances that might need immediate patching or further investigation. It's a great first step in a vulnerability management workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 3: Finding Open RDP Ports for Auditing
&lt;/h2&gt;

&lt;p&gt;Remote Desktop Protocol (RDP) is a common target for attackers, and accidentally exposing it to the internet is a significant security risk. You can quickly check for publicly exposed RDP services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;port:3389 AND protocol:rdp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple query will list all devices with an open RDP port (3389). If you manage a large network, running this query periodically can act as a quick check against misconfigurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for Effective Querying
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Be Specific:&lt;/strong&gt; The more specific your query, the more relevant your results will be. Use &lt;code&gt;AND&lt;/code&gt; and &lt;code&gt;OR&lt;/code&gt; operators to combine terms.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Explore Fields:&lt;/strong&gt; ScanSearch offers various fields you can query, such as &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;port&lt;/code&gt;, &lt;code&gt;service.name&lt;/code&gt;, &lt;code&gt;service.version&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;, etc. Experiment with these to narrow down your searches.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Regular Checks:&lt;/strong&gt; For ongoing security, integrate ScanSearch queries into your regular audit routine. It's a fantastic way to passively monitor your public-facing assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Beyond the Basics
&lt;/h2&gt;

&lt;p&gt;While we've only scratched the surface, ScanSearch's capabilities extend to discovering a wide array of network services, like databases (MongoDB, MySQL), industrial control systems (SCADA), and various IoT devices. The key is to understand what you're looking for and formulate your query accordingly.&lt;/p&gt;

&lt;p&gt;For more detailed query syntax and available fields, I recommend checking out the &lt;a href="https://scansearch.net" rel="noopener noreferrer"&gt;ScanSearch website&lt;/a&gt; directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ScanSearch offers a powerful and efficient way for developers, system administrators, and security professionals to gain insights into the internet's observable attack surface. By leveraging its internet-wide scanning data, you can quickly identify exposed services, pinpoint potential vulnerabilities, and audit your own infrastructure without the need for active scanning. It's a valuable tool in any developer's security toolkit for proactive defense and reconnaissance.&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>devsecops</category>
      <category>tools</category>
    </item>
    <item>
      <title>Catch expiring TLS certs and weak security headers in CI — with one small CLI</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Wed, 08 Jul 2026 22:29:05 +0000</pubDate>
      <link>https://dev.to/devyjones/catch-expiring-tls-certs-and-weak-security-headers-in-ci-with-one-small-cli-3fe</link>
      <guid>https://dev.to/devyjones/catch-expiring-tls-certs-and-weak-security-headers-in-ci-with-one-small-cli-3fe</guid>
      <description>&lt;p&gt;Your CI proves the app builds, the tests pass, and maybe that there are no known-vulnerable dependencies. Then it ships — behind a TLS certificate that will quietly expire in three weeks, serving responses with a security-headers grade of F.&lt;/p&gt;

&lt;p&gt;Nobody notices until a browser does.&lt;/p&gt;

&lt;p&gt;These two failure modes — an expiring certificate and weak HTTP security headers — are cheap to check and easy to forget. Here's a small, dependency-light CLI that checks them (plus DNS posture and subdomains) from your terminal, and a one-line GitHub Action that fails the build before your users find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"secably[dns]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core checks have &lt;strong&gt;zero dependencies&lt;/strong&gt;; the optional &lt;code&gt;[dns]&lt;/code&gt; extra pulls in &lt;code&gt;dnspython&lt;/code&gt; for SPF/DMARC/DNSSEC. Everything runs locally — nothing about your domain is sent to a third party.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check a TLS certificate
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secably ssl example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TLS — github.com:443
--------------------
  Subject          github.com
  Issuer           Sectigo Public Server Authentication CA DV E36
  Valid until      2026-09-30T23:59:59+00:00  (84 days left)
  Protocol         TLSv1.3
  Cipher           TLS_AES_128_GCM_SHA256
  Trusted          yes
  SAN              github.com, www.github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It completes the handshake, reads the leaf certificate, and reports the issuer, validity window, days remaining and negotiated protocol. It also flags the things you actually care about: an expired or not-yet-valid certificate, a chain that doesn't verify, or a server still negotiating an obsolete protocol like TLS 1.0.&lt;/p&gt;

&lt;p&gt;One implementation detail worth knowing if you build something similar: &lt;code&gt;ssl.getpeercert()&lt;/code&gt; returns an empty dict unless the certificate &lt;em&gt;verifies&lt;/em&gt;, so you can't use it to inspect an already-expired or self-signed cert — exactly the case you want to catch. The trick is to pull the certificate in DER form with &lt;code&gt;getpeercert(binary_form=True)&lt;/code&gt; and decode that instead, then run verification separately just to set a "trusted" flag.&lt;/p&gt;

&lt;p&gt;The CI-friendly part is the exit code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secably ssl example.com &lt;span class="nt"&gt;--fail-if-expires&lt;/span&gt; 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exits non-zero if the certificate expires within 21 days. Drop it in a scheduled job and you get weeks of warning instead of an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grade your security headers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secably headers https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security headers — https://example.com
--------------------------------------
  Grade: A  (90/100, HTTP 200)
  Present:
  • strict-transport-security
  • content-security-policy
  • x-frame-options
  • x-content-type-options
  • referrer-policy
  Missing:
  ✗ permissions-policy — Set a Permissions-Policy to restrict powerful browser features.
  Info leaks:
  ! server: nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It scores the response A–F across the six headers that matter most — HSTS, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and Permissions-Policy — and calls out headers that leak your stack, like a verbose &lt;code&gt;Server&lt;/code&gt; or &lt;code&gt;X-Powered-By&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Same CI story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secably headers https://example.com &lt;span class="nt"&gt;--fail-below&lt;/span&gt; B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fails the build if the grade drops below B.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: DNS posture and subdomains
&lt;/h2&gt;

&lt;p&gt;Two more checks that are handy for recon and hygiene:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;secably dns example.com          &lt;span class="c"&gt;# A/AAAA/MX/NS/TXT + SPF / DMARC / DNSSEC&lt;/span&gt;
secably subdomains example.com   &lt;span class="c"&gt;# passive discovery via Certificate Transparency&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dns&lt;/code&gt; tells you whether your domain can be spoofed (no SPF, no DMARC, no DNSSEC — the CLI warns on each). &lt;code&gt;subdomains&lt;/code&gt; queries public Certificate Transparency logs — every name a CA has ever issued a certificate for — so there's no brute force and no traffic to the target. Add &lt;code&gt;--resolve&lt;/code&gt; to get IPs, and &lt;code&gt;--json&lt;/code&gt; to any command for a machine-readable object you can pipe into &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it in CI
&lt;/h2&gt;

&lt;p&gt;You can wire the CLI into any pipeline, but on GitHub there's a ready-made action so you don't have to script the exit-code handling yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security check&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;6&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;   &lt;span class="c1"&gt;# every morning&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secably&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secably/secably-security-check@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-domain.com&lt;/span&gt;
          &lt;span class="na"&gt;fail-if-expires&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;21&lt;/span&gt;
          &lt;span class="na"&gt;headers-min-grade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole thing. The job runs entirely inside your runner, prints a job summary, and turns red — with a GitHub error annotation — the moment the certificate is close to expiry or the headers slip below your bar. Running it on a daily &lt;code&gt;cron&lt;/code&gt; (not just on push) is the point: certificates expire on the calendar, not on your commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it runs
&lt;/h2&gt;

&lt;p&gt;To be upfront about it: &lt;code&gt;ssl&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt;, &lt;code&gt;dns&lt;/code&gt; and &lt;code&gt;subdomains&lt;/code&gt; all run on your machine with no account and no rate limit. There's also a &lt;code&gt;secably scan&lt;/code&gt; command for a deeper server-side website/port scan, which uses a free API key — but the four checks above need nothing.&lt;/p&gt;

&lt;p&gt;A CI run is a snapshot at deploy time, though. Certificates still expire, new subdomains still appear, and ports still open up between deploys. If you want these same checks running continuously with alerts, that's what the hosted side at &lt;a href="https://secably.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=secably-cli" rel="noopener noreferrer"&gt;Secably&lt;/a&gt; does — but the CLI and the Action are open source and stand on their own.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI: &lt;a href="https://github.com/Secably/secably-cli" rel="noopener noreferrer"&gt;github.com/Secably/secably-cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Action: &lt;a href="https://github.com/Secably/secably-security-check" rel="noopener noreferrer"&gt;github.com/Secably/secably-security-check&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you add it to a pipeline, I'd genuinely like to hear which check caught something first — mine was an expiring cert on a staging box nobody was watching.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>python</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>The Cheapest Dedicated Servers in 2026: A Price-Checked Shortlist</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Mon, 06 Jul 2026 11:28:16 +0000</pubDate>
      <link>https://dev.to/devyjones/the-cheapest-dedicated-servers-in-2026-a-price-checked-shortlist-3803</link>
      <guid>https://dev.to/devyjones/the-cheapest-dedicated-servers-in-2026-a-price-checked-shortlist-3803</guid>
      <description>&lt;p&gt;If you have ever tried to price a bare-metal server, you know the game: a big "&lt;strong&gt;from $X/mo&lt;/strong&gt;" on the landing page, then the actually-orderable configs cost two or three times that, half of them are out of stock, and the cheap one ships "in 3–5 business days." This is a price-checked shortlist of where to rent a genuinely cheap dedicated server in 2026 — who each provider is for, roughly where pricing starts, and the catch.&lt;/p&gt;

&lt;p&gt;I pulled the numbers for the last entry from a live, daily-refreshed dataset (linked at the bottom) so you can verify them instead of trusting a screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "cheap" should actually mean
&lt;/h2&gt;

&lt;p&gt;Before the list, four things that separate a real deal from bait:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The price you see is the price you pay.&lt;/strong&gt; Watch for setup/install fees and "from" pricing that no in-stock config matches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In stock now, not "provisioning in 5 days."&lt;/strong&gt; A $16 box you can't deploy today isn't a $16 box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bandwidth and traffic caps.&lt;/strong&gt; Unmetered 1 Gbps and a 30 TB cap are very different economics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location.&lt;/strong&gt; A server two continents from your users is cheap and useless.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The shortlist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Kimsufi (OVH budget brand) — legacy boxes, rock-bottom
&lt;/h3&gt;

&lt;p&gt;OVH's budget label recycles older Atom/Xeon hardware at the lowest headline prices in the market. Great for a cheap always-on box, a seedbox, or a lab. Catch: stock is famously thin, hardware is old, and support is minimal.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Scaleway Dedibox — cheap French metal
&lt;/h3&gt;

&lt;p&gt;Long-running budget bare-metal out of Paris/Amsterdam, entry configs among the cheapest in the EU. Good if you specifically want France/NL and Scaleway's ecosystem. Catch: entry SKUs sell out and the cheapest tiers use aging CPUs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Hetzner (server auction) — best price/performance in the EU
&lt;/h3&gt;

&lt;p&gt;Not the lowest sticker, but the auction floor gets you modern Ryzen/EPYC with lots of RAM for what budget hosts charge for an Atom. The default answer for "cheap &lt;em&gt;and&lt;/em&gt; fast" in Germany/Finland. Catch: auction inventory rotates, and it's EU-only locations.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Contabo — cheap high-spec, oversubscribed
&lt;/h3&gt;

&lt;p&gt;Known for a lot of cores/RAM/disk per dollar. Fine for storage or batch workloads where you don't need guaranteed peak performance. Catch: reputation for oversubscription and variable I/O; read recent reviews for your region.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Leaseweb / enterprise carriers — not cheap, but predictable
&lt;/h3&gt;

&lt;p&gt;When uptime SLAs and DDoS protection matter more than the invoice, the enterprise carriers start higher but deliver consistency. Catch: you'll pay for it, and it's overkill for a hobby box.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Valebyte — cheap &lt;em&gt;and&lt;/em&gt; in-stock across 20 countries
&lt;/h3&gt;

&lt;p&gt;Aggregates in-stock bare-metal across a wide provider network, so the differentiator is &lt;strong&gt;breadth + honesty&lt;/strong&gt;: ~800 servers deployable now across 20 countries, prices are final (no "from $X" bait), and the whole catalog is published as a machine-readable dataset. Entry is &lt;strong&gt;$16/mo&lt;/strong&gt; (Atom-class in France/NL), with modern Ryzen from ~$72 and EPYC from ~$160. Catch: it's an aggregator, so the cheapest tiers are still older CPUs — same as everyone else at $16.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheapest by region (live figures)
&lt;/h2&gt;

&lt;p&gt;Where the boxes actually are and what the cheapest one costs, at the time of writing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;In stock&lt;/th&gt;
&lt;th&gt;Cheapest&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;France&lt;/td&gt;
&lt;td&gt;241&lt;/td&gt;
&lt;td&gt;$16/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netherlands&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;$16/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;td&gt;$59/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;$41/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poland&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;$54/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;$42/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canada&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;$40/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;$40/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;France and the Netherlands are where the floor is; if you don't need a specific country, that's where the cheapest usable metal lives in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually verify prices
&lt;/h2&gt;

&lt;p&gt;The single most useful habit: &lt;strong&gt;check a machine-readable price list instead of marketing pages.&lt;/strong&gt; The regional numbers above come from an open dataset that refreshes daily — &lt;a href="https://github.com/ValebyteServers/dedicated-server-prices" rel="noopener noreferrer"&gt;dedicated-server-prices on GitHub&lt;/a&gt; — with CSV/JSON of specs, location, monthly price and order URL for every in-stock server. Clone it, sort by price, done. No "from $X," no stale screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Lowest sticker, don't care about hardware age → &lt;strong&gt;Kimsufi / Scaleway Dedibox&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Cheap &lt;em&gt;and&lt;/em&gt; fast in the EU → &lt;strong&gt;Hetzner auction&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Lots of cores/RAM per dollar → &lt;strong&gt;Contabo&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;SLA and DDoS matter more than price → &lt;strong&gt;Leaseweb&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Cheap, in-stock now, specific country → &lt;strong&gt;&lt;a href="https://valebyte.com/en/dedicated-servers/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cheapest-dedicated-2026" rel="noopener noreferrer"&gt;Valebyte&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever you pick, sort by &lt;em&gt;in-stock final price in your region&lt;/em&gt; — not the number on the hero banner.&lt;/p&gt;

</description>
      <category>hosting</category>
      <category>devops</category>
      <category>sysadmin</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Bypassing Telegram Blocks: How MTProto FakeTLS Proxies Actually Work</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Sat, 04 Jul 2026 12:48:41 +0000</pubDate>
      <link>https://dev.to/devyjones/bypassing-telegram-blocks-how-mtproto-faketls-proxies-actually-work-5c53</link>
      <guid>https://dev.to/devyjones/bypassing-telegram-blocks-how-mtproto-faketls-proxies-actually-work-5c53</guid>
      <description>&lt;p&gt;When an ISP or national firewall throttles or blocks Telegram, the usual advice is "use a VPN." But there's a lighter, Telegram-specific tool that is often more resilient and far simpler for end users: the &lt;strong&gt;MTProto proxy&lt;/strong&gt; with &lt;strong&gt;FakeTLS&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  DPI vs. Telegram
&lt;/h2&gt;

&lt;p&gt;Deep Packet Inspection classifies traffic by its protocol fingerprint. A plain MTProto stream or a naive proxy is easy to spot and drop. The trick is to make the connection look like something completely ordinary.&lt;/p&gt;

&lt;h2&gt;
  
  
  How FakeTLS works
&lt;/h2&gt;

&lt;p&gt;MTProto is Telegram's own transport protocol. &lt;strong&gt;FakeTLS&lt;/strong&gt; wraps it so the wire looks like a normal TLS 1.3 handshake to a real website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client sends a TLS &lt;code&gt;ClientHello&lt;/code&gt; with the &lt;strong&gt;SNI of a popular domain&lt;/strong&gt; (e.g. &lt;code&gt;www.microsoft.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Authentication is a per-user secret verified via an &lt;strong&gt;HMAC computed over the ClientHello&lt;/strong&gt; (with the 32-byte random field acting as the digest).&lt;/li&gt;
&lt;li&gt;If the HMAC is invalid (a censor's active probe), the proxy transparently &lt;strong&gt;proxies the prober to the real fronting domain&lt;/strong&gt; — so an attacker who pokes the port just gets Microsoft's real certificate back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To a censor, a real client's flow is indistinguishable from an HTTPS visit to that domain. That's what keeps it working where generic proxies are already blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  A failure mode nobody documents: clock skew
&lt;/h2&gt;

&lt;p&gt;Here's a real-world gotcha we hit in production. FakeTLS embeds a &lt;strong&gt;timestamp&lt;/strong&gt; for anti-replay. If the &lt;strong&gt;proxy host's clock drifts&lt;/strong&gt; more than a couple of minutes, every legitimate client handshake is rejected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;invalid faketls client hello: incorrect timestamp. got=..., now=..., diff=2m54s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nasty part: the proxy still accepts TCP, still answers TLS, still passes an &lt;code&gt;openssl s_client&lt;/code&gt; masquerade check — it looks perfectly alive. It just silently &lt;strong&gt;falls back to domain-fronting instead of relaying&lt;/strong&gt;, so &lt;em&gt;every&lt;/em&gt; user on that host is broken at once. The fix is boring but essential: &lt;strong&gt;run NTP (chrony) on every proxy host.&lt;/strong&gt; We shipped a box without it and spent an afternoon chasing "the proxy is down" tickets that were really a 3-minute clock drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  MTProto proxy vs VPN
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;MTProto proxy&lt;/th&gt;
&lt;th&gt;VPN&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Telegram only&lt;/td&gt;
&lt;td&gt;whole device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;one tap on a link&lt;/td&gt;
&lt;td&gt;install + configure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detectability&lt;/td&gt;
&lt;td&gt;masked as TLS&lt;/td&gt;
&lt;td&gt;often fingerprinted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battery/speed&lt;/td&gt;
&lt;td&gt;negligible / fast&lt;/td&gt;
&lt;td&gt;heavier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For unblocking Telegram specifically, MTProto is usually the better tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Notes + setup (open): &lt;a href="https://github.com/blureshot/free-telegram-proxy" rel="noopener noreferrer"&gt;https://github.com/blureshot/free-telegram-proxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A free, working MTProto proxy (24h, no signup, one-tap): &lt;a href="https://t.me/NotifyGproxyBot?start=devto" rel="noopener noreferrer"&gt;https://t.me/NotifyGproxyBot?start=devto&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full disclosure: that bot is the free tier of a proxy service I work on (GProxy). But the mechanics above are protocol-level and vendor-neutral — any FakeTLS MTProto proxy (mtg, mtprotoproxy) works the same way, and the clock-skew lesson applies to all of them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Need production-grade rotating &amp;amp; residential proxies with real uptime? Try *&lt;/em&gt;&lt;a href="https://gproxy.net/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=devto-gproxy" rel="noopener noreferrer"&gt;gproxy.net&lt;/a&gt;*&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>telegram</category>
      <category>networking</category>
      <category>privacy</category>
      <category>security</category>
    </item>
    <item>
      <title>A free proxy list that's actually checked (HTTP/SOCKS4/SOCKS5, updated every 30 min)</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:38:33 +0000</pubDate>
      <link>https://dev.to/devyjones/a-free-proxy-list-thats-actually-checked-httpsocks4socks5-updated-every-30-min-1k6k</link>
      <guid>https://dev.to/devyjones/a-free-proxy-list-thats-actually-checked-httpsocks4socks5-updated-every-30-min-1k6k</guid>
      <description>&lt;p&gt;Every "free proxy list" you find is the same story: a giant wall of &lt;code&gt;ip:port&lt;/code&gt; lines, 90% of them already dead, no idea which are HTTP or SOCKS, no idea where they exit. You paste 500 into your scraper and three of them work.&lt;/p&gt;

&lt;p&gt;So I put up a small one that's &lt;strong&gt;actually checked&lt;/strong&gt;: &lt;a href="https://github.com/gproxynet/free-proxy-list" rel="noopener noreferrer"&gt;gproxynet/free-proxy-list&lt;/a&gt;. A checker validates each proxy, tags it with protocol, country and latency, and the repo is regenerated &lt;strong&gt;every 30 minutes&lt;/strong&gt;. It's a rotating sample, not a 50k dump — the point is that what's in it was alive minutes ago, not last month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab it
&lt;/h2&gt;

&lt;p&gt;Plain text, one &lt;code&gt;ip:port&lt;/code&gt; per line, split by protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/all.txt
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/socks5.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the structured version with protocol, country and latency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/proxies.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"103.156.224.66:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"latency_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4225&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"checked_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-07-04T09:34:16Z"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"185.26.180.180:80"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"NL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"latency_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;340&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"checked_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-07-04T09:41:02Z"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use it in Python
&lt;/h2&gt;

&lt;p&gt;Filter to the protocol and speed you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/proxies.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;fast_socks5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protocol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;socks5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fast_socks5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fast SOCKS5 proxies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're rotating them across a scraper, feed the list straight into a pool. I use &lt;a href="https://github.com/gproxynet/proxyspin" rel="noopener noreferrer"&gt;proxyspin&lt;/a&gt; (a small rotating pool with health tracking + ban detection), which can load a URL directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin.requests_adapter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RotatingSession&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/all.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RotatingSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# dead ones get benched automatically
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;These are &lt;strong&gt;public proxies&lt;/strong&gt;. They're shared by strangers, they're slow, they die within minutes to hours, and you should never send anything sensitive through them. A checked list saves you the "which of these 500 is alive" step — it does not make public proxies reliable. They're great for testing, learning, one-off checks and throwaway requests.&lt;/p&gt;

&lt;p&gt;The moment you need proxies that &lt;em&gt;stay&lt;/em&gt; up — real scraping, account work, ad verification — you'll want dedicated ones (residential, mobile or datacenter) instead of public ones. But if a free, checked, auto-updating list is what you need today, it's there:&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;&lt;a href="https://github.com/gproxynet/free-proxy-list" rel="noopener noreferrer"&gt;github.com/gproxynet/free-proxy-list&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bookmark the raw URL, poll it every so often, and you've always got a fresh handful to work with.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Need production-grade rotating &amp;amp; residential proxies with real uptime? Try *&lt;/em&gt;&lt;a href="https://gproxy.net/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=devto-gproxy" rel="noopener noreferrer"&gt;gproxy.net&lt;/a&gt;*&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>proxy</category>
      <category>python</category>
      <category>webscraping</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Rotating proxies for Scrapy, Playwright and requests — one small library</title>
      <dc:creator>Billy</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:28:29 +0000</pubDate>
      <link>https://dev.to/devyjones/rotating-proxies-for-scrapy-playwright-and-requests-one-small-library-2j97</link>
      <guid>https://dev.to/devyjones/rotating-proxies-for-scrapy-playwright-and-requests-one-small-library-2j97</guid>
      <description>&lt;p&gt;If you scrape anything at scale, you know the drill: proxies die, sites start returning 403/429, and your run grinds to a halt. The classic Scrapy answer, &lt;a href="https://github.com/TeamHG-Memex/scrapy-rotating-proxies" rel="noopener noreferrer"&gt;&lt;code&gt;scrapy-rotating-proxies&lt;/code&gt;&lt;/a&gt;, hasn't seen a real update in years — and it's Scrapy-only, so the moment you reach for Playwright or plain &lt;code&gt;requests&lt;/code&gt;, you're rebuilding rotation from scratch.&lt;/p&gt;

&lt;p&gt;I wanted &lt;strong&gt;one&lt;/strong&gt; small proxy pool I could share across all three. So I wrote &lt;a href="https://github.com/gproxynet/proxyspin" rel="noopener noreferrer"&gt;&lt;code&gt;proxyspin&lt;/code&gt;&lt;/a&gt;: a rotating pool with health tracking, ban detection and sticky sessions, and thin adapters for Scrapy, Playwright and &lt;code&gt;requests&lt;/code&gt;. Zero required dependencies, pure standard library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;proxyspin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The pool
&lt;/h2&gt;

&lt;p&gt;Everything is built around one object. Load proxies from a list, a file, or a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxies.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;round_robin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# or inline / from your provider's export endpoint:
&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@gate1.example.com:8000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.0.0.2:8000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/api/my-list.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="c1"&gt;# -&amp;gt; Proxy; proxy.url is ready to use
&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_failed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# bench it after repeated failures
&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# reset its failure streak
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It parses every common list format — &lt;code&gt;host:port&lt;/code&gt;, &lt;code&gt;host:port:user:pass&lt;/code&gt;, &lt;code&gt;user:pass@host:port&lt;/code&gt;, &lt;code&gt;scheme://user:pass@host:port&lt;/code&gt; — for HTTP, HTTPS, SOCKS4 and SOCKS5.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rotation strategies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;round_robin&lt;/code&gt; — cycle through healthy proxies in order (default)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random&lt;/code&gt; — pick one at random&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sticky&lt;/code&gt; — keep returning the &lt;strong&gt;same&lt;/strong&gt; proxy for a given key (a target domain, an account id, a worker name) until it goes unhealthy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The health model
&lt;/h3&gt;

&lt;p&gt;Every proxy starts healthy. &lt;code&gt;mark_failed&lt;/code&gt; bumps its failure streak; when the streak hits &lt;code&gt;max_failures&lt;/code&gt; (default 2) the proxy is benched with &lt;strong&gt;exponential backoff&lt;/strong&gt; (&lt;code&gt;cooldown * 2**overshoot&lt;/code&gt;, base 60s, capped at 1h), then automatically rejoins rotation. &lt;code&gt;mark_ok&lt;/code&gt; resets the streak. So dead proxies quietly drop out and recover on their own — you never manually prune the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrapy
&lt;/h2&gt;

&lt;p&gt;Enable the middleware and point it at your proxies. Ban detection and retry-through-the-next-proxy are automatic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;DOWNLOADER_MIDDLEWARES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxyspin.scrapy_middleware.ProxySpinMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;610&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;PROXYSPIN_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxies.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PROXYSPIN_STRATEGY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticky&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# one proxy per target host
&lt;/span&gt;&lt;span class="n"&gt;PROXYSPIN_BAN_CODES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;    &lt;span class="c1"&gt;# these responses rotate the proxy
&lt;/span&gt;&lt;span class="n"&gt;PROXYSPIN_MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any &lt;code&gt;403&lt;/code&gt;/&lt;code&gt;429&lt;/code&gt; (configurable) marks the proxy as failed and retries the request through the next healthy one. No spider code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright takes a proxy per browser context, which is the natural rotation unit — great for one-IP-per-account flows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin.playwright_helper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;proxy_settings&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxies.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticky&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;proxy_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="c1"&gt;# each account keeps its own IP for the whole session
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  requests
&lt;/h2&gt;

&lt;p&gt;A drop-in &lt;code&gt;Session&lt;/code&gt; that rotates on every call and retries failures through another proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;proxyspin.requests_adapter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RotatingSession&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RotatingSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxies.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;   &lt;span class="c1"&gt;# new IP per call
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check a list first
&lt;/h2&gt;

&lt;p&gt;Bad proxies waste run time. The bundled CLI tests a whole list concurrently and writes out the survivors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;proxyspin check proxies.txt &lt;span class="nt"&gt;--workers&lt;/span&gt; 100 &lt;span class="nt"&gt;--alive-out&lt;/span&gt; alive.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK   45.155.10.4:8000        612 ms  HTTP 200
DEAD 91.10.77.2:3128                 TimeoutError
...
118/200 alive
wrote 118 proxies to alive.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting proxies to test with
&lt;/h2&gt;

&lt;p&gt;Want to try it right now without your own proxies? Bootstrap straight from a live public list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/gproxynet/free-proxy-list/main/all.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fair warning: &lt;strong&gt;public proxies are unreliable by nature&lt;/strong&gt; — they're shared, slow, and die within minutes. They're fine for kicking the tires, not for a real crawl. For production you'll want dedicated proxies (residential/mobile/datacenter); a pool of one gateway entry per endpoint is all &lt;code&gt;proxyspin&lt;/code&gt; needs since rotation happens server-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;One pool, the same health model everywhere, and you stop babysitting dead proxies. The code is MIT-licensed and on &lt;a href="https://github.com/gproxynet/proxyspin" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — issues and PRs welcome. If you've been limping along on an unmaintained rotation middleware, give it a spin.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Need production-grade rotating &amp;amp; residential proxies with real uptime? Try *&lt;/em&gt;&lt;a href="https://gproxy.net/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=devto-gproxy" rel="noopener noreferrer"&gt;gproxy.net&lt;/a&gt;*&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>scrapy</category>
      <category>proxy</category>
    </item>
  </channel>
</rss>
