<?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>Forem: Hilman Ramadhan</title>
    <description>The latest articles on Forem by Hilman Ramadhan (@hilmanski).</description>
    <link>https://forem.com/hilmanski</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1178399%2Fd95919e5-d9e8-44d6-8f30-3eeab2b937ac.png</url>
      <title>Forem: Hilman Ramadhan</title>
      <link>https://forem.com/hilmanski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hilmanski"/>
    <language>en</language>
    <item>
      <title>Grok AI API tutorial - from Chat to Web Search</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Mon, 30 Mar 2026 02:03:37 +0000</pubDate>
      <link>https://forem.com/serpapi/grok-ai-api-tutorial-from-chat-to-web-search-32bo</link>
      <guid>https://forem.com/serpapi/grok-ai-api-tutorial-from-chat-to-web-search-32bo</guid>
      <description>&lt;p&gt;The xAI Grok API provides access to powerful frontier models like Grok 4 series, supporting chat completions (text + vision), image generation, tool calling (function calling + built-in tools like web search), and more advanced features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Intro
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Sign up at &lt;a href="https://x.ai/api" rel="noopener noreferrer"&gt;https://x.ai/api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Generate an API key from the console&lt;/li&gt;
&lt;li&gt;  Install: pip install xai-sdk&lt;/li&gt;
&lt;li&gt;  Set env var: export XAI_API_KEY="your_key_here"&lt;/li&gt;
&lt;li&gt;  Models list: &lt;a href="https://docs.x.ai/developers/models" rel="noopener noreferrer"&gt;https://docs.x.ai/developers/models&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll share some samples in Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lfkaemugd2qrchceiah.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lfkaemugd2qrchceiah.webp" alt="Learn how to use Grok AI - xAI" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Chat API Call
&lt;/h2&gt;

&lt;p&gt;Let's first prepare our project before making the API call&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Install the xai-sdk
&lt;/li&gt;
&lt;/ol&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;xai-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Set env var: export XAI_API_KEY="your_key_here" or use .env file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, create a new file and this basic setup:&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk.chat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;XAI_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;XAI_API_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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Ensure you can print out your &lt;code&gt;XAI_API_KEY&lt;/code&gt; correctly at this stage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, let's call the chat function&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="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-4-1-fast-non-reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are Grok, a highly intelligent, helpful AI assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How can I be a good developer?&lt;/span&gt;&lt;span class="sh"&gt;"&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Feel free to switch the model based on your needs or preferences.&lt;/p&gt;

&lt;p&gt;Here is an example output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp820pmweyv3ub8gtutub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp820pmweyv3ub8gtutub.png" alt="Grok AI API basic call" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Generation API
&lt;/h2&gt;

&lt;p&gt;Let's see how to generate an image with Grok API. We'll need to use the "grok-imagine-image" model for this.&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="bp"&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-imagine-image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;detective cat searching on website&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;Generated image: &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;url&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a URL like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvba4n4bc1kbr84v3zfun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvba4n4bc1kbr84v3zfun.png" alt="Image generation API using xAI API" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Generation API
&lt;/h2&gt;

&lt;p&gt;Generating a video is as easy as generating an image with Grok API. We'll need to use the "grok-imagine-video" model for this.&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A glowing crystal-powered rocket launching from the red dunes of Mars, ancient alien ruins lighting up in the background as it soars into a sky full of unfamiliar constellations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-imagine-video&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;duration&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;aspect_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16:9&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;720p&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="nf"&gt;print&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="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mjz0mata1h2x9u84mvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mjz0mata1h2x9u84mvi.png" alt="Grok Video API example" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can set the duration, aspect ratio, and resolution.&lt;/p&gt;

&lt;p&gt;The xAI Grok API features powerful &lt;a href="https://docs.x.ai/developers/tools/overview" rel="noopener noreferrer"&gt;tool calling&lt;/a&gt; capabilities, allowing Grok to go far beyond simple text generation. It can take real actions such as performing web searches, running code, retrieving information from your own data sources, or invoking any custom functions you've defined.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz6keluq45qofuf0grs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz6keluq45qofuf0grs1.png" alt="from x.ai - Available tools" width="742" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start by calling a custom function, as it'll help us call any internal or external API or function.&lt;/p&gt;

&lt;p&gt;Let's say we want to call a function to look for an item's price. First, we need to define the function, such as adding the name, description, and parameters.&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="bp"&gt;...&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk.chat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_result&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Define tools
&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_item_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get the price of an item from the store&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;parameters&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;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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_name&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;string&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;description&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;Name of the item to get the price for&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_name&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="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;Upon calling the client method, we now need to include the tool we declared above.&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;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-4.20-reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the price of a laptop?&lt;/span&gt;&lt;span class="sh"&gt;"&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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;========= response ===========&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;response&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;==========================&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;&lt;strong&gt;Important:&lt;/strong&gt; At this stage, Grok doesn't care if we have the actual function to check the price or not. The AI simply wants to know "what tools are available" for them to use.&lt;/p&gt;

&lt;p&gt;Try to run the code to see the output from the chat call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93wj0grj9t63gprb79ob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93wj0grj9t63gprb79ob.png" alt="Function Calling output sample" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Grok can detect the tool we need to call. You can see it from &lt;code&gt;outputs &amp;gt; message &amp;gt; tool_calls&lt;/code&gt; . It consists of the name of the function and the arguments that are extracted from the user's prompt, so it'll be dynamic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Function call simulation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Next, let's create a fake function to call. In real life, it could be a call to a database or APIs.&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_item_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prices&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;laptop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;999.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smartphone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;499.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headphones&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;199.99&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prices&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;item_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item not found&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;Following up from the latest code, we can check if the response has a "tool_calls" object or not. If so, we'll call the actual function we just declared above.&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;# Handle tool calls
&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;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt; &lt;span class="ow"&gt;in&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;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_item_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tool_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  We need to loop through the tool_calls object&lt;/li&gt;
&lt;li&gt;  We need to extract the argument to pass to the function&lt;/li&gt;
&lt;li&gt;  Call the actual function alongside the argument value&lt;/li&gt;
&lt;li&gt;  Add the information back to our &lt;code&gt;chat&lt;/code&gt; method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, calling the &lt;code&gt;chat.sample()&lt;/code&gt; method, will include all the information we received from calling the "fake function" before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthd3deobxgf4wom0ylsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthd3deobxgf4wom0ylsb.png" alt="Sample result for function calling" width="573" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try with a different prompt&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I need to buy two laptops and a smartphone. Can you tell me how much that will cost?&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 is the result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqym6gki105k9ski8fydb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqym6gki105k9ski8fydb.png" alt="function calling result sample" width="721" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Search API
&lt;/h2&gt;

&lt;p&gt;Grok can access real-time information through this feature, so you can get up-to-date content. Unlike the function calling above, we don't need to declare a custom function, as it's an internal tool. Here is a simple example:&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk.chat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xai_sdk.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;web_search&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;XAI_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;XAI_API_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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-4.20-reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# reasoning model
&lt;/span&gt;    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="n"&gt;include&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;verbose_streaming&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="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grok VS OpenAI API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;is_thinking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;for&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;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&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;tool_call&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Calling tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with arguments: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&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;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;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasoning_tokens&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_thinking&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="se"&gt;\r&lt;/span&gt;&lt;span class="s"&gt;Thinking... (&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;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasoning_tokens&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tokens)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&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;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_thinking&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="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Final Response:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;is_thinking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_thinking&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;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&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="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\n&lt;/span&gt;&lt;span class="s"&gt;Citations:&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;citations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  Use tools=[web_search()]&lt;/li&gt;
&lt;li&gt;  To show what's happening in the process, we use &lt;code&gt;include=["verbose_streaming"],&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;is_thinking&lt;/code&gt; variable is to check if the process is still running (a boolean variable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkyzqk5xr0wvh3y005tx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkyzqk5xr0wvh3y005tx.png" alt="Web Search API with Grok AI" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it'll perform several searches on the internal with different queries. It'll then visit a specific URL after that to get more context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allowed domains&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can search only in specific domains using &lt;code&gt;allowed_domains&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="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nf"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowed_domains&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;grokipedia.com&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exclude Domains&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Vice versa, you can exclude specific domains&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;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-4.20-reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nf"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;excluded_domains&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;grokipedia.com&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Better Web Search API&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While you can specifically choose the domain, the keyword Grok uses to find answers on the internet is random. For example, when I'm asking for &lt;em&gt;"Top 3 pizza restaurants from Google Maps in Boston. Share some reviews and ratings for each place."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is what I saw from the thinking process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5qo2k4izmmllw8gyypl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5qo2k4izmmllw8gyypl.png" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It needs to perform multiple queries before returning the answer.&lt;/p&gt;

&lt;p&gt;Another sample, when asking simply for 3 images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznt5px2941x413tb5irh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznt5px2941x413tb5irh.png" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It runs on multiple pages, and unfortunately, &lt;strong&gt;the links are not valid&lt;/strong&gt;. &lt;strong&gt;Grok may hallucinate at this point.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Web Search API alternative&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In some cases, AI-generated keywords are fine, but if you're building an app where you want efficiency and full control of the process, the native "Web Search Tool" can be replaced with a simple API call to a specific API your app needs. For example, to find answers on the internet, &lt;a href="https://serpapi.com/search-engine-apis" rel="noopener noreferrer"&gt;SerpApi provides 100+ APIs&lt;/a&gt; for you.&lt;/p&gt;

&lt;p&gt;Need a generic Google answer? We have:&lt;br&gt;&lt;br&gt;
- &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;Google Search API&lt;/a&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://serpapi.com/ai-overview" rel="noopener noreferrer"&gt;Google AI Overview&lt;/a&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://serpapi.com/google-ai-mode-api" rel="noopener noreferrer"&gt;Google AI Mode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and more!&lt;/p&gt;

&lt;p&gt;See how &lt;a href="https://serpapi.com/blog/the-web-search-api-for-ai-applications/" rel="noopener noreferrer"&gt;SerpApi is the Web Search API for your AI apps, LLM, and agents&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using Grok API with SerpApi
&lt;/h3&gt;

&lt;p&gt;To get some idea of how SerpApi works, feel free to test the results on &lt;a href="https://serpapi.com/playground" rel="noopener noreferrer"&gt;our playground&lt;/a&gt;. You can play with different parameters and directly see the JSON sample we return.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folnj7gqh3jorbjw3vzz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folnj7gqh3jorbjw3vzz5.png" alt="SerpApi Playground" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample case&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's say we want to find images via Google Image API like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtmbrp1spmj9igkr3ipd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtmbrp1spmj9igkr3ipd.png" alt="Sample result search with SerpApi" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can register for free at serpapi.com to get your API key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Parsing keyword&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's say we need 3 images from Google. Since users can type anything, we need to parse the keyword, as SerpApi simply performs a search using a particular keyword.&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;USER_QUERY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Show me 3 cute cat images from the internet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Step 1: Ask Grok to extract a search keyword from the user's natural language
&lt;/span&gt;&lt;span class="n"&gt;keyword_chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-3-fast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;keyword_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract the most relevant search keyword or phrase from the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s message. Reply with only the keyword, nothing else.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;keyword_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER_QUERY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;keyword_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyword_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;search_keyword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyword_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;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;Extracted keyword: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;search_keyword&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Search via SerpApi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We now have the keyword. Let's run a search on SerpApi&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;# Step 2: Search via SerpAPI using simple requests (Google Images)
&lt;/span&gt;&lt;span class="n"&gt;serpapi_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;api_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;SERPAPI_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;engine&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;google_images&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;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;search_keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hl&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;en&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;gl&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="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;serpapi_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://serpapi.com/search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;serpapi_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;serpapi_url&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;serpapi_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serpapi_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, you already have the answers you're looking for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: (Optional) Filter results&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Sometimes, we don't need all the information. It's good to filter it programmatically first, so we don't use too many tokens.&lt;/p&gt;

&lt;p&gt;For example, I'm only interested in the top 5 answers:&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;image_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&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;images_results&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="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="n"&gt;formatted_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&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="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;img&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;title&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;No title&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;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;img&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;original&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&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;thumbnail&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;No URL&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;image_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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;SerpAPI results:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;formatted_results&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also format the answer as a bonus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: (Optional) Reply in a natural language&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Depending on your application, you may want to answer the user back in natural language. We just need to pass the answers above back to the AI:&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;# Step 3: Feed results back to Grok for a final response
&lt;/span&gt;&lt;span class="n"&gt;final_chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grok-3-fast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;final_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant. Use the provided search results to answer the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s question.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;final_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;user&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;User question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;USER_QUERY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Search results from SerpAPI:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;formatted_results&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Please answer the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s question based on these results.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;final_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;final_chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Final Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;final_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzgsptepdyehlgc9jn5vb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzgsptepdyehlgc9jn5vb.png" alt="You can try the other APIs for other use cases." width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sidenote
&lt;/h2&gt;

&lt;p&gt;It's also possible to call the API with the OpenAI SDK. Sample:&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;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XAI_API_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;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.x.ai/v1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
    </item>
    <item>
      <title>How to scrape 100 search results on Google (num parameter)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Thu, 25 Sep 2025 01:48:54 +0000</pubDate>
      <link>https://forem.com/serpapi/how-to-scrape-100-search-results-on-google-num-parameter-4jm1</link>
      <guid>https://forem.com/serpapi/how-to-scrape-100-search-results-on-google-num-parameter-4jm1</guid>
      <description>&lt;p&gt;Finally, there is a solution to scrape 100 search results from Google after they dropped the num parameter: introducing the Google Fast Light API&lt;/p&gt;

