<?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: SerpApi.Org</title>
    <description>The latest articles on DEV Community by SerpApi.Org (@serpapiorg).</description>
    <link>https://dev.to/serpapiorg</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%2F2109705%2F47d319df-6491-498e-b9b7-b6072856803e.png</url>
      <title>DEV Community: SerpApi.Org</title>
      <link>https://dev.to/serpapiorg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/serpapiorg"/>
    <language>en</language>
    <item>
      <title>Google custom search api free limit: How to bypass the cap</title>
      <dc:creator>SerpApi.Org</dc:creator>
      <pubDate>Sun, 19 Jul 2026 03:03:25 +0000</pubDate>
      <link>https://dev.to/serpapiorg/google-custom-search-api-free-limit-how-to-bypass-the-cap-376j</link>
      <guid>https://dev.to/serpapiorg/google-custom-search-api-free-limit-how-to-bypass-the-cap-376j</guid>
      <description>&lt;p&gt;Running out of API quota in the middle of a production deployment is a frustrating rite of passage. If you are using the Google Custom Search API, you have likely hit that 100 free daily queries wall. Once you do, your application throws a &lt;code&gt;403 Quota Exceeded&lt;/code&gt; error, stalling your features unless you link a billing card and risk uncapped charges of $5 per 1,000 queries.&lt;/p&gt;

&lt;p&gt;In my experience, relying on Google's default limits without safeguards is a major liability. Here is how I protect my cloud budget, stretch the free tier using Redis, and transition to scalable alternatives when 100 queries are no longer enough.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Enforce a Hard Billing Cap in GCP
&lt;/h3&gt;

&lt;p&gt;Never rely on email alerts alone; they do not stop API requests. If a recursive loop in your code or a malicious bot targets your search endpoint, your credit card will bear the brunt. &lt;/p&gt;

&lt;p&gt;To set up a hard stop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into your &lt;strong&gt;Google Cloud Console&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;APIs &amp;amp; Services&lt;/strong&gt; &amp;gt; &lt;strong&gt;Enabled APIs &amp;amp; Services&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Custom Search API&lt;/strong&gt;, then click the &lt;strong&gt;Quotas&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Locate &lt;strong&gt;Queries per day&lt;/strong&gt; and click the edit pencil icon.&lt;/li&gt;
&lt;li&gt;Set your maximum limit to &lt;strong&gt;95&lt;/strong&gt; (not 100). &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Pro Tip: This 5-query cushion gives you a safe buffer for emergency local debugging without triggering paid overages.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Implement Redis Caching Middleware
&lt;/h3&gt;

&lt;p&gt;Over 40% of search queries in typical web applications are repetitive. Implementing a Redis database to cache these searches can cut your API consumption by up to 80%.&lt;/p&gt;

&lt;p&gt;Here is a simple Python middleware pattern to normalize queries and cache them with a 24-hour Time-To-Live (TTL):&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;redis&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# Connect to local Redis instance
&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decode_responses&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;fetch_search_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;search_engine_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Normalize input to avoid duplicate cache keys
&lt;/span&gt;    &lt;span class="n"&gt;normalized_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&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;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&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;search:cache:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;normalized_query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Check local cache first
&lt;/span&gt;    &lt;span class="n"&gt;cached_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&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;cache_key&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;cached_data&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;cached_data&lt;/span&gt;  &lt;span class="c1"&gt;# Returns cached JSON string
&lt;/span&gt;
    &lt;span class="c1"&gt;# 2. Cache Miss: Query Google API
&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;https://www.googleapis.com/customsearch/v1&lt;/span&gt;&lt;span class="sh"&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;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;search_engine_id&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;normalized_query&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;url&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&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="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="c1"&gt;# 3. Cache the result for 24 hours (86,400 seconds)
&lt;/span&gt;        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&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;data&lt;/span&gt;
    &lt;span class="k"&gt;elif&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API limit hit! Fallback triggered.&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;h3&gt;
  
  
  The Myth of Multiple Search Engines
&lt;/h3&gt;

&lt;p&gt;A common mistake is trying to bypass the limit by creating multiple Programmable Search Engine IDs (&lt;code&gt;cx&lt;/code&gt; parameters). This does not work. Google tracks usage at the &lt;strong&gt;project/API key level&lt;/strong&gt;, not the individual search engine level. &lt;/p&gt;

&lt;p&gt;Rotating multiple free API keys across different projects is technically possible, but it violates Google’s Terms of Service and introduces fragile, hard-to-maintain code that risks IP banning.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Transitioning to Low-Cost Alternatives
&lt;/h3&gt;