&lt;p&gt;Google recently discontinued support for showing up to 100 results per page in search, a parameter that many SEO professionals, data companies, and researchers have relied on for years. &lt;a href="https://serpapi.com/blog/google-experiments-with-restricting-results-per-page/" rel="noopener noreferrer"&gt;This change&lt;/a&gt; disrupts established workflows, making large-scale data collection, competitor analysis, and keyword research less efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  API to scrape 100 search results from Google
&lt;/h2&gt;

&lt;p&gt;Introducing &lt;a href="https://serpapi.com/google-light-fast-api" rel="noopener noreferrer"&gt;Google Light Fast API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this API, you can scrape 100 search results from Google with one single request. This API only contains the &lt;code&gt;organic_results&lt;/code&gt; , which makes it faster compared to our regular Google Search API.&lt;/p&gt;

&lt;p&gt;You can access this API with a single GET request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://serpapi.com/search.json?engine=google_light_fast&amp;amp;q=coffee&amp;amp;api_key=YOUR_API_KEY&amp;amp;num=100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  You can replace &lt;code&gt;coffee&lt;/code&gt; with any keyword you want to search for.&lt;/li&gt;
&lt;li&gt;  To get your API key, you can register for free at &lt;a href="http://serpapi.com/?utm_source=blog_google_light_fast_api" rel="noopener noreferrer"&gt;SerpApi&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ckkc6dm2d1t8ww8b80n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ckkc6dm2d1t8ww8b80n.png" alt="Scraping 100 search results from Google" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping other rich results from Google
&lt;/h2&gt;

&lt;p&gt;If you need the complete Google results on the first page, you can still use our Google Search API in order to scrape AI Overview answer, knowledge graph, and so on. So, if you need both &lt;code&gt;100 organic results&lt;/code&gt; and &lt;code&gt;complete rich results&lt;/code&gt; , you only need to perform two searches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  1 to get the 100 organic results via the new Google Fast Light API&lt;/li&gt;
&lt;li&gt;  2 to get the rich results on the Google Search API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No need to paginate the results or run 10 searches anymore.&lt;/p&gt;

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

&lt;p&gt;The removal of the num=100 (&lt;code&gt;&amp;amp;num=100&lt;/code&gt;) parameter from Google has a lot of impacts on many SEO companies that need to serve ranking data for their clients. Many search API providers fail to scrape 100 search results, which results in many errors and misinformation on their platform.&lt;/p&gt;

&lt;p&gt;This issue was also mentioned a couple of times on X (Twitter) by many search engine professionals,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Barry Schwartz -  founder of the Search Engine Roundtable&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawm22h6t4pdn1ibtveae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fawm22h6t4pdn1ibtveae.png" alt="Tweet from Barry Schwartz" width="593" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Brodie Clark - Independent SEO consultant&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiep3nnmb4p32uhlokw1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiep3nnmb4p32uhlokw1n.png" alt="Tweet from Brodie Clark" width="593" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SEO companies looking for a way to track their clients’ &lt;a href="https://serpapi.com/use-cases/seo" rel="noopener noreferrer"&gt;SEO rankings programmatically&lt;/a&gt;, as well as AI companies seeking more real-time data from Google, should try this new API.&lt;/p&gt;

</description>
      <category>webscraping</category>
    </item>
    <item>
      <title>How to scrape Tripadvisor (2025 Tutorial)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Fri, 19 Sep 2025 02:17:42 +0000</pubDate>
      <link>https://forem.com/serpapi/how-to-scrape-tripadvisor-2025-tutorial-57o0</link>
      <guid>https://forem.com/serpapi/how-to-scrape-tripadvisor-2025-tutorial-57o0</guid>
      <description>&lt;p&gt;Scraping Tripadvisor listings allows you to gather detailed information about various attractions, hotels, or restaurants, including descriptions, amenities, pricing, and user-generated ratings. This data can be instrumental in understanding market trends, identifying competitors, and optimizing your own listings or offerings in the travel industry.&lt;/p&gt;

&lt;p&gt;Luckily, you can do this easily using our brand new &lt;a href="https://serpapi.com/tripadvisor-search-api" rel="noopener noreferrer"&gt;Tripadvisor API&lt;/a&gt;. We'll see how to scrape it in cURL, Python, and JavaScript. We can scrape restaurants, things to do, hotels, destinations, vacation rentals, and forum information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Available data on the Tripadvisor Search API
&lt;/h2&gt;

&lt;p&gt;Here is the list of data you can retrieve from this Tripadvisor Search API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  title&lt;/li&gt;
&lt;li&gt;  description&lt;/li&gt;
&lt;li&gt;  rating&lt;/li&gt;
&lt;li&gt;  reviews&lt;/li&gt;
&lt;li&gt;  location&lt;/li&gt;
&lt;li&gt;  thumbnail&lt;/li&gt;
&lt;li&gt;  highlighted overview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8e0xzja19smxxy2rars.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8e0xzja19smxxy2rars.png" alt="Tripadvisor Search API results" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is perfect if you need to collect place data from Tripadvisor.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to scrape the Tripadvisor website?
&lt;/h2&gt;

&lt;p&gt;Now, let's see how to use this simple API from SerpApi to collect the data!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get your API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
First, ensure you register at serpapi.com to get your API Key. You can get 250 free searches per month. You can use this API Key to access all of our APIs, including the Tripadvisor Search API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Available parameters&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
On top of running the basic search, you can see all of our &lt;a href="https://serpapi.com/tripadvisor-search-api" rel="noopener noreferrer"&gt;available parameters here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  cURL Implementation
&lt;/h3&gt;

&lt;p&gt;Here is the basic implementation in cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --get https://serpapi.com/search \
 -d api_key="YOUR_API_KEY" \
 -d engine="tripadvisor" \
 -d q="Rome"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;q&lt;/code&gt; parameter is responsible for the search query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8ldqsivhgg6je2asn34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8ldqsivhgg6je2asn34.png" width="723" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sample response from cURL request&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scrape Tripadvisor search results in Python&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Next, let's see how to scrape the Tripadvisor search results in Python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation for accessing the SerpApi API in Python&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Create a new &lt;code&gt;main.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;  Install requests with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what the basic setup looks like:&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;SERPAPI_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_REAL_SERPAPI_API_KEY&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;api_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;SERPAPI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#replace with your real API Key
&lt;/span&gt;    &lt;span class="c1"&gt;# soon
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;search&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://serpapi.com/search&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="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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&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="nf"&gt;print&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these few lines of code, we can access all of the search engines available at SerpApi, including the Tripadvisor Search API.&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;SERPAPI_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_SERPAPI_API_KEY&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;api_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;SERPAPI_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;engine&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;tripadvisor&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;q&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;indonesia&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&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://serpapi.com/search&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="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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&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="nf"&gt;print&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it easier to see the response, let's add indentation to the output.&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;json&lt;/span&gt;

&lt;span class="c1"&gt;# ...
# ...
# all previous code
&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&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="n"&gt;indent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this Python file should show you the listing for that keyword from Tripadvisor website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Print specific information&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's say we only need the title, description, rating, and reviews. This is how we can print specific columns from the &lt;code&gt;locations&lt;/code&gt; results:&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;# ...
&lt;/span&gt;&lt;span class="n"&gt;search&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://serpapi.com/search&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="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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&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;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&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;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;locations&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="n"&gt;title&lt;/span&gt; &lt;span class="o"&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;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&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;rating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reviews&lt;/span&gt; &lt;span class="o"&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;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&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;description&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;Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&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;Rating: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rating&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;Reviews: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reviews&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;description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;description&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41pgyoq6dz9c6mbi9epb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41pgyoq6dz9c6mbi9epb.png" alt="Sample response from printing specific information" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Export data to a CSV file&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's see how to export this Tripadvisor product data into a CSV file in Python&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;# ...
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tripadvisor_results.csv&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&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;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fieldnames&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;title&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;rating&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;reviews&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;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&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;result&lt;/span&gt; &lt;span class="ow"&gt;in&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;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;locations&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="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;title&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;rating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;rating&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;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;reviews&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;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;description&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="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;Data exported successfully.&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi4j2cotuw2snr8iusno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi4j2cotuw2snr8iusno.png" alt="Export Tripadvisor search results into a CSV file" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript implementation
&lt;/h3&gt;

&lt;p&gt;Finally, let's see how to scrape the Tripadvisor search results in JavaScript.&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;serpapi&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install serpapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a basic query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { getJson } = require("serpapi");
getJson({
  engine: "tripadvisor",
  api_key: API_KEY, // Put your API Key
  q: "indonesia"
}, (json) =&amp;gt; {
  console.log(json["locations"]);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other programming languages
&lt;/h3&gt;

&lt;p&gt;While you can use our APIs using a simple GET request with any programming language, you can also see our ready-to-use libraries here: &lt;a href="https://serpapi.com/integrations" rel="noopener noreferrer"&gt;SerpApi Integrations&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to customize the search?
&lt;/h2&gt;

&lt;p&gt;We provide many filters to customize your search.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  tripadvisor_domain: Parameter defines the Tripadvisor domain to use. It defaults to &lt;code&gt;tripadvisor.com&lt;/code&gt;. Head to &lt;a href="https://serpapi.com/tripadvisor-domains" rel="noopener noreferrer"&gt;Tripadvisor domains&lt;/a&gt; for a full list of supported domains.&lt;/li&gt;
&lt;li&gt;  lat and lon: Specify the lat and lon GPS coordinates when performing the search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example using a different&lt;/em&gt; Tripadvisor &lt;em&gt;domain:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --get https://serpapi.com/search \
 -d api_key="YOUR_API_KEY" \
 -d engine="tripadvisor" \
 -d q="cafe" \
 -d tripadvisor_domain="wwww.tripadvisor.ca"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advanced Parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  ssrc: This parameter specifies the search filter you want to use for the Tripadvisor search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Available options:&lt;br&gt;&lt;br&gt;
&lt;code&gt;a&lt;/code&gt; - All Results&lt;br&gt;&lt;br&gt;
&lt;code&gt;r&lt;/code&gt; - Restaurants&lt;br&gt;&lt;br&gt;
&lt;code&gt;A&lt;/code&gt; - Things to Do&lt;br&gt;&lt;br&gt;
&lt;code&gt;h&lt;/code&gt; - Hotels&lt;br&gt;&lt;br&gt;
&lt;code&gt;g&lt;/code&gt; - Destinations&lt;br&gt;&lt;br&gt;
&lt;code&gt;v&lt;/code&gt; - Vacation Rentals&lt;br&gt;&lt;br&gt;
&lt;code&gt;f&lt;/code&gt; - Forums&lt;/p&gt;

&lt;h3&gt;
  
  
  How to paginate the results?
&lt;/h3&gt;

&lt;p&gt;You can scrape beyond the first page using the &lt;code&gt;offset&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; parameter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  limit defines how many results you want to receive per page. By default, it's 30. Maximum value is 100.&lt;/li&gt;
&lt;li&gt;  Offset skips the given number of results. For example, 0 for the first page, 30 for 2nd page, 60 for 3rd page, and so on. Or if you're using &lt;code&gt;100&lt;/code&gt; as the &lt;code&gt;limit&lt;/code&gt;, then the 2nd page will be &lt;code&gt;100&lt;/code&gt;, 3rd page will be &lt;code&gt;200&lt;/code&gt;, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is it legal to scrape the Tripadvisor website?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Scraping publicly available data from websites like Tripadvisor is generally permitted under U.S. law.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Register at serpapi.com to start for free. If you want to scale, we offer tiered plans based on your usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do you need to scrape Tripadvisor listing?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Scraping Tripadvisor listings can provide valuable data for market analysis, competitive insights, and understanding customer preferences in the travel and hospitality industry. This information can help businesses optimize their offerings and improve customer engagement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;That's it! Thank you very much for reading this blog post. You can play around for free on our &lt;a href="https://serpapi.com/playground?engine=tripadvisor" rel="noopener noreferrer"&gt;playground here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webscraping</category>
    </item>
    <item>
      <title>Scrape YouTube videos in Python</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Fri, 08 Aug 2025 08:10:37 +0000</pubDate>
      <link>https://forem.com/serpapi/scrape-youtube-videos-in-python-1p6d</link>
      <guid>https://forem.com/serpapi/scrape-youtube-videos-in-python-1p6d</guid>
      <description>&lt;p&gt;Scraping YouTube videos enables developers and businesses to extract detailed YouTube video metadata at scale, including titles, descriptions, view counts, thumbnails, channel names, related videos, and comments/replies. It streamlines what would otherwise require complex scraping and anti‑blocking measures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get your API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
First, ensure to register at &lt;a href="https://serpapi.com/?ref=youtube_video_article" rel="noopener noreferrer"&gt;SerpApi&lt;/a&gt; to get your API Key. You can get 250 free searches per month. Use this API Key to access all of our APIs, including the YouTube Video API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Available parameters&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In addition to running the basic search, you can view all &lt;a href="https://serpapi.com/youtube-video-api" rel="noopener noreferrer"&gt;YouTube Video API parameters here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to scrape YouTube video data with Python
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Create a new &lt;code&gt;main.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;  Install requests with:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here is what the basic setup looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
SERPAPI_API_KEY = "YOUR_REAL_SERPAPI_API_KEY"

params = {
    "api_key": SERPAPI_API_KEY, #replace with the actual API Key
    # soon
}

search = requests.get("https://serpapi.com/search", params=params)
response = search.json()
print(response)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these few lines of code, we can access all of the search engines available at SerpApi, including the YouTube Video API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
SERPAPI_API_KEY = "YOUR_SERPAPI_API_KEY"

params = {
    "api_key": SERPAPI_API_KEY, 
    "engine": "youtube_video",
    "v": "j3YXfsMPKjQ" # YouTube video ID
}

search = requests.get("https://serpapi.com/search", params=params)
response = search.json()
print(response)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it easier to see the response, let's add indentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json

# ...
# ...
# all previous code

print(json.dumps(response, indent=2))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljn0f97m2hym2x6zhq1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljn0f97m2hym2x6zhq1k.png" alt="YouTube Video API response example" width="716" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how to scrape the comments on a video.&lt;/p&gt;

&lt;p&gt;From the request we performed previously, you should be able to see this in the response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibpwjts6hdxnyqoupe0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibpwjts6hdxnyqoupe0o.png" alt="comment page token response" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can scrape the "Top comments" or "Newest first" comments using the &lt;code&gt;token&lt;/code&gt; that is available for each. We can put this token in the &lt;code&gt;next_page_token&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;params = {
    "api_key": SERPAPI_API_KEY, 
    "engine": "youtube_video",
    "v": "j3YXfsMPKjQ", # YouTube video ID
    "next_page_token": "Eg0SC2ozWVhmc01QS2pRGAYyOCIRIgtqM1lYZnNNUEtqUTABeAIwAUIhZW5nYWdlbWVudC1wYW5lbC1jb21tZW50cy1zZWN0aW9u"
}

search = requests.get("https://serpapi.com/search", params=params)
response = search.json()
print(json.dumps(response, indent=2))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmzofv6auyt7hm5s194e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmzofv6auyt7hm5s194e.png" alt="Scraping YouTube comments example" width="744" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in scraping the reply on the comment, you can repeat the same action, but this time using the &lt;code&gt;replies_next_page_token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can also scrape YouTube search results using our YouTube Search API. Here is &lt;a href="https://serpapi.com/blog/how-to-scrape-youtube-data-with-simple-api/" rel="noopener noreferrer"&gt;how to scrape YouTube search results using Python&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use It?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Real‑time data&lt;/strong&gt;: Fetch up‑to‑date video details.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rich metadata access&lt;/strong&gt;: Retrieve comments and nested replies, related videos, and viewer stats in one request.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Automated proxy and captcha handling&lt;/strong&gt;: SerpApi manages the complexities of scraping so you can focus on analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Feature: Comprehensive video metadata

&lt;ul&gt;
&lt;li&gt;Benefit: Includes title, description, duration, chapters, views, publishing date, channel, thumbnails&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Feature: Comments &amp;amp; replies

&lt;ul&gt;
&lt;li&gt;Benefit: Access nested comments for engagement analysis&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Feature: Related videos

&lt;ul&gt;
&lt;li&gt;Benefit: Discover context and competition around a video&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>webscraping</category>
      <category>youtube</category>
    </item>
    <item>
      <title>Build a SERP rank tracker app with this API</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Mon, 23 Sep 2024 07:48:06 +0000</pubDate>
      <link>https://forem.com/serpapi/build-a-serp-rank-tracker-app-with-this-api-4nbj</link>
      <guid>https://forem.com/serpapi/build-a-serp-rank-tracker-app-with-this-api-4nbj</guid>
      <description>&lt;p&gt;If you're interested in building a SERP ranking tracker app, you'll love our API. SerpApi provides a simple API to access live data from various search engines, including Google, Bing, DuckDuckGo, Yahoo, and others. It enables you to build an app like a SERP ranking tracker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv80hkrqa30a74vsml1ga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv80hkrqa30a74vsml1ga.png" alt="Rank tracker API illustration." width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;To get the ranking position of a website, we need to access the organic results and check where the domain first appears. The organic results data is available through our API. Here are the three APIs we're going to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;Google Search API&lt;/a&gt;: Scrape the results from Google search.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/bing-search-api" rel="noopener noreferrer"&gt;Bing Search API&lt;/a&gt;: Scrape the results from the Bing search.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/duckduckgo-search-api" rel="noopener noreferrer"&gt;DuckDuckGo Search API&lt;/a&gt;: Scrape the results from the DuckDuckGo search.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Design
&lt;/h2&gt;

&lt;p&gt;We'll create a single endpoint where people can receive the ranking results from the above search engine using this parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Domain name (String): The website domain we want to track.&lt;/li&gt;
&lt;li&gt;  Keywords (Array): List of keywords that we want to search for.&lt;/li&gt;
&lt;li&gt;  Engines (Object): List of search engines we want to search on. We can also adjust the parameter details based on the API. Refer to the relevant documentation to check the available parameters for each APIs.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST: /api/rankings

Data:
 - domain (string)
 - keywords (array[string])
 - engines ((array[name, params]))

Example:
{
  "domain": "archive.org",
  "keywords": ["internet archive"],
  "engines": [
    {
      "name": "google",
      "params": {
        "domain": "google.com",
        "gl": "es"
      }
    }
  ]
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source code is available on GitHub; feel free to take a look at the detailed implementation here:&lt;br&gt;
&lt;a href="https://github.com/hilmanski/rank-tracker-api/" rel="noopener noreferrer"&gt;https://github.com/hilmanski/rank-tracker-api/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's write the code!
&lt;/h2&gt;

&lt;p&gt;I'll use Nodejs for this API; feel free to use other languages/frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Express&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's use Express to help us clean up the code structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i express --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Export your API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can either export your API key in a terminal like the sample below or save it on an &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export SERPAPI_API_KEY=YOUR_ACTUAL_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Basic route&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Prepare the POST endpoint with relevant parameters&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express')
const app = express()
const port = 3000

app.use(express.json());

app.post('/api/rankings', async(req, res) =&amp;gt; {
  const { keywords, engines, domain } = req.body;

  // detail implementation later

  res.json({ keywords, engines });
})

app.listen(port, () =&amp;gt; {
  console.log(`Example app listening on port ${port}`)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Validate the input type&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Make sure API users use the correct types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/api/rankings', async(req, res) =&amp;gt; {
  const { keywords, engines, domain } = req.body;

   // Validate keywords
  if (!Array.isArray(keywords) || !keywords.length) {
    return res.status(400).json({ error: 'Keywords and engines must be arrays.' });
  }

  // Validate engines
  for (const engine of engines) {
    if (typeof engine !== 'object' || !engine.name) {
      return res.status(400).json({ error: 'Each engine must be an object with a "name" property.' });
    }
    if (engine.params &amp;amp;&amp;amp; typeof engine.params !== 'object') {
      return res.status(400).json({ error: 'Engine "params" must be an object.' });
    }
  }

  // coming soon

})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run parallel search&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Since we're enabling multiple keywords and multiple search engines, we need to run the function in parallel to save us some time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Parallel search
  const results = await Promise.all(engines.map(async engine =&amp;gt; {
    const rankings = await Promise.all(keywords.map(async keyword =&amp;gt; {
      return await getRanking(keyword, engine, cleanDomain);
    }));

    // map keywords - rankings in one array
    const rankingResults = keywords.map((keyword, index) =&amp;gt; {
      return [keyword, rankings[index]];
    });

    console.log(rankingResults);

    return { domain, engine, rankingResults };
  }))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*getRanking method coming soon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GetRanking method implementation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is the function that is responsible for running the search for each search engine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const suportEngines = ['google', 'bing', 'duckduckgo'];

async function getRanking(keyword, engine, domain) {
  const engineName = engine.name.toLowerCase();

  if(!suportEngines.includes(engineName)) {
      console.error(`Error: Engine ${engineName} is not supported.`);
      return;
  }

  return new Promise(async (resolve, reject) =&amp;gt; {
      switch(engineName) {
          case 'google':
            resolve(await searchGoogle(keyword, engine.params, domain))
          break;
          case 'bing':
            resolve(await searchBing(keyword, engine.params, domain))
          break;
          case 'duckduckgo':
            resolve(await searchDuckDuckGo(keyword, engine.params, domain))
          break;
          default:
          break;
      }
  })
}

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

&lt;/div&gt;



&lt;p&gt;We'll use the native fetch method in NodeJS to request the actual SerpApi endpoint for each search engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API to access ranking position in Google&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;function searchGoogle(keyword, params, domain) {
  let endpoint = `https://serpapi.com/search?q=${keyword}&amp;amp;engine=google&amp;amp;num=100&amp;amp;api_key=${SERPAPI_API_KEY}`
  if(params) {
      endpoint += `&amp;amp;${new URLSearchParams(params).toString()}`
  }

  return fetch(endpoint)
    .then(response =&amp;gt; response.json())
    .then(data =&amp;gt; {
      const organic_results = data.organic_results;
      let ranking = organic_results.findIndex(result =&amp;gt; result.link.includes(domain))
      return ranking + 1;
    })
    .catch(error =&amp;gt; {
      console.error(error);
    });
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;API to access ranking position in Bing&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;function searchBing(keyword, params, domain) {
  let endpoint = `https://serpapi.com/search?q=${keyword}&amp;amp;engine=bing&amp;amp;count=50&amp;amp;api_key=${SERPAPI_API_KEY}`
  if(params) {
      endpoint += `&amp;amp;${new URLSearchParams(params).toString()}`
  }

  return fetch(endpoint)
    .then(response =&amp;gt; response.json())
    .then(data =&amp;gt; {
      const organic_results = data.organic_results;
      let ranking = organic_results.findIndex(result =&amp;gt; result.link.includes(domain))
      return ranking + 1;
    })
    .catch(error =&amp;gt; {
      console.error(error);
    });
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;API to access ranking position in DuckDuckGo&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;function searchDuckDuckGo(keyword, params, domain) {
  let endpoint = `https://serpapi.com/search?q=${keyword}&amp;amp;engine=duckduckgo&amp;amp;api_key=${SERPAPI_API_KEY}`
  if(params) {
      endpoint += `&amp;amp;${new URLSearchParams(params).toString()}`
  }

  return fetch(endpoint)
    .then(response =&amp;gt; response.json())
    .then(data =&amp;gt; {
      const organic_results = data.organic_results;
      let ranking = organic_results.findIndex(result =&amp;gt; result.link.includes(domain))
      return ranking + 1;
    })
    .catch(error =&amp;gt; {
      console.error(error);
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Final Endpoint&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here's what our endpoint looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/api/rankings', async(req, res) =&amp;gt; {
  const { keywords, engines, domain } = req.body;

  // Validate keywords
  if (!Array.isArray(keywords) || !keywords.length) {
    return res.status(400).json({ error: 'Keywords and engines must be arrays.' });
  }

  // Validate engines
  for (const engine of engines) {
    if (typeof engine !== 'object' || !engine.name) {
      return res.status(400).json({ error: 'Each engine must be an object with a "name" property.' });
    }
    if (engine.params &amp;amp;&amp;amp; typeof engine.params !== 'object') {
      return res.status(400).json({ error: 'Engine "params" must be an object.' });
    }
  }

  // CLean up domain
  // Since people can include https:// or http:// or a subdomain, strip all of it?
  const cleanDomain = domain.replace(/^https?:\/\//, '').replace(/\/$/, '');

  // Parallel search
  const results = await Promise.all(engines.map(async engine =&amp;gt; {
    const rankings = await Promise.all(keywords.map(async keyword =&amp;gt; {
      return await getRanking(keyword, engine, cleanDomain);
    }));

    // map keywords - rankings in one array
    const rankingResults = keywords.map((keyword, index) =&amp;gt; {
      return [keyword, rankings[index]];
    });

    console.log(rankingResults);

    return { domain, engine, rankingResults }
  }))

  res.json(results);
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the API
&lt;/h2&gt;

&lt;p&gt;Let's try out this API via cURL.&lt;/p&gt;

&lt;p&gt;No parameter example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://localhost:3000/api/rankings \
  -H 'Content-Type: application/json' \
  -d '{
    "keywords": ["internet archive"],
    "domain": "archive.org",
    "engines": [
      {
       "name": "google"
     }
    ]
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using multiple keywords and search engine parameter sample&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://localhost:3000/api/rankings \
  -H 'Content-Type: application/json' \
-d '{
    "keywords": ["internet archive", "digital library archived internet"],
    "domain": "archive.org",
    "engines": [
      {
        "name": "google",
        "params": {
            "google_domain": "google.co.id",
            "gl": "id"
        }
      }
    ]
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using multiple search engines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://localhost:3000/api/rankings \
  -H 'Content-Type: application/json' \
  -d '{
    "keywords": ["internet archive", "digital archive", "internet library"],
    "domain": "archive.org",
    "engines": [
    {
      "name": "google",
      "params": {
        "domain": "google.com",
        "gl": "es"
      }
    },
    {
      "name": "Bing",
      "params": {
        "cc": "gb"
      }
    },
{
      "name": "duckduckgo",
      "params": {
        "kl": "uk-en"
      }
    }
  ]
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! I hope you like this post. Please let us know if you have any questions. Feel free to also contribute to this project on GitHub.&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Create a super fast AI assistant with Groq (Without a database)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Thu, 16 May 2024 02:47:59 +0000</pubDate>
      <link>https://forem.com/serpapi/create-a-super-fast-ai-assistant-with-groq-without-a-database-7e7</link>
      <guid>https://forem.com/serpapi/create-a-super-fast-ai-assistant-with-groq-without-a-database-7e7</guid>
      <description>&lt;p&gt;Last week, I tried to build a voice AI assistant using OpenAI AI assistant. It takes a while to generate a response, which is not suitable for a voice assistant. So, I'm looking for an alternative to make my assistant faster. That's how I found out about Groq. This post will cover how I build an AI assistant using Groq.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros and Cons summary&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Pro:&lt;br&gt;&lt;br&gt;
Easy to implement with only one API (Groq API).&lt;br&gt;&lt;br&gt;
Respond is fast.&lt;/p&gt;

&lt;p&gt;Cons:&lt;br&gt;&lt;br&gt;
The longer we chat, the higher the chance that we might lose some context along the way.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Groq?
&lt;/h2&gt;

&lt;p&gt;Groq is a service that provides a super fast engine to run AI applications. &lt;strong&gt;It's not an AI model!&lt;/strong&gt; We can run different AI models like Llama, Mixtral, Gemma and more!&lt;/p&gt;

&lt;p&gt;Ref: &lt;a href="https://wow.groq.com/why-groq/" rel="noopener noreferrer"&gt;Why Groq?&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How I build a fast AI assistant
&lt;/h2&gt;

&lt;p&gt;Many AI models exist, but only OpenAI offers an easy way to implement a chat-like experience using the &lt;a href="https://platform.openai.com/docs/assistants/overview" rel="noopener noreferrer"&gt;Assistants API&lt;/a&gt;. By default, these models won't know or understand the context of our previous chat. So, we have to re-explain everything if we want the AI to understand the context of each message.&lt;/p&gt;

&lt;p&gt;There are some alternatives out there, such as using &lt;a href="https://python.langchain.com/v0.1/docs/use_cases/question_answering/chat_history/" rel="noopener noreferrer"&gt;LangChain chat history&lt;/a&gt;. But I prefer to find a simple way (*with the caveat, of course). Luckily, I found some ideas on the internet (Thank you, Internet!).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The idea below can be implemented for any AI model/engine, not just Groq. You can try this with OpenAI itself, Mixtral, Claude, and so on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpst8juagjiqtfpf7f1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpst8juagjiqtfpf7f1f.png" alt="chat flow illustration" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The user sends the initial message&lt;/li&gt;
&lt;li&gt;  The AI responds to the message&lt;/li&gt;
&lt;li&gt;  We ask AI to summarize the conversation&lt;/li&gt;
&lt;li&gt;  We send the response and summary back to the user&lt;/li&gt;
&lt;li&gt;  The user will send the summary back later alongside the new message&lt;/li&gt;
&lt;li&gt;  AI now will reply based on the fresh message and with help of the conversation summary to provide some context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The caveat of this method&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
By summarizing a conversation, we may lose some information along the way. That's why it's a good idea in certain cases to store the message history on a database (Vector database).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One way I can reduce this shortage is by attaching the recent reply from AI. I've also read an article that suggests keeping the latest 2-3 conversations and providing them as additional context later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Code implementation
&lt;/h2&gt;

&lt;p&gt;I'll use NodeJs for this tutorial. Feel free to use any language you want. The final code is available at GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hilmanski/assistants-api-with-groq-ai" rel="noopener noreferrer"&gt;GitHub -assistants-api-with-groq-ai&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Install dependencies&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i express groq-sdk dotenv --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;  Express for creating a route for the endpoint&lt;/li&gt;
&lt;li&gt;  Groq-sdk is the official package for using Groq in Javascript&lt;/li&gt;
&lt;li&gt;  dotenv to store our API key safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Add API Key&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a new &lt;code&gt;.env&lt;/code&gt; file. Add your Groq API key in this file 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;GROQ_API_KEY=YOUR_GROQ_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to sign up to Groq and get your API key &lt;a href="https://console.groq.com/keys" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Basic Setup&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's create a new &lt;code&gt;index.js&lt;/code&gt;file, and we'll write everything in this file. We prepare one endpoint called &lt;code&gt;chat&lt;/code&gt; where we'll send these parameters:&lt;br&gt;&lt;br&gt;
- message: user's message&lt;br&gt;&lt;br&gt;
- latestReply: The latest reply from AI&lt;br&gt;&lt;br&gt;
- messageSummary: The conversation summary so far&lt;/p&gt;

&lt;p&gt;In this endpoint, we'll do two things:&lt;br&gt;&lt;br&gt;
- Respond to new user message (with latestReply and messageSummary as context)&lt;br&gt;&lt;br&gt;
- Create a new conversation summary by providing the fresh reply from AI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');

// Express Setup
const app = express();
app.use(express.json());
const port = 3000

require("dotenv").config();
const { GROQ_API_KEY } = process.env;

// GROQ Setup
const Groq = require("groq-sdk");
const groq = new Groq({
    apiKey: GROQ_API_KEY
});

async function chatWithGroq() { } // soon
async function summarizeConversation() { } // soon

app.post('/chat', async (req, res) =&amp;gt; {
    const { message, latestReply, messageSummary } = req.body;

    // request chat completion
    const reply = await chatWithGroq(message, latestReply, messageSummary)

    // request chat summary
    const summary = await summarizeConversation(message, reply, messageSummary)

    // Always return chat history/summary
    res.send({
        reply,
        summary
    })
})

app.listen(port, () =&amp;gt; {
  console.log(`Example app listening on port ${port}`)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Chat with Groq method&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the chatWithGroq method implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function chatWithGroq(userMessage, latestReply, messageHistory) {
    let messages = [{
        role: "user",
        content: userMessage
    }]

    if(messageHistory != '') {
        messages.unshift({
            role: "system",
            content: `Our conversation's summary so far: """${messageHistory}""". 
                     And this is the latest reply from you """${latestReply}"""`
        })
    }

    console.log('original message', messages)

    const chatCompletion = await groq.chat.completions.create({
        messages,
        model: "llama3-8b-8192"
    });

    const respond = chatCompletion.choices[0]?.message?.content || ""
    return respond
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  We only provide a conversation summary when we have one (look at the if statement). So, it won't be included in our first message.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Conversation summary method&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the &lt;code&gt;summarizeConversation&lt;/code&gt; method implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function summarizeConversation(message, reply, messageSummary) {
    let content = `Summarize this conversation 
                    user: """${message}""",
                    you(AI): """${reply}"""
                  `

    // For N+1 message
    if(messageSummary != '') {
        content = `Summarize this conversation: """${messageSummary}"""
                    and last conversation: 
                    user: """${message}""",
                    you(AI): """${reply}"""
                `
    }

    const chatCompletion = await groq.chat.completions.create({
        messages: [
            {
                role: "user",
                content: content
            }
        ],
        model: "llama3-8b-8192"
    });

    const summary = chatCompletion.choices[0]?.message?.content || ""
    console.log('summary: ', summary)
    return summary
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this method, we ask the AI to create a summary based on the latest summary and recent reply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Time!
&lt;/h2&gt;

&lt;p&gt;You can use any API client, like Postman, Thunder (VS Code), etc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't forget to run your program with &lt;code&gt;node index.js&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a POST request for the &lt;code&gt;/chat&lt;/code&gt; endpoint and provide &lt;code&gt;message&lt;/code&gt; endpoint and provide the first message parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv00xn3ii2b3h18dgln05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv00xn3ii2b3h18dgln05.png" alt="initial message illustration" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can display the &lt;code&gt;reply&lt;/code&gt; from the &lt;code&gt;response&lt;/code&gt; on our user interface. This is the actual reply to our message.&lt;/p&gt;

&lt;p&gt;We'll save the &lt;code&gt;summary&lt;/code&gt; for the next request.&lt;/p&gt;

&lt;p&gt;Now, this is how the JSON looks like for the N+1 message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo33cyxw2ek7a4hcrpipm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo33cyxw2ek7a4hcrpipm.png" alt="N+1 message parameters" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next messages should include the &lt;code&gt;latestReply&lt;/code&gt; and &lt;code&gt;messageSummary&lt;/code&gt; as parameters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  message: *Don't forget to add a new message. This is you talking to the AI. Notice that I use &lt;code&gt;here&lt;/code&gt; on my question, to validate that the AI knows what's the previous context here.&lt;/li&gt;
&lt;li&gt;  latestReply: Send the latest reply from AI (from previous response)&lt;/li&gt;
&lt;li&gt;  messageSummary: Send the conversation summary so far (from previous response)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the result to this request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqghgpdzyk9hoconuvvx5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqghgpdzyk9hoconuvvx5.png" alt="Summary conversation and reply example" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the AI knows that when I said &lt;code&gt;here&lt;/code&gt; I was talking about &lt;code&gt;Indonesia&lt;/code&gt;. You can try to send a follow-up message (create a new request) by asking something like "Can you tell me more about number 4?" as an example. But don't forget that we always need to update the &lt;code&gt;latestReply&lt;/code&gt; and &lt;code&gt;summaryConversation&lt;/code&gt; on each request.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To return the response and summarize the conversation, I only need to wait around &lt;code&gt;2s&lt;/code&gt;. This is much faster than using OpenAI AI assistants.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://serpapi.com/blog/build-a-smart-ai-voice-assistant-connect-to-the-internet/" rel="noopener noreferrer"&gt;Build a smart AI voice assistant&lt;/a&gt;&lt;br&gt;&lt;br&gt;
- &lt;a href="https://serpapi.com/blog/assistant-api-openai-beginner-tutorial/" rel="noopener noreferrer"&gt;Basic tutorial: Assistants API by OpenAI&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>Build an AI Voice assistant like Siri (use OpenAI AI Assistant)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Tue, 30 Apr 2024 00:23:48 +0000</pubDate>
      <link>https://forem.com/serpapi/build-an-ai-voice-assistant-like-siri-use-openai-ai-assistant-24do</link>
      <guid>https://forem.com/serpapi/build-an-ai-voice-assistant-like-siri-use-openai-ai-assistant-24do</guid>
      <description>&lt;p&gt;Hi! Today, we'll learn how to build an AI Voice assistant like Siri that can understand what we say and speak back to us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo AI Voice assistant
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/tx4E6VWyAzw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here's what we're going to build:&lt;/p&gt;

&lt;p&gt;This post will focus on implementing it as a web application. Therefore, we will use HTML for the interface and Javascript for the voice features. You might want to adjust this if you build for another platform (mobile, desktop, etc.).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Regardless of the platform, knowing the components of how to build this will help us along the way.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Voice AI assistant Structure
&lt;/h2&gt;

&lt;p&gt;Just like other applications, we will have this basic structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdp7h59b2hgi2066om060.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdp7h59b2hgi2066om060.png" alt="input, logic, and output illustration." width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what it looks like on our app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa02afe3tm5cbs52s9cyq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa02afe3tm5cbs52s9cyq.png" alt="AI Voice assistant basic logic illustration." width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can replace each of these elements with the more advanced option. I'm trying to stick with what we already have in the browser. These are the alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Voice input: AssemblyAI or OpenAI Whisper&lt;/li&gt;
&lt;li&gt;  Voice output: Elevenlabs or OpenAI Whisper&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code Tutorial on how to build a Voice AI assistant
&lt;/h2&gt;

&lt;p&gt;We'll split our codebase into two parts, one for the front end and one for the back end.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First, we just want to make sure that we can get a text from the user's voice and read a text out loud; there is no AI or conversation involved yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the final code result of this post:&lt;br&gt;
&lt;a href="https://github.com/hilmanski/simple-ai-voice-assistant-openai-demo" rel="noopener noreferrer"&gt;GitHub - hilmanski/simple-ai-voice-assistant-openai-demo&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Basic frontend Views
&lt;/h3&gt;

&lt;p&gt;Since we're building a web application, we'll use HTML.&lt;br&gt;&lt;br&gt;
- We need two buttons to start and stop the recording&lt;br&gt;&lt;br&gt;
- A div to display the text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button id="record"&amp;gt;Record&amp;lt;/button&amp;gt;
&amp;lt;button id="stop"&amp;gt;Stop&amp;lt;/button&amp;gt;

&amp;lt;div id="output"&amp;gt;Output&amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;(Optional) If you want to copy the style I implemented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;style&amp;gt;
body {
    margin: 50px auto;
    width: 500px;
}

#output {
    margin-top: 20px;
    border: 1px solid #000;
    padding: 10px;
    height: 200px;
    overflow-y: scroll;
}

#output p:nth-child(even) {
    background-color: #f8f6b1;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Listen to speech
&lt;/h3&gt;

&lt;p&gt;Let's trigger the actions from Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    // Set up
    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    const SpeechGrammarList = window.SpeechGrammarList || window.webkitSpeechGrammarList;
    const SpeechRecognitionEvent = window.SpeechRecognitionEvent || window.webkitSpeechRecognitionEvent;

    const recognition = new SpeechRecognition();
    const speechRecognitionList = new SpeechGrammarList();

    recognition.grammars = speechRecognitionList;
    recognition.continuous = true;
    recognition.lang = 'en-US';
    recognition.interimResults = false;
    recognition.maxAlternatives = 1;


    // Start recording
    document.getElementById('record').onclick = function() {
        recognition.start();
    }

    // Stop recording
    document.getElementById('stop').onclick = function() {
        recognition.stop();
        console.log('Stopped recording.');
    }

    // Output
    recognition.onresult = async function(event) {
        // Get the latest transcript 
        const lastItem = event.results[event.results.length - 1]
        const transcript = lastItem[0].transcript;
        document.getElementById('output').textContent = transcript;
        recognition.stop(); 
        // await sendMessage(transcript); // we'll implement this later
    }

    recognition.onspeechend = function() {
        recognition.stop();
    }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Backend structure
&lt;/h3&gt;

&lt;p&gt;We're using NodeJS/Express for the backend. Make sure to install the necessary packages in your new directory (separate from the frontend code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y #initialize NPM package
npm init express cors dotenv openai --save
touch index.js #create a new empty file
&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;const express = require('express');
const cors = require('cors')

// Setup Express and allow CORS
const app = express();
app.use(express.json());
app.use(cors()) // allow CORS for all origins

// Main route
app.post('/message', async (req, res) =&amp;gt; {
    const { message } = req.body;
    res.json({ message: 'Received: ' + message });
});

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () =&amp;gt; {
  console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;node js&lt;/code&gt; in your terminal to run the server. Your application is running on localhost:3000.&lt;/p&gt;

&lt;p&gt;We only have one route, which is &lt;code&gt;/message&lt;/code&gt; , which receives a message from the client and echoes it back. This is to ensure that the input and output parts are running smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Send a message from frontend
&lt;/h3&gt;

&lt;p&gt;Let's add a fetch method to send the message to that endpoint.&lt;/p&gt;

&lt;p&gt;Update your &lt;code&gt;recognition.onresult&lt;/code&gt; event to call the &lt;code&gt;sendMessage&lt;/code&gt; function 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;recognition.onresult = async function(event) {
    // Get the latest transcript 
    const lastItem = event.results[event.results.length - 1]
    const transcript = lastItem[0].transcript;
    document.getElementById('output').textContent = transcript;

    await sendMessage(transcript); // New addition
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's declare the &lt;code&gt;sendMessage&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function sendMessage(message) {
    const response = await fetch('http://localhost:3000/message', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ message })
    });

    const data = await response.json();
    console.log(data);

    speak(data.message);
}

function speak(message) {
    if (synthesis) {
        const utterance = new SpeechSynthesisUtterance(message);
        synthesis.speak(utterance);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also added a new method called &lt;code&gt;speak&lt;/code&gt; to use the Web Speech API, specifically &lt;code&gt;SpeechSynthesis&lt;/code&gt; method to speak. The SendMessage will hit the endpoint we prepared previously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Try the App
&lt;/h3&gt;

&lt;p&gt;Now, run your HTML file, you can use something like VS Code Live server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  press record&lt;/li&gt;
&lt;li&gt;  say anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryfx4dbwod42e682jej5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryfx4dbwod42e682jej5.png" width="732" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI Voice assistant web application screenshot&lt;/p&gt;

&lt;p&gt;Now, you should see your message echo back to you (it's coming from the server). Press the record button again to send a different message.&lt;/p&gt;

&lt;p&gt;Now, we have an app that can listen and speak to us. Let's dive into the AI part!&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart AI assistant
&lt;/h2&gt;

&lt;p&gt;We'll use the &lt;a href="https://platform.openai.com/docs/assistants/overview" rel="noopener noreferrer"&gt;OpenAI Assistants API&lt;/a&gt; as the brain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install the OpenAI package&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We've previously installed the OpenAI package for NodeJS. Make sure you've installed it as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Grab your API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Get your API Key from the OpenAI dashboard. Create a new &lt;code&gt;.env&lt;/code&gt; file and paste it there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=YOUR_API_KEY_FROM_OPENAI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Import OpenAI and dotenv&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;require("dotenv").config();
const OpenAI = require('openai');
const { OPENAI_API_KEY } = process.env;

// Set up OpenAI Client
const openai = new OpenAI({
    apiKey: OPENAI_API_KEY,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Implement assistants API&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you're not familiar with the assistants API, I suggest to read this introduction blog post first:&lt;/p&gt;

&lt;p&gt;[&lt;/p&gt;

&lt;p&gt;Assistant API by OpenAI (Basic Tutorial)&lt;/p&gt;

&lt;p&gt;Learn the basics of Assistant API by OpenAI. We’ll create a super simple coding example to understand the barebone of Assistant API: &lt;a href="https://serpapi.com/blog/assistant-api-openai-beginner-tutorial/" rel="noopener noreferrer"&gt;https://serpapi.com/blog/assistant-api-openai-beginner-tutorial/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll use the same logic and code for this tutorial (with some updates soon). You can also get the code sample for the API assistants here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hilmanski/assistant-API-openai-nodejs-sample" rel="noopener noreferrer"&gt;GitHub - hilmanski/assistant-API-openai-nodejs-sample: A simple example for Assistant API by OpenAI using NodeJS&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I won't explain and show the whole code here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Add the assistants API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need to tell the AI assistants that they will act as general helpers who can help us with anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkatth9r7upn2xoz1wu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkatth9r7upn2xoz1wu6.png" alt="Create the AI assistant via OpenAI dashboard" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;*Make sure to update the assistant_id key on your code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Assign a thread ID&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Since the API needs a thread unique ID for each conversation, I'll add a new fetch request on our frontend to automatically ask for a thread id on the first visit. The ID will also updated on browser refresh.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reminder: we have this route to create a thread ID from the OpenAI assistant tutorial&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Open a new thread
app.get('/thread', (req, res) =&amp;gt; {
    createThread().then(thread =&amp;gt; {
        res.json({ threadId: thread.id });
    });
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add this block to our HTML file (frontend part)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let threadId = null;

// onload
window.onload = function() {
    fetch('http://localhost:3000/thread')
        .then(response =&amp;gt; response.json())
        .then(data =&amp;gt; {
            console.log(data);
            assistant_id = data.threadId;
        });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll adjust our &lt;code&gt;sendMessage&lt;/code&gt; method to attach the &lt;code&gt;threadId&lt;/code&gt; when sending a message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; async function sendMessage(message) {
    const response = await fetch('http://localhost:3000/message', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ message, threadId }) // &amp;lt;- update here
    });

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 7: Return the last message only&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's update our &lt;code&gt;checkingStatus&lt;/code&gt; method to only return the latest message from the AI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function checkingStatus(res, threadId, runId) {
    const runObject = await openai.beta.threads.runs.retrieve(
        threadId,
        runId
    );

    const status = runObject.status;
    console.log(runObject)
    console.log('Current status: ' + status);

    if(status == 'completed') {
        clearInterval(pollingInterval);

        const messagesList = await openai.beta.threads.messages.list(threadId);
        const lastMessage = messagesList.body.data[0].content[0].text.value

        res.json({ message: lastMessage });
    }
}

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

&lt;/div&gt;



&lt;p&gt;Our voice assistant is ready!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8: Show all messages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to add all previous transcriptions to the user interface, here is the code to collect all the previous messages in the div.&lt;/p&gt;

&lt;p&gt;First, every time we speak:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;recognition.onresult = async function(event) {
    // Get the latest transcript 
    const lastItem = event.results[event.results.length - 1]
    const transcript = lastItem[0].transcript;

    // Update: Append new text to div
    const newText = "&amp;lt;p&amp;gt;" + transcript + "&amp;lt;/p&amp;gt;";
    document.getElementById('output').insertAdjacentHTML("afterbegin", newText);

    recognition.stop();
    await sendMessage(transcript);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, every time we got a response from the AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function sendMessage(message) {
    console.log('Sending message: ', threadId);
    const response = await fetch('http://localhost:3000/message', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ message, threadId })
    });

    const data = await response.json();

    // update: add new text here
    const newText = "&amp;lt;p&amp;gt;" + data.message + "&amp;lt;/p&amp;gt;";
    document.getElementById('output').insertAdjacentHTML("afterbegin", newText);

    speak(data.message);
}

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Make sure to re-run your NodeJS app. Now try to have a chat with your AI!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Things we can improve
&lt;/h2&gt;

&lt;p&gt;There are several things we can improve for this voice assistant&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Instruction&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Since we're building a voice assistant, it shouldn't explain things too long for a simple question. We can adjust the instructions 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;You're a general AI assistant that can help with anything. You're a voice assistant, so don't speak too much, make it clear and concise.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Voice and listening&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We use native browser API to listen and speak. Better alternatives exist, such as Elevenlabs, AssemblyAI, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowledge Limitation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We're using one of the OpenAI models, where the knowledge is cut off at a particular year. We can expand its knowledge by providing PDF files or &lt;a href="https://serpapi.com/blog/connect-assistant-api-to-the-internet-openai-x-google/" rel="noopener noreferrer"&gt;connecting the assistant API to the internet.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
    </item>
    <item>
      <title>Beautiful Soup: Web Scraping with Python (Beginner friendly)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Tue, 05 Mar 2024 02:22:05 +0000</pubDate>
      <link>https://forem.com/serpapi/beautiful-soup-web-scraping-with-python-beginner-friendly-45gd</link>
      <guid>https://forem.com/serpapi/beautiful-soup-web-scraping-with-python-beginner-friendly-45gd</guid>
      <description>&lt;p&gt;Imagine you're looking for a shiny new monitor to perfect your setup. You've found the perfect model on a popular shopping site, but there's a catch – it's just a bit outside your budget. You know prices fluctuate, often dropping during sales or special promotions, but checking the website daily is a tedious task that wastes your valuable time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Instead of manually monitoring the site, we can build a simple yet effective program that checks the price for you&lt;/em&gt;. This is where web scraping comes into play, and Beautiful Soup is your ally!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is beautiful soup?
&lt;/h2&gt;

&lt;p&gt;Beautiful Soup is a Python library designed to help you easily extract information from web pages by parsing HTML and XML documents.&lt;br&gt;&lt;br&gt;
Link: &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Beautiful soup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beautiful Soup is a versatile tool that can be used to extract all kinds of data from web pages, not just price information. Whether you're interested in headlines from a news website, comments from a forum, product details from an e-commerce site, or any other information, Beautiful Soup can help you automate the extraction process efficiently.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You might also interested in reading this post&lt;/em&gt;&lt;br&gt;
&lt;a href="https://serpapi.com/blog/python-web-scraping-tutorial/#step-by-step-basic-web-scraping-tutorial-in-python" rel="noopener noreferrer"&gt;Learn web scraping in Python for beginner&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Basic understanding of Python.&lt;/li&gt;
&lt;li&gt;  Python is installed on your machine.&lt;/li&gt;
&lt;li&gt;  PIP for installing Python packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a basic tutorial on web scraping with Python. We will use two popular libraries: &lt;a href="https://requests.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; for making HTTP requests and &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;Beautiful Soup&lt;/code&gt;&lt;/a&gt; for parsing HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Necessary Libraries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
First, you need to install the &lt;code&gt;requests&lt;/code&gt; and &lt;code&gt;BeautifulSoup&lt;/code&gt; libraries. You can do this using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests beautifulsoup4

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Import Libraries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In your Python script or Jupyter Notebook, import the necessary modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Make an HTTP Request&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Choose a website you want to scrape and send a GET request to it. For this example, let's scrape Google's homepage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url = 'https://google.com'
response = requests.get(url)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Parse the HTML Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Once you have the HTML content, you can use Beautiful Soup to parse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;soup = BeautifulSoup(response.text, 'html.parser')

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Extract Data&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now, you can extract data from the HTML. Let's say you want to extract all the headings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;headings = soup.find_all('div')
for heading in headings:
    print(heading.text.strip())

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 6: Handle Errors&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Always make sure to handle errors like bad requests or connection problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if response.status_code == 200:
    # Proceed with scraping
    # ...
else:
    print("Failed to retrieve the web page")

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need two primary tools to perform web scraping in Python: HTTP Client and HTML Parser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  An HTTP API Client to fetch web pages.
e.g. requests, urllib, &lt;a href="https://serpapi.com/blog/python-curl-and-alternative/" rel="noopener noreferrer"&gt;pycurl&lt;/a&gt; or httpx&lt;/li&gt;
&lt;li&gt;  An HTML parser to extract data from the fetched pages.
e.g. Beautiful Soup, lxml, or pyquery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Inspect the page first!
&lt;/h2&gt;

&lt;p&gt;If we want to scrape specific data from a website, we need to know where this data is located.&lt;/p&gt;

&lt;p&gt;Websites are built using HTML, which organizes content in a nested structure of elements like headings, paragraphs, and divs. Each element can have attributes like class and id, which are useful for identifying specific page parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To effectively use Beautiful Soup to extract data, you need to inspect the website and identify the elements that contain the data you're interested in.&lt;/strong&gt; This process involves using a web browser's developer tools to look at the HTML structure of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ej23r5kbk680c2pktue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ej23r5kbk680c2pktue.png" alt="Inspect HTML element page." width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By understanding how the content is organized, you can write more precise and efficient Beautiful Soup code to target exactly what you need, avoiding unnecessary processing and ensuring you get accurate data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world example using Beautiful Soup
&lt;/h2&gt;

&lt;p&gt;Let's practice! For this example, we're going to scrape the price of the item in this website: &lt;a href="https://webscraper.io/test-sites/e-commerce/allinone/computers" rel="noopener noreferrer"&gt;https://webscraper.io/test-sites/e-commerce/allinone/computers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Inspect the page&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Remember always to inspect the page first. We can do this by right-clicking on the page and clicking inspect. You can see the HTML structure in the &lt;code&gt;elements&lt;/code&gt; tab.&lt;/p&gt;

&lt;p&gt;I can see the price is wrapped in the &lt;code&gt;card&lt;/code&gt; element, and specifically on the &lt;code&gt;price&lt;/code&gt; CSS class.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgk5r45cktvfl31n912x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgk5r45cktvfl31n912x.png" alt="Inspect page simulation for web scraping." width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Make an HTTP request&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your Python script, import the necessary modules &lt;em&gt;(Make sure to install BeautifSoup first!)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup

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

&lt;/div&gt;



&lt;p&gt;Choose a website you want to scrape and send a GET request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url = 'https://webscraper.io/test-sites/e-commerce/allinone/computers'
response = requests.get(url)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Parse the HTML Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Once you have the HTML content, you can use Beautiful Soup to parse the price information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;soup = BeautifulSoup(response.text, 'html.parser')

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

&lt;/div&gt;



&lt;p&gt;Now we're going to scrape all data from the items, not just price.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Empty list to hold the data
products = []

# Find all product wrapper divs
for card in soup.find_all('div', class_='product-wrapper'):
    # Extract title, description, price, rating, and number of reviews
    title = card.find('a', class_='title').text.strip()
    description = card.find('p', class_='description').text.strip()
    price = card.find('h4', class_='price').text.strip()
    rating = len(card.find('div', class_='ratings').find_all('span', class_='ws-icon-star'))
    reviews = card.find('p', class_='review-count').text.strip().split(' ')[0]  # Assuming "X reviews"

    # Append the data to the products list
    products.append({
        'title': title,
        'description': description,
        'price': price,
        'rating': rating,
        'reviews': reviews
    })

# Display the extracted data
for product in products:
    print(product)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to see this data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklw8mykza0qaz2kfn6b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklw8mykza0qaz2kfn6b9.png" alt="Beautiful soup with Python result example" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can expand your code to save this data on your database or CSV files and compare it later after running the script every day &lt;em&gt;(Pro tip: You can use CRON to automate this process).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing data when there is no ID or class provided
&lt;/h2&gt;

&lt;p&gt;If the HTML structure you're working with lacks specific class or id attributes to easily identify elements, you can still use Beautiful Soup to navigate and extract data based on the hierarchical structure of the HTML.&lt;/p&gt;

&lt;p&gt;For this example, I'll simplify the previous HTML structure, removing specific class names and id attributes and then demonstrate how to extract the same information.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of the HTML structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;img src="/path/to/image1.png"&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;h4&amp;gt;$107.99&amp;lt;/h4&amp;gt;
                &amp;lt;h4&amp;gt;Galaxy Tab 3&amp;lt;/h4&amp;gt;
                &amp;lt;p&amp;gt;7", 8GB, Wi-Fi, Android 4.2, Yellow&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;p&amp;gt;14 reviews&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Rating: ★★★☆☆&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!-- Repeat for other products --&amp;gt;
&amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;In this scenario, you can still extract data by carefully navigating the structure. Let's write Python code using Beautiful Soup to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from bs4 import BeautifulSoup

html_content = """
&amp;lt;div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;img src="/path/to/image1.png"&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;h4&amp;gt;$107.99&amp;lt;/h4&amp;gt;
                &amp;lt;h4&amp;gt;Galaxy Tab 3&amp;lt;/h4&amp;gt;
                &amp;lt;p&amp;gt;7", 8GB, Wi-Fi, Android 4.2, Yellow&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;p&amp;gt;14 reviews&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Rating: ★★★☆☆&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;img src="/path/to/image2.png"&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;h4&amp;gt;$50.01&amp;lt;/h4&amp;gt;
                &amp;lt;h4&amp;gt;Second I phone&amp;lt;/h4&amp;gt;
                &amp;lt;p&amp;gt;Blue night&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;p&amp;gt;10 reviews&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Rating: ★★☆☆☆&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
"""

# Parse the HTML content
soup = BeautifulSoup(html_content, 'html.parser')

# Empty list to hold the data
products = []

# Assuming each product is within the third-level div
for div in soup.div.find_all('div', recursive=False):
     # Extract the image, price, title, description, reviews, and rating
    img_src = div.div.img['src']
    price = div.div.find_next('h4').text
    title = div.div.find_next('h4').find_next_sibling('h4').text
    description = div.div.find('p').text
    reviews = div.div.find_all('p')[1].text.split(' ')[0]
    rating = div.div.find_all('p')[2].text.count('★')

    # Append the product data to the products list
    products.append({
        'img_src': img_src,
        'price': price,
        'title': title,
        'description': description,
        'reviews': reviews,
        'rating': rating
    })

# Display the extracted data
for product in products:
    print(product)


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

&lt;/div&gt;



&lt;p&gt;It demonstrates how you can still extract data without specific class names or ids, by carefully navigating the HTML tree and using the relationships between elements.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>How to scrape a website with Python (Beginner tutorial)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Fri, 23 Feb 2024 00:30:31 +0000</pubDate>
      <link>https://forem.com/serpapi/how-to-scrape-a-website-with-python-beginner-tutorial-2cde</link>
      <guid>https://forem.com/serpapi/how-to-scrape-a-website-with-python-beginner-tutorial-2cde</guid>
      <description>&lt;p&gt;If you've ever been curious about how to extract valuable data from websites, you're in the right place. Web scraping is a powerful tool for gathering information from the internet, and Python, with its rich ecosystem of libraries, makes this task easy for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2faiuid0eb3flw4h36m.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2faiuid0eb3flw4h36m.webp" alt="Web scraping tutorial with Python tutorial" width="595" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this blog post, we'll cover:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  List of tools we can use for web scraping with Python.&lt;/li&gt;
&lt;li&gt;  Simple web scraping for static websites.&lt;/li&gt;
&lt;li&gt;  Using Selenium for dynamic content or Javascript-heavy site/&lt;/li&gt;
&lt;li&gt;  MechanicalSoup to automate some task in browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have a lot of libraries in Python that we can use for scraping data from a website. Here is some of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Category: HTTP Libraries

&lt;ul&gt;
&lt;li&gt;Tool/Library: Requests&lt;/li&gt;
&lt;li&gt;Description: Simple HTTP library for Python, built for human beings.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: urllib&lt;/li&gt;
&lt;li&gt;Description: A module for fetching URLs included with Python.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: urllib3&lt;/li&gt;
&lt;li&gt;Description: A powerful, user-friendly HTTP client for Python.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: httpx&lt;/li&gt;
&lt;li&gt;Description: A fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: Parsing Libraries

&lt;ul&gt;
&lt;li&gt;Tool/Library: Beautiful Soup&lt;/li&gt;
&lt;li&gt;Description: A library for pulling data out of HTML and XML files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: lxml&lt;/li&gt;
&lt;li&gt;Description: Processes XML and HTML in Python, supporting XPath and XSLT.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: pyquery&lt;/li&gt;
&lt;li&gt;Description: A jQuery-like library for parsing HTML.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: Web Drivers

&lt;ul&gt;
&lt;li&gt;Tool/Library: Selenium&lt;/li&gt;
&lt;li&gt;Description: An automated web browser, useful for complex scraping tasks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: Splinter&lt;/li&gt;
&lt;li&gt;Description: Open-source tool for testing web applications.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: Automation Tools

&lt;ul&gt;
&lt;li&gt;Tool/Library: Scrapy&lt;/li&gt;
&lt;li&gt;Description: An open-source web crawling and scraping framework.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: 

&lt;ul&gt;
&lt;li&gt;Tool/Library: MechanicalSoup&lt;/li&gt;
&lt;li&gt;Description: A Python library for automating interaction with websites.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: Data Processing

&lt;ul&gt;
&lt;li&gt;Tool/Library: pandas&lt;/li&gt;
&lt;li&gt;Description: A fast, powerful, flexible and easy-to-use data analysis tool.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category: JavaScript Support

&lt;ul&gt;
&lt;li&gt;Tool/Library: Pyppeteer (Python port of Puppeteer)&lt;/li&gt;
&lt;li&gt;Description: A tool for browser automation and web scraping.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Feel free to suggest if you know any other tools out there!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by Step basic web scraping tutorial in Python
&lt;/h2&gt;

&lt;p&gt;Here's a basic tutorial on web scraping in Python. For this example, we will use two popular libraries: &lt;a href="https://requests.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; for making HTTP requests and &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;Beautiful Soup&lt;/code&gt;&lt;/a&gt; for parsing HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Basic understanding of Python.&lt;/li&gt;
&lt;li&gt;  Python is installed on your machine.&lt;/li&gt;
&lt;li&gt;  PIP for installing Python packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Necessary Libraries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
First, you need to install the &lt;code&gt;requests&lt;/code&gt; and &lt;code&gt;BeautifulSoup&lt;/code&gt; libraries. You can do this using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests beautifulsoup4

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Import Libraries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In your Python script or Jupyter Notebook, import the necessary modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Make an HTTP Request&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Choose a website you want to scrape and send a GET request to it. For this example, let's scrape Google's homepage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url = 'https://google.com'
response = requests.get(url)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Parse the HTML Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Once you have the HTML content, you can use Beautiful Soup to parse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;soup = BeautifulSoup(response.text, 'html.parser')

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Extract Data&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now, you can extract data from the HTML. Let's say you want to extract all the headings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;headings = soup.find_all('div')
for heading in headings:
    print(heading.text.strip())

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 6: Handle Errors&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Always make sure to handle errors like bad requests or connection problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if response.status_code == 200:
    # Proceed with scraping
    # ...
else:
    print("Failed to retrieve the web page")

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need two primary tools to perform web scraping in Python: HTTP Client and HTML Parser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  An HTTP API Client to fetch web pages.
e.g. requests, urllib, &lt;a href="https://serpapi.com/blog/python-curl-and-alternative/" rel="noopener noreferrer"&gt;pycurl&lt;/a&gt; or httpx&lt;/li&gt;
&lt;li&gt;  An HTML parser to extract data from the fetched pages.
e.g. Beautiful Soup, lxml, or pyquery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a concrete example on how to use these tools on a real world use case: &lt;a href="https://serpapi.com/blog/how-to-scrape-google-search-results-with-python/#scraping-google-search-results-with-python-and-beautifulsoup" rel="noopener noreferrer"&gt;How to scrape Google search results with Python&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by Step scraping dynamic content in Python
&lt;/h2&gt;

&lt;p&gt;What if the content you want to scrape is not loaded initially? Sometimes, the data hides behind a user interaction. To scrape dynamic content in Python, which often involves interacting with JavaScript, you'll typically use &lt;a href="https://www.selenium.dev/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unlike the requests and BeautifulSoup combination, which works well for static content, Selenium can handle dynamic websites by automating a web browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Basic knowledge of Python and web scraping (as covered in the previous lesson).&lt;/li&gt;
&lt;li&gt;  Python is installed on your machine.&lt;/li&gt;
&lt;li&gt;  Selenium package and a WebDriver installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Selenium&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
First, install Selenium using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install selenium

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Download WebDriver&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You'll need a WebDriver for the browser you want to automate (e.g., Chrome, Firefox). For Chrome, download &lt;a href="https://sites.google.com/a/chromium.org/chromedriver/downloads" rel="noopener noreferrer"&gt;ChromeDriver&lt;/a&gt;. Make sure the WebDriver version matches your browser version. Place the WebDriver in a known directory or update the system path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Import Selenium and Initialize WebDriver&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Import Selenium and initialize the WebDriver in your script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from selenium import webdriver

driver = webdriver.Chrome()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Fetch Dynamic Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Open a website and fetch its dynamic content. Let's use &lt;code&gt;http://example.com&lt;/code&gt; as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url = 'https://google.com'
driver.get(url)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Print title&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is an example of how to get a certain element on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(driver.title)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to run this script. You'll see a new browser pop up and open the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Interact with the Page (if necessary)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you need to interact with the page (like clicking buttons or filling forms), you can do so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
submit_button.click()

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 7: Scrape Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now, you can scrape the content. For example, to get all paragraphs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paragraphs = driver.find_elements_by_tag_name('p')
for paragraph in paragraphs:
    print(paragraph.text)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 8: Close the Browser&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Once done, don't forget to close the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;driver.quit()

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Additional Tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Selenium can perform almost all actions that you can do manually in a browser.&lt;/li&gt;
&lt;li&gt;  For complex web pages, consider using explicit waits to wait for elements to load.&lt;/li&gt;
&lt;li&gt;  Remember to handle exceptions and errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a video tutorial on using Selenium for automation in Python by NeuralNine on Youtube.&lt;/p&gt;

&lt;h2&gt;
  
  
  A basic example of web scraping using MechanicalSoup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mechanicalsoup.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;MechanicalSoup&lt;/a&gt; is a Python library for web scraping that combines the simplicity of Requests with the convenience of BeautifulSoup. It's particularly useful for interacting with web forms, like login pages. Here's a basic example to illustrate how you can use MechanicalSoup for web scraping:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that MechanicalSoup doesn't handle javascript loaded content. That's a task for Selenium 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Python is installed on your machine.&lt;/li&gt;
&lt;li&gt;  Basic understanding of Python and HTML.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install MechanicalSoup&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can install MechanicalSoup via pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install mechanicalsoup

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Import MechanicalSoup&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In your Python script, import MechanicalSoup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import mechanicalsoup

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Create a Browser Object&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
MechanicalSoup provides a &lt;code&gt;Browser&lt;/code&gt; class, which you'll use to interact with web pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;browser = mechanicalsoup.StatefulBrowser()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Make a Request&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let's say you want to scrape data from a simple example page. You can use the &lt;code&gt;Browser&lt;/code&gt; object to open the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url = 'https://google.com'
print(browser.get(url))

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Parse the HTML Content&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The &lt;code&gt;page&lt;/code&gt; variable now contains the response from the website. You can access the BeautifulSoup object via &lt;code&gt;browser.page&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page = browser.page
print(page)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 6: Extract Data&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now, you can extract data using BeautifulSoup methods. For example, to get all paragraphs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page = browser.page
pTags = page.find_all('p')
print(pTags)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 7: Handling Forms (Optional)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you need to interact with forms, you can do so easily.&lt;/p&gt;

&lt;p&gt;Given this HTML content on a page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form action="/pages/forms/" class="form form-inline" method="GET"&amp;gt;
&amp;lt;label for="q"&amp;gt;Search for Teams:  &amp;lt;/label&amp;gt;
&amp;lt;input class="form-control" id="q" name="q" placeholder="Search for Teams" type="text"/&amp;gt;
&amp;lt;input class="btn btn-primary" type="submit" value="Search"/&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To submit a search query on a form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Select the form
browser.select_form('form')

# Fill the form with your query
browser['q'] = 'red'

# Submit the form
response = browser.submit_selected()

# Print the URL (assuming the form is correctly submitted and a new page is loaded)
print("Form submitted to:", response.url)

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

&lt;/div&gt;



&lt;p&gt;What if you have multiple forms on the page?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;select_form&lt;/code&gt; and another method in MechanicalSoup usually accept a CSS selector parameter. So, whether it's id or class you can always name it specifically there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When to use MechanicalSoup (From their documentation)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
MechanicalSoup is designed to simulate the behavior of a human using a web browser. Possible use-case include:&lt;br&gt;&lt;br&gt;
- Interacting with a website that doesn’t provide a webservice API, out of a browser.&lt;br&gt;&lt;br&gt;
- Testing a website you’re developing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why use Python for web scraping?
&lt;/h2&gt;

&lt;p&gt;Python is a popular choice for web scraping for several reasons. Here are the top three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Seamless Integration with Data Science Tools&lt;/strong&gt;: After scraping data from the web, you often need to clean, analyze, and visualize this data, which is where Python's data science capabilities come in handy. Tools like Pandas, NumPy, and Matplotlib integrate seamlessly with web scraping libraries, allowing for an efficient end-to-end process. Here's a bit more detail on each:&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Rich Ecosystem of Libraries&lt;/strong&gt;: Python has a vast selection of libraries specifically designed for web scraping, such as Beautiful Soup, Scrapy, Selenium, Requests, and MechanicalSoup. These libraries simplify the process of extracting data from websites, parsing HTML and XML, handling HTTP requests, and even interacting with JavaScript-heavy sites. This rich ecosystem means that Python offers a tool for almost every web scraping need, from simple static pages to complex, dynamic web applications.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Ease of Learning and Use&lt;/strong&gt;: Python is known for its simplicity and readability, making it an excellent choice for beginners and experienced programmers alike. Its straightforward syntax allows developers to write less code compared to many other programming languages, making the process of writing and understanding web scraping scripts easier and faster. This ease of use is particularly beneficial in web scraping, where scripts can often become complex and difficult to manage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it! I hope you enjoy this tutorial!&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
    </item>
    <item>
      <title>Scrape Google Trends with Python (simple API, pytrends alternative)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Wed, 31 Jan 2024 03:29:45 +0000</pubDate>
      <link>https://forem.com/serpapi/scrape-google-trends-with-python-simple-api-pytrends-alternative-421k</link>
      <guid>https://forem.com/serpapi/scrape-google-trends-with-python-simple-api-pytrends-alternative-421k</guid>
      <description>&lt;p&gt;We are surrounded by tons of data. But what's the point if we can't understand it all? That's where Google Trends comes in. Google Trends offers valuable insights into what people search for on the internet. Let's dive in and see how we can programmatically scrape Google Trends data using Python (Psst.. no PyTrends needed!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjorsgq3ibxgsg00d8rul.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjorsgq3ibxgsg00d8rul.webp" alt="Scraping Google Trends data using Python API" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step scraping Google Trends data with Python
&lt;/h2&gt;

&lt;p&gt;Without further ado, let's start and collect data from Google Trends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Tools
&lt;/h3&gt;

&lt;p&gt;We'll use the new official Python library by SerpApi: &lt;a href="https://github.com/serpapi/serpapi-python" rel="noopener noreferrer"&gt;serpapi-python&lt;/a&gt; .&lt;br&gt;&lt;br&gt;
That's the only tool that we need!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlbn09heyyzto1o6ih9k.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlbn09heyyzto1o6ih9k.webp" alt="Python library from SerpApi, the tool we need to scrape Google SERP" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a side note: You can use this library to scrape search results from other search engines, not just Google.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usually, you'll write your DIY solution using &lt;a href="https://github.com/GeneralMills/pytrends" rel="noopener noreferrer"&gt;PyTrends&lt;/a&gt;, BeautifulSoup, Selenium, Scrapy, Requests, etc., to scrape Google Trends results. You can relax now since we perform all these heavy tasks for you. &lt;strong&gt;So, you don't need to worry about all the problems you might've encountered while implementing your web scraping solution.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Setup and preparation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Sign up for free at &lt;a href="https://serpapi.com/" rel="noopener noreferrer"&gt;SerpApi&lt;/a&gt;. You can get 100 free searches per month.&lt;/li&gt;
&lt;li&gt;  Get your SerpApi Api Key from &lt;a href="https://serpapi.com/manage-api-key" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Create a new &lt;code&gt;.env&lt;/code&gt; file, and assign a new env variable with value from API_KEY above.
&lt;code&gt;SERPAPI_KEY=$YOUR_SERPAPI_KEY_HERE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Install python-dotenv to read the &lt;code&gt;.env&lt;/code&gt; file with
&lt;code&gt;pip install python-dotenv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Install SerpApi's Python library with
&lt;code&gt;pip install serpapi&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Create new &lt;code&gt;main.py&lt;/code&gt; file for the main program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your folder structure will look 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;|_ .env
|_ main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Write the code for scraping Google Trends
&lt;/h3&gt;

&lt;p&gt;Let's say I want to get this result:&lt;br&gt;&lt;br&gt;
- Keyword: 'standing desk'&lt;br&gt;&lt;br&gt;
- Region: 'worldwide'&lt;br&gt;&lt;br&gt;
- Time: 'Past 12 months'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp18dw2ao88slo2o7uau.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp18dw2ao88slo2o7uau.webp" alt="sample Google Trends live result" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the Python code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import serpapi
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv('SERPAPI_KEY')

client = serpapi.Client(api_key=api_key)
search =  client.search(
    engine="google_trends",
    q="standing desk",
    api_key=api_key,
  )

print(search)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the Google Trends API will set the location for &lt;code&gt;worldwide&lt;/code&gt; and duration of the &lt;code&gt;past 12 months&lt;/code&gt; . We'll take a look at how to change these values later. For now, take a look at our perfect result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjffcimye7x26o1jlf5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjffcimye7x26o1jlf5.webp" alt="Google Trends API result on search inspector" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note, that we only return the response in JSON format. You'll need to build the graph yourself if you need one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Adjust region and timeline
&lt;/h3&gt;

&lt;p&gt;Let's change the settings. I set the region to &lt;code&gt;United States&lt;/code&gt; and timeline to &lt;code&gt;Past 30 days&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ugkldvpeetvd6cziw6v.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ugkldvpeetvd6cziw6v.webp" alt="sample Google trends for the past 30 days" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how it is represented in our code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search =  client.search(
    engine="google_trends",
    q="standing desk",
    api_key=api_key,
    geo="US", // Two-code-letter country code
    date="today 1-m" // Timeline for last 1 month
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is perfect as well:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypktxztagq5ftc7pnrsc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypktxztagq5ftc7pnrsc.webp" alt="Google trends API result" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More information about available parameters: &lt;a href="https://serpapi.com/google-trends-api" rel="noopener noreferrer"&gt;https://serpapi.com/google-trends-api&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SerpApi Google Trends API features
&lt;/h2&gt;

&lt;p&gt;Using SerpApi, it's possible to grab other parts from Google Trends, like the breakdown by region result, interest by region, related queries, related topics, and what's trending now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Searching for multiple terms&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It's possible to search for multiple terms. You can separate the terms by a comma. Here are the limitations:&lt;br&gt;&lt;br&gt;
- Max 5 terms per search&lt;br&gt;&lt;br&gt;
- Max 100 characters per search&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search =  client.search(
    engine="google_trends",
    q="standing desk, ergonomic chair",
    api_key=api_key,
)

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Interest by Region&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We can grab the data for interest by region. We need to change the &lt;code&gt;data_type&lt;/code&gt; to &lt;code&gt;GEO_MAP_0&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search = client.search(
    engine="google_trends",
    q="coffee latte",
    date="today 12-m",
    tz="420",
    data_type="GEO_MAP_0", // Important part
    api_key=api_key,
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the list of possible values for &lt;code&gt;data_type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8cuesslpq13easv1mon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8cuesslpq13easv1mon.png" alt="data types possible values" width="656" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, if you're interested in the related topics, you need to adjust your code to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search = client.search(
    engine="google_trends",
    q="coffee latte",
    date="today 12-m",
    tz="420",
    data_type="RELATED_TOPICS", // Adjust this accordingly
    api_key=api_key,
)

print(search)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;References to our other related API:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://serpapi.com/google-trends-autocomplete" rel="noopener noreferrer"&gt;Trends Autocomplete API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-compared-breakdown" rel="noopener noreferrer"&gt;Trends Breakdown by region API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-interest-by-region" rel="noopener noreferrer"&gt;Trends by Region API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-interest-over-time" rel="noopener noreferrer"&gt;Interest overtime API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-related-queries" rel="noopener noreferrer"&gt;Trends related queries API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-related-topics" rel="noopener noreferrer"&gt;Trends related topics API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serpapi.com/google-trends-trending-now" rel="noopener noreferrer"&gt;Trending Now API&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pytrends Tutorial to scrape Google Trends data
&lt;/h2&gt;

&lt;p&gt;There is also another unofficial API for Google Trends for Python. It's called Pytrends. We'll see a basic tutorial on how to implement this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that the repository has not been very active these last few months. There are also some unadressed issues. Therefore, we recommend using &lt;a href="https://serpapi.com/google-trends-api" rel="noopener noreferrer"&gt;Google Trends API&lt;/a&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Install Pytrends&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pytrends
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize the object. We also initialize &lt;a href="https://pandas.pydata.org/" rel="noopener noreferrer"&gt;&lt;code&gt;pandas&lt;/code&gt;&lt;/a&gt; so it's easier to read the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from pytrends.request import TrendReq
import pandas as pd

pytrend = TrendReq()
pytrend.build_payload(kw_list=['Standing Desk'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;build_payload&lt;/code&gt; method, we can put the keywords list in an array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interest Over Time&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;# Interest over time
pytrend = TrendReq()
pytrend.build_payload(kw_list=['Standing Desk'])
df = pytrend.interest_over_time()
print(df)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5l0fkqchf7a5bvd6edh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5l0fkqchf7a5bvd6edh.png" alt="Pytrends tutorial result" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some other parameters for &lt;code&gt;build_payload&lt;/code&gt; method. Unfortunately, I sometimes get error results. Here are the parameters you can play with: &lt;code&gt;year_start, month_start, day_start, hour_start, year_end, month_end, day_end, hour_end.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interest by Region&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is how to split the trends by country and sort the top 10 results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytrend = TrendReq()
pytrend.build_payload(kw_list=['Standing Desk'])
df = pytrend.interest_by_region(resolution='COUNTRY')
print(df.sort_values(by='Standing Desk', ascending=False).head(10))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Related topics&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is how to show related topics using Pytrends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pytrend.related_topics()
print(df)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Related queries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is how to show related topics using Pytrends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pytrend.related_queries()
print(df)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trending searches&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is how to display trending searches by Region from Google Trends&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Trending Searches
df = pytrend.trending_searches(pn='united_states') # trending searches in real time for United States
print(df.head)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following API methods are available for PyTrends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Interest Over Time: returns historical, indexed data for when the keyword was searched most as shown on Google Trends' Interest Over Time section.&lt;/li&gt;
&lt;li&gt;  Multirange Interest Over Time: returns historical, indexed data similar to interest over time but across multiple time date ranges.&lt;/li&gt;
&lt;li&gt;  Historical Hourly Interest: returns historical, indexed, hourly data for when the keyword was searched most, as shown on Google Trends' Interest Over Time section. It sends multiple requests to Google, each retrieving one week of hourly data. It seems like this would be the only way to get historical, hourly data.&lt;/li&gt;
&lt;li&gt;  Interest by Region: This returns data for where the keyword is most searched, as shown in Google Trends' Interest by Region section.&lt;/li&gt;
&lt;li&gt;  Related Topics: returns data for the related keywords to a provided keyword shown on Google Trends' Related Topics section.&lt;/li&gt;
&lt;li&gt;  Related Queries: returns data for the related keywords to a provided keyword shown on Google Trends' Related Queries section.&lt;/li&gt;
&lt;li&gt;  Trending Searches: returns data for the latest trending searches shown on Google Trends' Trending Searches section.&lt;/li&gt;
&lt;li&gt;  Top Charts: returns the data for a given topic shown in Google Trends' Top Charts section.&lt;/li&gt;
&lt;li&gt;  Suggestions: Return a list of additional suggested keywords that can be used to refine a trend search.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Thank you for reading this blog post! I hope it can help you to gather data from the Google Trends site.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We have a Google Trends playground where you can play with our API:&lt;/em&gt; &lt;a href="https://serpapi.com/playground?engine=google_trends" rel="noopener noreferrer"&gt;https://serpapi.com/playground?engine=google_trends&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you in the next post!&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
    </item>
    <item>
      <title>Image data parsing: From Image to data (Using Vision API)</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Thu, 18 Jan 2024 00:24:45 +0000</pubDate>
      <link>https://forem.com/serpapi/image-data-parsing-from-image-to-data-using-vision-api-3j05</link>
      <guid>https://forem.com/serpapi/image-data-parsing-from-image-to-data-using-vision-api-3j05</guid>
      <description>&lt;p&gt;The AI becomes &lt;del&gt;scarier&lt;/del&gt; better every day. OpenAI now offers the vision API, which allows you to extract information from an image.&lt;/p&gt;

&lt;p&gt;We'll learn how to use Vision API by OpenAI in a simple image and extract data from complex images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ln15m9enlwezse72s9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ln15m9enlwezse72s9.webp" alt="OpenAI vision API to scrape data from an image" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;_We experimented with parsing HTML raw data with AI before, feel free to read the blog post: &lt;a href="https://serpapi.com/blog/web-scraping-and-parsing-experiment-with-ai-openai/" rel="noopener noreferrer"&gt;Web scraping experiment with AI (Parsing HTML with GPT-4)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Vision API tutorial step-by-step
&lt;/h2&gt;

&lt;p&gt;Let's start with setting up a project to test the Vision API. I'll be using Javascript (Nodejs) in this sample, but feel free to use any language you're comfortable with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a new directory and initialize NPM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir openai-vision-api &amp;amp;&amp;amp; cd openai-vision-api 
npm init -y // NPM init
npm install openai dotenv --save  // Install openai and dotenv package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add API Key&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Get your API Key from openAI dashboard, and put it in the &lt;code&gt;.env&lt;/code&gt; file. Feel free to create a new &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Basic code setup&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a new &lt;code&gt;index.js&lt;/code&gt; file and import related packages and create a new openai instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("dotenv").config();
const OpenAI = require('openai');

const { OPENAI_API_KEY } = process.env;

const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add vision API method&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is how to call a vision API in your code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function main() {
  const response = await openai.chat.completions.create({
    model: "gpt-4-vision-preview",
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "What’s in this image?" },
          {
            type: "image_url",
            image_url: {
              "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
            },
          },
        ],
      },
    ]
  });
  console.log(response.choices[0].message.content);
}
main();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the program with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node index.js 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxzl6dgi4wghinz7b2c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxzl6dgi4wghinz7b2c5.png" alt="Simple example of Vision API" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing data from complex image with Vision API
&lt;/h2&gt;

&lt;p&gt;We saw it worked with a simple image. Now, let's try for a complex one. I'm going to take a screenshot from Google Shopping results.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'll upload this image, to use the public URL on our Vision API&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco312zhbnx79h8qj1e2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco312zhbnx79h8qj1e2v.png" alt="Google shopping results screenshot for coffee" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I need to update two things: first, the token parameter since the response should be longer. Second is the prompt, to tell exactly what I want from the AI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function main() {
  const response = await openai.chat.completions.create({
    model: "gpt-4-vision-preview",
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "Please share the detail information of each item on this product on a nice structure JSON" },
          {
            type: "image_url",
            image_url: {
              "url": "https://i.ibb.co/F8nGWk5/Clean-Shot-2024-01-17-at-13-46-43.png",
            },
          },
        ],
      },
    ],
    max_tokens: 1000 // Add more token
  });
  console.log(response.choices[0].message.content);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddlz3zjos15s91agdmfh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddlz3zjos15s91agdmfh.png" alt="Vision API result for a complex image" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result is very good! but here is the catch:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
- The response is not always consistent (structure wise). I believe we can solve this by adjusting our prompt&lt;br&gt;&lt;br&gt;
- The time taken for this particular image is range between 10+ to 20+ seconds. (It's just the parsing time, not the scraping time).&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we use this as a web scraping solution?
&lt;/h2&gt;

&lt;p&gt;As you might know, parsing data is just a part of web scraping. There are other things involved like proxy rotation, solving captcha, and so on. So we can't say that vision API is a web scraping solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is the idea though, of how to use this as part of our web scraping solution:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
- Create a scraping solution, for example &lt;a href="https://serpapi.com/blog/web-scraping-dynamic-website-with-puppeteer/#how-to-take-screenshots-in-puppeteer" rel="noopener noreferrer"&gt;using Puppeteer in Javascript to take a screenshot&lt;/a&gt; .&lt;br&gt;&lt;br&gt;
- Upload the image to a public URL or get the base64 code&lt;br&gt;&lt;br&gt;
- Pass this image to the vision API method parameter like the one we provided above.&lt;br&gt;&lt;br&gt;
- Return the results in a nice structured way.&lt;br&gt;&lt;br&gt;
- (Bonus) If you want to have a consistent data structure, you might want to learn about &lt;a href="https://platform.openai.com/docs/guides/function-calling" rel="noopener noreferrer"&gt;function calling by OpenAI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;It's very fun to experiment with OpenAI features like vision API and see the possibility to help us with web scraping and parsing.&lt;/p&gt;

&lt;p&gt;In above example, where we try to parse the Google Shopping results page data, it's still far from ready for production, compare to the &lt;a href="https://serpapi.com/google-shopping-api" rel="noopener noreferrer"&gt;Google Shopping API&lt;/a&gt;, which only take 1-3s to scrape and return the Google Shopping page in a consistent structured format.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How much does vision API cost?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Model &lt;code&gt;gpt-4-1106-vision-preview&lt;/code&gt; costs &lt;code&gt;$0.01 / 1K tokens&lt;/code&gt; for the input and &lt;code&gt;$0.03/1K tokens&lt;/code&gt; for the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it support function calling?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not right now, the &lt;code&gt;gpt-4-1106-vision-preview&lt;/code&gt; haven't support function calling yet (Per 17th January 2024).&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://platform.openai.com/docs/guides/vision" rel="noopener noreferrer"&gt;OpenAI Vision API&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>openai</category>
      <category>ai</category>
    </item>
    <item>
      <title>Scraping the full snippet from Google search result</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Tue, 02 Jan 2024 00:28:59 +0000</pubDate>
      <link>https://forem.com/serpapi/scraping-the-full-snippet-from-google-search-result-2i8</link>
      <guid>https://forem.com/serpapi/scraping-the-full-snippet-from-google-search-result-2i8</guid>
      <description>&lt;p&gt;Sometimes, you see truncated text on a Google search result like this (...) . Google doesn't always display the meta description of a website. Sometimes, it gets a snippet of relevant text to the search query, which could truncate the text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qkqptniopyvoaitqcxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qkqptniopyvoaitqcxw.png" alt="Example of truncated text result" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wonder how you can get the entire snippet of this search result? Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;The idea is to visit the page URL and scrape part of the relevant text until the next period sign or the whole paragraph.&lt;/p&gt;

&lt;p&gt;But before that, we need to find the Google search results list. Therefore, we will use &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;Google search API by SerpApi&lt;/a&gt; to scrape the Google SERP.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can use any programming language you want, but I'll use Go Lang for this sample.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping Google SERP list with Go lang
&lt;/h2&gt;

&lt;p&gt;First, let's get the Google organic results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1:
&lt;/h3&gt;

&lt;p&gt;Get your SerpApi key&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Sign up for free at &lt;a href="https://serpapi.com/" rel="noopener noreferrer"&gt;SerpApi&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Get your SerpApi Api Key from &lt;a href="https://serpapi.com/manage-api-key" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2:
&lt;/h3&gt;

&lt;p&gt;Create a new Go Lang project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir fullsnippet &amp;amp;&amp;amp; cd fullsnippet // Create a new folder and move 
touch main.go // Create a new go file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3:
&lt;/h3&gt;

&lt;p&gt;Install &lt;a href="https://serpapi.com/blog/scraping-the-full-snippet-from-google-search-result/go%20get%20-u%20github.com/serpapi/serpapi-golang" rel="noopener noreferrer"&gt;Golang SerpApi package&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init project-snippet // Initialize Go Module
go get -u github.com/serpapi/serpapi-golang // Install Go lang package by SerpApi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4:
&lt;/h3&gt;

&lt;p&gt;This is how to get the organic_results from Google SERP&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    "github.com/serpapi/serpapi-golang"
)

const API_KEY = "YOUR_API_KEY"

func main() {
    client_parameter := map[string]string{
        "engine": "google",
        "api_key": API_KEY,
    }
    client := serpapi.NewClient(client_parameter)

    parameter := map[string]string{ 
        "q": "why the sky is blue", // Feel free to change with any keyword
    }

    data, err := client.Search(parameter)
    fmt.Println(data["organic_results"])

    if err != nil {
        fmt.Println(err)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've received each result's title, description, link, and other information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collect only specific data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can collect and display only specific data in a variable 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;data, err := client.Search(parameter)

type OrganicResult struct {
    Title string
    Snippet string
    Link string
}

var organic_results []OrganicResult

for _, result := range data["organic_results"].([]interface{}) {
    result := result.(map[string]interface{})
    organic_result := OrganicResult{
        Title: result["title"].(string),
        Snippet: result["snippet"].(string),
        Link: result["link"].(string),
    }

    organic_results = append(organic_results, organic_result)
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scraping the individual page
&lt;/h2&gt;

&lt;p&gt;SerpApi focuses on scraping search results. That's why we need extra help to scrape individual sites. We'll use &lt;a href="https://github.com/gocolly/colly" rel="noopener noreferrer"&gt;GoColly package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install package&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;go get -u github.com/gocolly/colly/v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code for scraping individual site&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Add this code inside the loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Scrape each of the link
c := colly.NewCollector()

c.OnHTML("body", func(e *colly.HTMLElement) {
    rawText := e.Text
    fmt.Println("Raw text in entire body tag:", rawText)
})

// Handle any errors
c.OnError(func(r *colly.Response, err error) {
    fmt.Println("Request URL:", r.Request.URL, "failed with response:", r, "\nError:", err)
})

// Start scraping
c.Visit(organic_result.Link)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If you need the whole text of each site, you can return the &lt;code&gt;rawText&lt;/code&gt; from above. Then you're done.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But if you need only the snippet part until the next period (the entire sentence), we will continue to the following function.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping only the relevant text
&lt;/h2&gt;

&lt;p&gt;Here's the pseudocode on returning only the relevant full snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find $partialSnippet in the rawText
Find the position of $partialSnippet
Find the next (closest) period after that partial snippet
Return the whole snippet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the Go Lang code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fullSnippet := findSentence(rawText, snippet)
return fullSnippet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;findSentence&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func findSentence(rawText string, searchText string) string {

    // 1. Replace all whitespaces with a single space
    re := regexp.MustCompile(`\s+`) 
    fullText := re.ReplaceAllString(rawText, " ")

    // 2. Replace all backtik ’ into ' at rawText
    re1 := regexp.MustCompile(`’`)
    fullText = re1.ReplaceAllString(fullText, "'")

    // 3. Find the start index of searchText
    startIndex := strings.Index(fullText, searchText)
    if startIndex == -1 {
        return "Text not found"
    }

    // 4. Calculate the end index of the snippet
    snippetEndIndex := startIndex + len(searchText)

    // 5. Find the end of the sentence after the snippet
    endOfSentenceIndex := strings.Index(fullText[snippetEndIndex:], ".")
    if endOfSentenceIndex == -1 {
        // Return the rest of the text from snippet if not found
        return fullText[startIndex:]
    }

    // Adjust to get the correct index in the full text
    endOfSentenceIndex += snippetEndIndex + 1

    return fullText[startIndex:endOfSentenceIndex]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j5gxk1lhj8i4nz71kgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j5gxk1lhj8i4nz71kgl.png" alt="Result of a full snippet from Google" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can create a conditional logic (if statement) to only perform this when the snippet has "..." (three dots in the end).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Full code sample
&lt;/h2&gt;

&lt;p&gt;Here is the full code sample in GitHub: &lt;a href="https://github.com/hilmanski/serpapi-fullsnippet-golang" rel="noopener noreferrer"&gt;https://github.com/hilmanski/serpapi-fullsnippet-golang&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Warning
&lt;/h2&gt;

&lt;p&gt;Here are a few potential issues and solutions with our method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Different snippet format
&lt;/h3&gt;

&lt;p&gt;This might not work when Google displays a snippet list, where the snippet comes from some headings or key points. We'll need to write a different logic for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding proxy
&lt;/h3&gt;

&lt;p&gt;To prevent getting blocked when scraping the individual site, you can add proxies to the GoColly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a reminder, You don't need to worry about getting block for scraping the Google search itself when using SerpApi.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reference: &lt;a href="https://go-colly.org/docs/examples/proxy_switcher/" rel="noopener noreferrer"&gt;https://go-colly.org/docs/examples/proxy_switcher/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sample proxy switcher&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "bytes"
    "log"

    "github.com/gocolly/colly"
    "github.com/gocolly/colly/proxy"
)

func main() {
    // Instantiate default collector
    c := colly.NewCollector(colly.AllowURLRevisit())

    // Rotate two socks5 proxies
    rp, err := proxy.RoundRobinProxySwitcher("socks5://127.0.0.1:1337", "socks5://127.0.0.1:1338")
    if err != nil {
        log.Fatal(err)
    }
    c.SetProxyFunc(rp)

    // Print the response
    c.OnResponse(func(r *colly.Response) {
        log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
    })

    // Fetch httpbin.org/ip five times
    for i := 0; i &amp;lt; 5; i++ {
        c.Visit("https://httpbin.org/ip")
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope it helps you to collect more data for your Google SERP!&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>go</category>
    </item>
  </channel>
</rss>