&lt;p&gt;When your application outgrows 100 daily queries, you have two logical paths forward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Self-Hosted SearxNG:&lt;/strong&gt; An open-source metasearch engine. You can host it on your VPS and run unlimited queries. The catch? You must manage your own rotating proxies to prevent search engines from blocking your server's IP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serpapi.org:&lt;/strong&gt; If you want structured, reliable search data without the headache of managing proxy networks, migrating to Serpapi is the most efficient move. It bypasses Google's strict limits and visual branding constraints, delivering clean, production-ready JSON from Bing and other major search indexes at a highly competitive scale.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Bài viết gốc được đăng tải tại &lt;a href="https://serpapi.org/posts/google-custom-search-api-free-limit-how-to-bypass-the-cap" rel="noopener noreferrer"&gt;Google custom search api free limit: How to bypass the cap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>cloud</category>
      <category>google</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to scrape google search results with php in 2026</title>
      <dc:creator>SerpApi.Org</dc:creator>
      <pubDate>Sat, 18 Jul 2026 07:28:53 +0000</pubDate>
      <link>https://dev.to/serpapiorg/how-to-scrape-google-search-results-with-php-in-2026-1dc6</link>
      <guid>https://dev.to/serpapiorg/how-to-scrape-google-search-results-with-php-in-2026-1dc6</guid>
      <description>&lt;p&gt;I’ve been building production-grade scrapers for over a decade, and I can tell you that the era of using a simple &lt;code&gt;file_get_contents()&lt;/code&gt; or basic cURL requests to target Google is long gone. In 2026, scraping Search Engine Results Pages (SERPs) requires a highly resilient architecture that can bypass aggressive anti-bot protections, manage system memory efficiently, and handle dynamic JavaScript execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  The PHP 8.x Web Scraping Stack
&lt;/h3&gt;

&lt;p&gt;When parsing search results in PHP, selecting the right parser depends entirely on your target page's architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Symfony DomCrawler:&lt;/strong&gt; Best for parsing static HTML. It is ultra-fast (&amp;lt;50ms execution) and has an extremely low memory footprint (&amp;lt;15MB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symfony Panther:&lt;/strong&gt; Essential for JS-rendered elements (such as dynamic maps or local packs). It runs a headless Chrome instance but consumes significant system memory (&amp;gt;150MB per process).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Avoid outdated libraries like &lt;em&gt;Simple HTML DOM Parser&lt;/em&gt;. They suffer from chronic memory leaks in long-running CLI processes due to circular reference handling in the DOM element tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surviving the Anti-Bot Shield (Handling 429 Errors)
&lt;/h3&gt;

&lt;p&gt;To execute requests successfully, you must route your traffic through a pool of &lt;strong&gt;residential proxies&lt;/strong&gt;. Datacenter IPs are blocked almost instantly by Google's automated firewalls. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rotate IPs &amp;amp; User-Agents:&lt;/strong&gt; Swap your residential proxy credentials and desktop User-Agent strings on every few requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Exponential Backoff with Jitter:&lt;/strong&gt; When you hit an HTTP 429 (Too Many Requests), do not just sleep. Multiply your base delay and add a randomized "jitter" (milliseconds) to prevent footprint detection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a typical Guzzle implementation with adaptive retry logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$baseDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;
&lt;span class="nv"&gt;$maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$maxAttempts&lt;/span&gt;&lt;span class="p"&gt;)&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="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'proxy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$proxyPool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;array_rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$proxyPool&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
            &lt;span class="s1"&gt;'connect_timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'User-Agent'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$randomUserAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'Accept-Language'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'en-US,en;q=0.9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConnectException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$baseDelay&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nv"&gt;$attempt&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="nb"&gt;rand&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$delay&lt;/span&gt;&lt;span class="p"&gt;);&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;h3&gt;
  
  
  Memory Management in CLI Workers
&lt;/h3&gt;

&lt;p&gt;If you're running scraping tasks inside long-running queue workers (like Laravel Queues or Symfony Messenger), memory leaks will crash your daemon. To prevent this, always clear your variables and manually invoke PHP’s garbage collector inside your loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside your scraping loop&lt;/span&gt;
&lt;span class="nv"&gt;$crawler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Crawler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$crawler&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.g'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Crawler&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'h3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'link'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'href'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Explicitly free memory&lt;/span&gt;
&lt;span class="k"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$crawler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;gc_collect_cycles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scalability: Custom Parser vs. Structured API
&lt;/h3&gt;

&lt;p&gt;Building a custom PHP parser is an excellent educational exercise. However, at enterprise scale (10,000+ daily queries), the cost of residential proxy bandwidth, CAPTCHA bypass services, and constant layout maintenance will easily exceed the cost of a dedicated third-party API (like SerpApi). &lt;/p&gt;

&lt;p&gt;If you are building a mission-critical platform, routing your queries through structured search API endpoints will guarantee 99.9% uptime and completely free up your development team from cat-and-mouse scraping battles.&lt;/p&gt;




&lt;p&gt;Bài viết gốc được đăng tải tại &lt;a href="https://serpapi.org/posts/how-to-scrape-google-search-results-with-php-in-2026" rel="noopener noreferrer"&gt;How to scrape google search results with php in 2026&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>php</category>
      <category>tutorial</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
