<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hilman Ramadhan</title>
    <description>The latest articles on DEV Community by Hilman Ramadhan (@hilmanski).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1178399%2Fd95919e5-d9e8-44d6-8f30-3eeab2b937ac.png</url>
      <title>DEV Community: Hilman Ramadhan</title>
      <link>https://dev.to/hilmanski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hilmanski"/>
    <language>en</language>
    <item>
      <title>SerpApi vs. Firecrawl: Search Intelligence VS Website Content</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Mon, 27 Jul 2026 11:17:52 +0000</pubDate>
      <link>https://dev.to/serpapi/serpapi-vs-firecrawl-search-intelligence-vs-website-content-47n4</link>
      <guid>https://dev.to/serpapi/serpapi-vs-firecrawl-search-intelligence-vs-website-content-47n4</guid>
      <description>&lt;p&gt;When comparing SerpApi vs Firecrawl, you're not comparing two solutions to the same problem. You're comparing two fundamentally different data extraction philosophies that happen to both involve web scraping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firecrawl&lt;/strong&gt; is a website content scraper. You point it at a URL, &lt;code&gt;techcrunch.com/article/some-news&lt;/code&gt; and it returns clean, LLM-ready markdown of that article. It crawls links, extracts text, converts HTML to structured formats, and handles JavaScript rendering. It's designed for AI applications that need to ingest website content for RAG systems, knowledge bases, or training data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SerpApi&lt;/strong&gt; is a &lt;a href="https://serpapi.com/use-cases/ai-search-engine-api" rel="noopener noreferrer"&gt;search engine scraper&lt;/a&gt;. You query it with "best CRM software" and it returns what Google (and other top search engines) shows for that query: the top 10 organic results, the paid ads, the featured snippet, the "People Also Ask" box, and the local map pack. It doesn't scrape the content of those websites. It scrapes the search results themselves, the metadata, rankings, prices, ratings, and visibility signals that search engines display.&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%2Fwnp4oxio3h9rpnwrx6yf.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%2Fwnp4oxio3h9rpnwrx6yf.png" alt="Firecrawl vs SerpApi" width="799" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Firecrawl vs SerpApi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For generic content extraction like "Get me all blog posts from this competitor's site," Firecrawl works fine and is purpose-built for that task. But for business intelligence questions like "Who's ranking for this keyword?" or "What's the average price for this product across retailers?", Firecrawl is categorically the wrong tool.&lt;/p&gt;

&lt;p&gt;The core issue isn't that Firecrawl is bad at what it does. It's that Firecrawl and SerpApi solve opposite problems. If your business depends on understanding search visibility, competitive positioning, or what consumers actually see when they search, then scraping individual websites doesn't give you that data. You need to scrape the search engines themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Search Intelligence Gap: What Firecrawl Cannot Do
&lt;/h2&gt;

&lt;p&gt;While Firecrawl recently added a &lt;code&gt;/search&lt;/code&gt; endpoint that can perform basic web searches—including basic location customization—it fundamentally differs from true SERP scraping. Search engine results change drastically depending on the user. SerpApi provides an extensive range of parameters (like granular geolocation, device type, and language settings) to fetch fully customized results exactly as your target audience sees them.&lt;/p&gt;

&lt;p&gt;Furthermore, Firecrawl's search returns only simple result metadata like URLs, titles, and descriptions. It cannot extract the structured SERP features that drive business intelligence: paid ad blocks, &lt;a href="https://serpapi.com/organic-results" rel="noopener noreferrer"&gt;featured snippets&lt;/a&gt; as structured objects, &lt;a href="https://serpapi.com/knowledge-graph" rel="noopener noreferrer"&gt;Knowledge Graph panels&lt;/a&gt;, &lt;a href="https://serpapi.com/related-questions" rel="noopener noreferrer"&gt;People Also Ask sections&lt;/a&gt;, local pack results, shopping carousels, or any Google-specific SERP elements.&lt;/p&gt;

&lt;p&gt;As Firecrawl's own documentation clarifies: "SERP APIs specifically scrape and reformat data from existing search engines like Google or Bing. All SERP APIs are Search APIs, but not all Search APIs are SERP APIs."&lt;/p&gt;

&lt;p&gt;This eliminates entire categories of use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://serpapi.com/blog/rank-tracking-in-the-age-of-ai-overviews-whats-changed/" rel="noopener noreferrer"&gt;&lt;strong&gt;SEO rank tracking with SERP features&lt;/strong&gt;&lt;/a&gt;: While Firecrawl provides position numbers in search results, they don't extract structured SERP features (Featured Snippets, People Also Ask, Knowledge Graphs) as separate objects for comprehensive rank tracking&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serpapi.com/use-cases/local-seo" rel="noopener noreferrer"&gt;&lt;strong&gt;Local SEO analysis&lt;/strong&gt;&lt;/a&gt;: You cannot extract Google Maps data, local pack rankings with review counts, or business verification status&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serpapi.com/use-cases/price-monitoring" rel="noopener noreferrer"&gt;&lt;strong&gt;Price comparison&lt;/strong&gt;&lt;/a&gt;: You cannot scrape Google Shopping SERP features or compare prices across retailers as displayed in search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your product roadmap includes any feature that starts with "Show me what appears when I search for," Firecrawl is not the tool. SerpApi is.&lt;/p&gt;

&lt;p&gt;Read moe: &lt;a href="https://serpapi.com/blog/serpapi-alternatives-best-web-search-apis/" rel="noopener noreferrer"&gt;SerpApi vs other Web Search API alternatives&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Structured Data Problem
&lt;/h3&gt;

&lt;p&gt;Even with Firecrawl's basic search capability, you get raw result lists without the rich structured data that powers business applications.&lt;/p&gt;

&lt;p&gt;SerpApi returns JSON with &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;specific parsers for each SERP feature&lt;/a&gt;. When you query "pizza near me", SerpApi's response includes structured fields for rating, review counts, position ranking, hours of operation, and price indicators. Firecrawl returns basic fields like title, URL, and description.&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%2Fonrksmocard3f5zi3jnv.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%2Fonrksmocard3f5zi3jnv.png" alt="Example when querying " width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example when querying "Pizza Near me"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You'd have to build your own parser for these SERP-specific data points, which is exactly what SerpApi has industrialized across &lt;a href="https://serpapi.com/search-engine-apis" rel="noopener noreferrer"&gt;many platforms&lt;/a&gt; including &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, &lt;a href="https://serpapi.com/bing-search-api" rel="noopener noreferrer"&gt;Bing&lt;/a&gt;, &lt;a href="https://serpapi.com/youtube-search-api" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://serpapi.com/amazon-search-api" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;, with 100+ API endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Website Scraping vs. Search Scraping: A Concrete Example
&lt;/h3&gt;

&lt;p&gt;Here's a real-world scenario that clarifies the distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; You're building a competitive intelligence dashboard for e-commerce brands. Your users want to know:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which competitors rank for their target keywords&lt;/li&gt;
&lt;li&gt;What prices competitors show in Google Shopping&lt;/li&gt;
&lt;li&gt;Which products have featured snippets or rich results&lt;/li&gt;
&lt;li&gt;What ad copy competitors use in paid search&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Using Firecrawl:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You could scrape your competitors' product pages to extract their prices and descriptions. But you have no way to know which competitors to scrape because you don't know who ranks for your keywords. You'd need a pre-existing list of competitor URLs. Even with that list, you wouldn't see their Google Shopping prices (which often differ from their website prices), their ad copy, or their SERP rankings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using SerpApi:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You query &lt;a href="https://serpapi.com/google-shopping-api" rel="noopener noreferrer"&gt;SerpApi's Google Shopping API&lt;/a&gt; with your target product keyword. You get back every retailer showing prices for that product in Google Shopping, their exact prices, their ratings, their shipping costs, and their position in the results. You access ad data to see which competitors are bidding on that keyword and what ad copy they're using. All structured data, one API call per query.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The first approach gives you website content. The second gives you market intelligence.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "LLM-Ready" Misconception
&lt;/h2&gt;

&lt;p&gt;Firecrawl markets itself heavily on returning "LLM-ready markdown." That's true, and for content ingestion use cases, it's valuable. If you're building a chatbot that needs to answer questions about your company's documentation, Firecrawl can crawl your docs and return clean markdown.&lt;/p&gt;

&lt;p&gt;But "LLM-ready" doesn't mean "search-intelligence-ready." If your LLM-powered application needs to answer questions like "What are the top 5 CRM tools according to Google?" or "Which hotels in Miami are under $200 tonight?", the LLM needs search results data, not website content.&lt;/p&gt;

&lt;p&gt;SerpApi's JSON responses are just as "LLM-ready" as Firecrawl's markdown, arguably more so because they're pre-structured. When you integrate &lt;a href="https://serpapi.com/blog/connect-assistant-api-to-the-internet-openai-x-google" rel="noopener noreferrer"&gt;SerpApi with OpenAI's Assistant API&lt;/a&gt;, your LLM can query real-time search data and receive structured answers that Firecrawl simply cannot provide because it doesn't access Google Maps or structured search results.&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%2F9h9svqb69vmqgld6uzo0.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%2F9h9svqb69vmqgld6uzo0.png" alt="SerpApi as LLM's web search API" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SerpApi as LLM's web search API&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://serpapi.com/blog/the-web-search-api-for-ai-applications/" rel="noopener noreferrer"&gt;how SerpApi can be integrated with various LLMs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SerpApi vs Firecrawl: Feature-by-Feature Comparison
&lt;/h2&gt;

&lt;p&gt;Here's how &lt;strong&gt;SerpApi vs Firecrawl&lt;/strong&gt; stack up across the dimensions that matter for search intelligence:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SerpApi&lt;/th&gt;
&lt;th&gt;Firecrawl&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Function&lt;/td&gt;
&lt;td&gt;Scrapes search engine results pages (Google, Bing, etc.)&lt;/td&gt;
&lt;td&gt;Scrapes individual website content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Type&lt;/td&gt;
&lt;td&gt;Search rankings, ads, prices, local results, SERP features&lt;/td&gt;
&lt;td&gt;Website text, markdown, HTML content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search Engine Access&lt;/td&gt;
&lt;td&gt;17+ platforms, 100+ API endpoints&lt;/td&gt;
&lt;td&gt;Basic search endpoint (not true SERP scraping)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ranking Data&lt;/td&gt;
&lt;td&gt;Full organic and paid ranking positions&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopping Data&lt;/td&gt;
&lt;td&gt;Google Shopping prices, sellers, reviews, positions&lt;/td&gt;
&lt;td&gt;Must scrape individual retailer sites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Business Data&lt;/td&gt;
&lt;td&gt;Google Maps with reviews, hours, photos, Q&amp;amp;A&lt;/td&gt;
&lt;td&gt;Must scrape business websites individually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SERP Features&lt;/td&gt;
&lt;td&gt;Featured snippets, People Also Ask, Knowledge Graph, 40+ feature types&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM Integration&lt;/td&gt;
&lt;td&gt;Structured JSON for AI consumption&lt;/td&gt;
&lt;td&gt;Markdown optimized for LLMs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response Time&lt;/td&gt;
&lt;td&gt;~0.73-1.75 seconds with Ludicrous Speed&lt;/td&gt;
&lt;td&gt;Varies by page complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing Model&lt;/td&gt;
&lt;td&gt;Per search query (starts at $25/month for 1,000 searches)&lt;/td&gt;
&lt;td&gt;Per page crawled (starts at $16/month for 3,000 credits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Case Focus&lt;/td&gt;
&lt;td&gt;Competitive intelligence, SEO, market research&lt;/td&gt;
&lt;td&gt;Content extraction, documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Understanding the Pricing Models
&lt;/h3&gt;

&lt;p&gt;The pricing structures reflect the fundamental difference in what each tool does. SerpApi charges per search query because each query hits a search engine and extracts structured results. A single query like "best CRM software" can return many data points (10 organic results, ads, related questions, etc.).&lt;/p&gt;

&lt;p&gt;Firecrawl charges per page crawled because it's extracting content from individual URLs. A single-page scrape returns the markdown content of that one page.&lt;/p&gt;

&lt;p&gt;They're not comparable on price because they're not comparable on function. If you need SERP data, you cannot use Firecrawl regardless of its pricing.  &lt;/p&gt;

&lt;p&gt;On top of that, SerpApi gets cheaper as you scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each Tool
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose Firecrawl when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your data lives on specific websites, and you know the URLs&lt;/li&gt;
&lt;li&gt;You need full-text content for LLM consumption&lt;/li&gt;
&lt;li&gt;You're building content aggregation or documentation scrapers&lt;/li&gt;
&lt;li&gt;Your use case is "scrape these websites"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose SerpApi when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your data lives in search engine results, and you need to query by keyword&lt;/li&gt;
&lt;li&gt;You need search visibility metrics, rankings, or SERP features&lt;/li&gt;
&lt;li&gt;You're building SEO tools, price monitoring, or competitive intelligence&lt;/li&gt;
&lt;li&gt;Your use case is "what does Google show when I search for X"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: Choose Based on Your Data Source
&lt;/h2&gt;

&lt;p&gt;The decision between &lt;strong&gt;SerpApi vs Firecrawl&lt;/strong&gt; isn't about which is "better." It's about which data source your application requires.&lt;/p&gt;

&lt;p&gt;The web has two distinct data layers: the content that lives on websites, and the metadata that search engines display about that content. Firecrawl accesses the first. SerpApi accesses the second. For business intelligence, competitive analysis, and understanding market dynamics, the second layer is what matters.&lt;/p&gt;

&lt;p&gt;If you're building SEO tools, price monitoring, competitive intelligence, or local business analysis, you need search engine data. That's where SerpApi operates, and that's why it's the tool of choice for developers who need to understand not just what exists on the web, but what search engines choose to show their users.&lt;/p&gt;

&lt;p&gt;Ready to build with real search intelligence? &lt;a href="https://serpapi.com/users/sign_up" rel="noopener noreferrer"&gt;Start your free trial&lt;/a&gt; and access the data that drives business decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;After understanding the fundamental differences between SerpApi and Firecrawl, here are practical ways to get started with real-world search data:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore Search Data Types&lt;/strong&gt; : Try SerpApi's &lt;a href="https://serpapi.com/playground" rel="noopener noreferrer"&gt;interactive playground&lt;/a&gt; to see structured results from Google Search, Google Shopping, Google Maps, and many other platforms. Compare how different search engines structure their results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Rank Tracking&lt;/strong&gt; : Follow our guide to &lt;a href="https://serpapi.com/blog/serp-tracking-api-create-a-whiltelabel-rank-tracker-app/" rel="noopener noreferrer"&gt;create a SERP tracking API&lt;/a&gt; and monitor keyword positions across locations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract Competitive Intelligence&lt;/strong&gt; : Learn to &lt;a href="https://serpapi.com/blog/scrape-competitors-google-ads-data-using-python" rel="noopener noreferrer"&gt;scrape Google Ads data&lt;/a&gt; to analyze competitor ad copy and bidding strategies. Track &lt;a href="https://serpapi.com/blog/how-to-scrape-google-shopping-results" rel="noopener noreferrer"&gt;Google Shopping results&lt;/a&gt; for real-time price intelligence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrate with AI Applications&lt;/strong&gt; : Connect SerpApi to &lt;a href="https://serpapi.com/blog/connect-assistant-api-to-the-internet-openai-x-google" rel="noopener noreferrer"&gt;OpenAI's Assistant API&lt;/a&gt; for real-time search capabilities, or build an &lt;a href="https://serpapi.com/blog/ai-powered-seo-research-agent-with-openai-serpapi" rel="noopener noreferrer"&gt;AI-powered SEO research agent&lt;/a&gt; that analyzes search trends automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access Local Business Data&lt;/strong&gt; : Master &lt;a href="https://serpapi.com/blog/scrape-google-maps-data-and-reviews-using-python" rel="noopener noreferrer"&gt;Google Maps scraping&lt;/a&gt; for local SEO analysis and competitive research. Build location-based intelligence tools that track reviews, ratings, and local pack rankings.&lt;/p&gt;

&lt;p&gt;Also, check &lt;a href="https://serpapi.com/use-cases" rel="noopener noreferrer"&gt;various SerpApi's use cases&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SerpApi vs. Exa.ai: The Difference Between "Real-Time Truth" and "Neural Search"</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Sun, 26 Jul 2026 11:14:06 +0000</pubDate>
      <link>https://dev.to/serpapi/serpapi-vs-exaai-the-difference-between-real-time-truth-and-neural-search-405b</link>
      <guid>https://dev.to/serpapi/serpapi-vs-exaai-the-difference-between-real-time-truth-and-neural-search-405b</guid>
      <description>&lt;p&gt;If you're building an AI agent or a tool that needs search data, you've probably seen Exa.ai pop up in your research. The marketing positioning is compelling: "AI-native semantic search." But here's the question that matters: does your application need "an" answer or "the" answer?&lt;/p&gt;

&lt;p&gt;Exa describes itself as "the first meaning-based web search API powered by embeddings." What this actually means is that Exa searches its own proprietary index of the web using neural embeddings. It's like having a well-organized library catalog. You get results quickly because you're just querying a database.&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%2F5cjmiv0h14yu3idga0cv.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%2F5cjmiv0h14yu3idga0cv.webp" alt="Exa.ai vs SerpApi" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exa.ai vs SerpApi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://serpapi.com" rel="noopener noreferrer"&gt;SerpApi&lt;/a&gt; scrapes Google, Bing, and other search engines in real-time. It's not searching a catalog. It's reading the live dashboard of what search engines are displaying to users right now - like checking an instrument panel that shows current readings, not historical records. That's why SerpApi delivers real-time search intelligence, not cached snapshots.&lt;/p&gt;

&lt;p&gt;This isn't a subtle difference. If you're building a RAG chatbot that needs to answer generic questions like "explain quantum computing," Exa might work fine. But if you're building anything that requires market intelligence, &lt;a href="https://serpapi.com/blog/serp-tracking-api-create-a-whiltelabel-rank-tracker-app/" rel="noopener noreferrer"&gt;SEO rankings&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/automate-amazon-price-tracking-to-see-real-world-inflation/" rel="noopener noreferrer"&gt;price monitoring&lt;/a&gt;, ad verification, or local search results, you need real-time data that reflects the actual state of search engines. That's what SerpApi delivers.&lt;/p&gt;

&lt;p&gt;This article breaks down the technical and business differences between these two approaches so you can make an informed decision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also, read the_&amp;nbsp;_&lt;a href="https://serpapi.com/blog/serpapi-alternatives-best-web-search-apis/" rel="noopener noreferrer"&gt;best Web Search API options in 2026&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Data Freshness and "The Truth"
&lt;/h2&gt;

&lt;p&gt;The core architectural difference between Exa and SerpApi is what they're actually querying. Exa uses a proprietary index of the web, trained on embeddings for semantic search. This means Exa crawls the web periodically, builds an index, and then serves queries against that index. The problem with this approach is staleness.&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%2Fet48tu2ucpo5e0z0smvi.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%2Fet48tu2ucpo5e0z0smvi.png" alt="Exa landing page" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exa landing page&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's a concrete example. Let's say a user asks your AI agent, "What's the price of the iPhone 17 right now?" If you query Exa, you might get a blog post that mentions the iPhone 17's price, but that post could be from three days ago. The price might have changed. The product might be out of stock. You won't know because Exa is showing you what was true when it last crawled that page.&lt;/p&gt;

&lt;p&gt;If you query &lt;a href="https://serpapi.com/blog/how-to-scrape-google-shopping-results/" rel="noopener noreferrer"&gt;SerpApi's Google Shopping API&lt;/a&gt;, you get the actual Google Shopping results. You're seeing the same data that appears when a user searches Google Shopping in their browser. That's real-time truth.&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%2Fovnl6ih0m36i38lxbz30.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%2Fovnl6ih0m36i38lxbz30.png" alt="Sample iPhone 17 price search on Exa and SerpApi" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sample iPhone 17 price search on Exa and SerpApi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This matters even more for &lt;a href="https://serpapi.com/use-cases/seo" rel="noopener noreferrer"&gt;SEO&lt;/a&gt; and &lt;a href="https://serpapi.com/use-cases/local-seo" rel="noopener noreferrer"&gt;local search&lt;/a&gt;. If you're building a rank tracking tool, your customers want to know where their site ranks today, not where it ranked when Exa last updated its index. SerpApi captures search results exactly as they appear in real-time, including &lt;a href="https://serpapi.com/blog/scrape-google-maps-data-and-reviews-using-python/" rel="noopener noreferrer"&gt;Google Maps&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-google-local-pack-results/" rel="noopener noreferrer"&gt;Local Packs&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-google-knowledge-graph-results/" rel="noopener noreferrer"&gt;Knowledge Graphs&lt;/a&gt;, and &lt;a href="https://serpapi.com/blog/how-to-scrape-google-inline-shopping-results/" rel="noopener noreferrer"&gt;Shopping Carousels&lt;/a&gt;. Exa doesn't support any of these structured result types because it's searching its own index, not Google's live results.&lt;/p&gt;

&lt;p&gt;Let's look at what data types each service actually supports:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exa's Coverage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text content from web pages&lt;/li&gt;
&lt;li&gt;Semantic search across its index&lt;/li&gt;
&lt;li&gt;Basic filtering (date, domain, category)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SerpApi's Coverage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organic search results from &lt;a href="https://serpapi.com/blog/how-to-scrape-google-search-results-with-python/" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-bing-search-results-with-node-js/" rel="noopener noreferrer"&gt;Bing&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-yahoo-search-results/" rel="noopener noreferrer"&gt;Yahoo&lt;/a&gt;, Baidu, &lt;a href="https://serpapi.com/blog/how-to-scrape-yandex-search-results/" rel="noopener noreferrer"&gt;Yandex&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-duckduckgo-results/" rel="noopener noreferrer"&gt;DuckDuckGo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Maps and Local Pack results&lt;/li&gt;
&lt;li&gt;Shopping results (&lt;a href="https://serpapi.com/blog/how-to-scrape-google-shopping-results/" rel="noopener noreferrer"&gt;Google Shopping&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/scrape-amazon-product-data-tutorial/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-ebay-search-results/" rel="noopener noreferrer"&gt;eBay&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/how-to-scrape-walmart-product-results/" rel="noopener noreferrer"&gt;Walmart&lt;/a&gt;, &lt;a href="https://serpapi.com/blog/using-the-home-depot-product-api-from-serpapi/" rel="noopener noreferrer"&gt;Home Depot&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Image and video results&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serpapi.com/blog/how-to-scrape-google-knowledge-graph-results/" rel="noopener noreferrer"&gt;Knowledge Graphs&lt;/a&gt; and &lt;a href="https://serpapi.com/blog/scrape-multiple-google-answer-box-layouts-with-python/" rel="noopener noreferrer"&gt;answer boxes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://serpapi.com/blog/scraping-google-news-using-python-tutorial/" rel="noopener noreferrer"&gt;News results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://serpapi.com/blog/how-to-scrape-google-flights/" rel="noopener noreferrer"&gt;Google Flights&lt;/a&gt; and &lt;a href="https://serpapi.com/blog/how-to-scrape-google-hotels-results/" rel="noopener noreferrer"&gt;Hotels&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://serpapi.com/blog/scraping-google-trends-with-python-pytrends-alternative/" rel="noopener noreferrer"&gt;Google Trends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your use case requires anything beyond basic text search, Exa won't work. You can't get local business data, you can't track shopping prices, you can't monitor ad placements. Those data types don't exist in Exa's index.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Wars: "Neural Speed" vs. Real-Time Scraping
&lt;/h2&gt;

&lt;p&gt;Critics point out that scraping Google takes longer than querying a database. That's technically true. Exa can be faster because you're just doing a database lookup.&lt;/p&gt;

&lt;p&gt;But here's what that criticism misses: SerpApi has closed the performance gap. SerpApi's measured 30-day average response time is 1.09 seconds. For the majority of requests, you're looking at 1-2 second response times.&lt;/p&gt;

&lt;p&gt;For speed-sensitive applications, SerpApi offers Ludicrous Speed mode (using 2x server resources) and Ludicrous Speed Max mode (using 4x server resources) that can deliver sub-second response times while maintaining the same real-time data quality. This means you can get speed comparable to Exa's database lookups without sacrificing data freshness.&lt;/p&gt;

&lt;p&gt;Let's put this in perspective. If you're building a chatbot that responds to user queries, a 1-2 second difference in response time is usually acceptable. Users expect a slight delay when they ask a question that requires a web search. What they don't expect is outdated or incomplete information.&lt;/p&gt;

&lt;p&gt;The real question is: would you rather have a faster response with potentially stale data, or a response with real-time, accurate data? For most enterprise use cases, the answer is obvious.&lt;/p&gt;

&lt;p&gt;SerpApi also offers performance optimization options for speed-sensitive applications. The &lt;a href="https://serpapi.com/blog/get-google-organic-search-results-instantly-with-google-light-fast-api/" rel="noopener noreferrer"&gt;Google Light Fast API&lt;/a&gt; can achieve faster response times depending on your specific needs and the complexity of the search query.&lt;/p&gt;

&lt;p&gt;Here's the performance breakdown:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Average response time: ~1.09s (30-day measured average)&lt;/li&gt;
&lt;li&gt;Ludicrous Speed modes available for sub-second performance&lt;/li&gt;
&lt;li&gt;Success rate: 99.95% SLA&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Fast mode: 178ms P50, 252 P90&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other performance metric that matters is reliability. SerpApi maintains a 99.95% success rate across all requests. When you make a request, you get a result. That reliability matters when you're building production systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature-by-Feature Comparison
&lt;/h2&gt;

&lt;p&gt;Here's a direct comparison table to help you evaluate the two services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SerpApi&lt;/th&gt;
&lt;th&gt;Exa&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Source&lt;/td&gt;
&lt;td&gt;Real-time scraping of 50+ search engines and platforms&lt;/td&gt;
&lt;td&gt;Proprietary internal index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search Engine Coverage&lt;/td&gt;
&lt;td&gt;✅ Google, Bing, Yahoo, Amazon, eBay, Walmart, Yandex, Baidu, DuckDuckGo, Naver, and 40+ more&lt;/td&gt;
&lt;td&gt;❌ Single proprietary index only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freshness&lt;/td&gt;
&lt;td&gt;Real-time (live mirror of search engines)&lt;/td&gt;
&lt;td&gt;Updated every minute (news); incremental for other content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supported Data Types&lt;/td&gt;
&lt;td&gt;Organic results, Maps, Local Pack, Shopping, Images, Videos, Flights, Hotels, Knowledge Graphs, News, Ads&lt;/td&gt;
&lt;td&gt;Text content from web pages, semantic search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured SERP Features&lt;/td&gt;
&lt;td&gt;✅ Knowledge Graphs, Local Pack, Shopping Carousels, Answer Boxes, Featured Snippets, People Also Ask, Related Searches&lt;/td&gt;
&lt;td&gt;❌ Limited to text content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce Platform Support&lt;/td&gt;
&lt;td&gt;✅ Amazon, eBay, Walmart, Home Depot APIs&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ad Data&lt;/td&gt;
&lt;td&gt;✅ Paid search ads included in all results&lt;/td&gt;
&lt;td&gt;❌ Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Business Data&lt;/td&gt;
&lt;td&gt;✅ Google Maps, reviews, photos, hours, contact info&lt;/td&gt;
&lt;td&gt;❌ Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;99.95% SLA (actual uptime: 99.967%)&lt;/td&gt;
&lt;td&gt;Not publicly disclosed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Location Targeting&lt;/td&gt;
&lt;td&gt;✅ Precise geo-targeting with 1000+ locations worldwide&lt;/td&gt;
&lt;td&gt;✅ Worldwide coverage (less granular)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device-Specific Results&lt;/td&gt;
&lt;td&gt;✅ Mobile, tablet, desktop variations&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAPTCHA Handling&lt;/td&gt;
&lt;td&gt;✅ Automatic CAPTCHA solving included&lt;/td&gt;
&lt;td&gt;N/A (no scraping)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser Rendering&lt;/td&gt;
&lt;td&gt;✅ Full browser simulation for JavaScript-heavy pages&lt;/td&gt;
&lt;td&gt;N/A (index-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical Data&lt;/td&gt;
&lt;td&gt;✅ Search Archive API available&lt;/td&gt;
&lt;td&gt;❌ Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;✅ Built-in (free repeated queries)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Architecture&lt;/td&gt;
&lt;td&gt;✅ JSON output, REST API&lt;/td&gt;
&lt;td&gt;✅ JSON output, REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client Libraries/SDKs&lt;/td&gt;
&lt;td&gt;✅ 10+ languages (Python, Node.js, Ruby, PHP, Java, Go, Rust, .NET, Swift, C++)&lt;/td&gt;
&lt;td&gt;✅ Python, JavaScript, curl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No-Code Integration&lt;/td&gt;
&lt;td&gt;✅ Google Sheets add-on, Zapier, Make.com, n8n, Bubble&lt;/td&gt;
&lt;td&gt;❌ API-only (requires coding)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive Playground&lt;/td&gt;
&lt;td&gt;✅ Test queries in browser before coding&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;✅ Comprehensive docs and SDKs&lt;/td&gt;
&lt;td&gt;✅ Comprehensive docs and SDKs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Options&lt;/td&gt;
&lt;td&gt;✅ Custom SLAs available (99.97% for enterprise)&lt;/td&gt;
&lt;td&gt;✅ Custom SLAs available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global Access&lt;/td&gt;
&lt;td&gt;✅ Multi-region proxy networks&lt;/td&gt;
&lt;td&gt;✅ Worldwide index coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legal Protection&lt;/td&gt;
&lt;td&gt;✅ US Legal Shield with up to $2M indemnification (Production+ plans)&lt;/td&gt;
&lt;td&gt;❌ None (customers must indemnify Exa)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing (1k searches)&lt;/td&gt;
&lt;td&gt;Starts at $75/month for 5,000 searches. Getting cheaper as you you scale&lt;/td&gt;
&lt;td&gt;$5 (Basic Search 1-25 results), $15 (Deep Search)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Cases&lt;/td&gt;
&lt;td&gt;Data for AI Agent, SEO tracking, price monitoring, ad verification, local search, market intelligence, competitive analysis&lt;/td&gt;
&lt;td&gt;Generic RAG, content discovery, semantic search, concept exploration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pricing comparison requires context. Exa is cheaper on a per-search basis for basic semantic queries ($5 per 1,000 searches vs. SerpApi's $10-25 per 1,000 at standard tiers). However, SerpApi gets cheaper as you scale. When you factor in the value of real-time data, comprehensive coverage of SERP features, and l&lt;a href="https://serpapi.com/us-legal-shield" rel="noopener noreferrer"&gt;egal protection&lt;/a&gt;, SerpApi's pricing makes sense for production use cases that require search engine accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which API Is Right for You?
&lt;/h2&gt;

&lt;p&gt;Choose Exa if you're building a system that benefits from semantic understanding and concept exploration, where speed is critical and data freshness within minutes or hours is acceptable. Exa works well for RAG applications and content discovery tools.&lt;/p&gt;

&lt;p&gt;Choose SerpApi if you're building a business that needs data reflecting the actual state of search engines right now. If you need web search data for AI tools, SEO rankings, product prices, ad placements, local results, or any structured SERP features beyond basic text, SerpApi is the only option. If you're an enterprise that needs legal protection for data collection activities, SerpApi provides the US Legal Shield feature.&lt;/p&gt;

&lt;p&gt;The fundamental question is this: are you building a system that needs to understand what the web says about a topic (Exa's strength), or are you building a system that needs to understand what search engines are showing users right now (SerpApi's strength)? For most production applications involving market intelligence or user-facing search data, you need the latter.&lt;/p&gt;

&lt;p&gt;Don't settle for a "version" of the web when you need real-time accuracy. SerpApi has provided developers, researchers, and businesses with access to real-time search data for over eight years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Once you've chosen your search API approach, here are the logical next steps based on your use case:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For SEO and Rank Tracking Projects:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start building your rank tracking system using &lt;a href="https://serpapi.com/blog/serp-tracking-api-create-a-whiltelabel-rank-tracker-app/" rel="noopener noreferrer"&gt;SerpApi's SERP Tracking API&lt;/a&gt;. This guide walks through creating a white-label rank tracker application that monitors search rankings in real-time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For E-commerce and Price Monitoring:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implement automated price tracking with the &lt;a href="https://serpapi.com/blog/automate-amazon-price-tracking-to-see-real-world-inflation/" rel="noopener noreferrer"&gt;Amazon Price Tracking tutorial&lt;/a&gt;. Learn how to monitor product prices over time and detect market trends across multiple platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Multi-Platform Search Applications:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're migrating from another search API or expanding your data sources, review the &lt;a href="https://serpapi.com/blog/bing-search-api-replacement-web-search/" rel="noopener noreferrer"&gt;Bing Search API Replacement guide&lt;/a&gt; to understand how SerpApi provides comprehensive coverage across search engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Local Business Intelligence:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build location-based applications using the &lt;a href="https://serpapi.com/blog/scrape-google-maps-data-and-reviews-using-python/" rel="noopener noreferrer"&gt;Google Maps Data Scraping tutorial&lt;/a&gt;. This covers extracting business listings, reviews, and local pack data for market analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For API Optimization and Cost Management:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understand how to optimize your search credit usage with the &lt;a href="https://serpapi.com/blog/calculating-search-credit-usage-with-serpapi/" rel="noopener noreferrer"&gt;Credit Usage Calculation guide&lt;/a&gt;. Learn which parameters affect credit consumption and how to structure queries efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Technical Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Master the full range of search customization options in the &lt;a href="https://serpapi.com/blog/google-search-parameters/" rel="noopener noreferrer"&gt;Google Search Parameters documentation&lt;/a&gt;. This comprehensive reference covers all available parameters for fine-tuning your search queries.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SerpApi vs. Brave Search API: Why "Independent" Data Isn't Enough for Business Intelligence</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Sat, 25 Jul 2026 11:09:10 +0000</pubDate>
      <link>https://dev.to/serpapi/serpapi-vs-brave-search-api-why-independent-data-isnt-enough-for-business-intelligence-1k6n</link>
      <guid>https://dev.to/serpapi/serpapi-vs-brave-search-api-why-independent-data-isnt-enough-for-business-intelligence-1k6n</guid>
      <description>&lt;p&gt;When comparing SerpApi vs Brave Search API, the fundamental question isn't about data quality in isolation; it's about data relevance. The Brave Search API queries a private, independent index of the web containing approximately 20 billion pages. Compare that to Google's index, which spans &lt;a href="https://www.google.com/search/howsearchworks/how-search-works/organizing-information/" rel="noopener noreferrer"&gt;hundreds of billions of pages&lt;/a&gt;. Brave bills itself as privacy-first and independent from Big Tech. It's what you might call a "Sanitized Web", curated, lean, and intentionally isolated from the mainstream search ecosystem.&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%2F86x4pox9498qrs05zc0x.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%2F86x4pox9498qrs05zc0x.png" alt="Brave Search API vs SerpApi" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Brave Search API vs SerpApi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SerpApi takes a different approach. It &lt;a href="https://serpapi.com/search-api" rel="noopener noreferrer"&gt;scrapes the live results&lt;/a&gt; from Google, Bing, YouTube, and yes, even Brave if you need it. It's the "Real Web", the messy, commercial, algorithmically-ranked reality that your customers actually see when they search.&lt;/p&gt;

&lt;p&gt;For generic retrieval-augmented generation (RAG) tasks like "What is the capital of France?" or "Explain photosynthesis," Brave works fine. The answer doesn't change based on which index you query. But for Business Intelligence questions like "Who is bidding on 'CRM software' in London?" or "What's the average price for wireless earbuds on Google Shopping right now?" Brave becomes statistically irrelevant.&lt;/p&gt;

&lt;p&gt;The core issue isn't that Brave is bad. It's that Brave answers a different question. If your business depends on understanding what consumers actually see, where they click, what prices they compare, which local businesses appear in the map pack, then you're not just choosing between two APIs. You're choosing between building on top of the market or building in parallel to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 1% vs. 90% Problem: Understanding Brave Search API Limitations
&lt;/h2&gt;

&lt;p&gt;When evaluating Google Search API vs Brave, the market share statistics tell a sobering story. &lt;a href="https://gs.statcounter.com/search-engine-market-share" rel="noopener noreferrer"&gt;Google controls approximately 90% of the global search market&lt;/a&gt;. &lt;a href="https://gs.statcounter.com/search-engine-market-share" rel="noopener noreferrer"&gt;Bing sits at around 4%&lt;/a&gt;. Brave Search, despite its privacy credentials and growing user base, represents less than 1% of search traffic.&lt;/p&gt;

&lt;p&gt;This creates what I call the "Vanity Metric" danger, one of the most critical &lt;strong&gt;Brave Search API limitations&lt;/strong&gt; for commercial applications. Imagine you're building &lt;a href="https://serpapi.com/blog/serp-tracking-api-create-a-whiltelabel-rank-tracker-app/" rel="noopener noreferrer"&gt;SEO rank tracking APIs&lt;/a&gt; for marketing agencies. Your clients want to know where their e-commerce site ranks for "running shoes" in New York. If you build this tool using the Brave Search API, you're reporting on a user base that effectively doesn't exist for most businesses.&lt;/p&gt;

&lt;p&gt;Your dashboard shows their client is ranking #3 on Brave. That sounds great until you realize that Brave's &amp;lt;1% market share means the client is visible to a tiny fraction of potential customers. Meanwhile, on Google, where 90% of searches happen and where actual revenue gets generated, that same site might be on page 10. Your tool just gave them false confidence based on irrelevant 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%2Fciacy87zcvkkrsi13eby.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%2Fciacy87zcvkkrsi13eby.png" alt="Example of different search results between Google Search and Brave Search" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example of different search results between Google Search and Brave Search&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The problem compounds when you consider the algorithm gap. Brave's ranking algorithm is completely independent from Google's. It doesn't use PageRank, doesn't weight E-E-A-T signals the same way, and doesn't apply the same spam filters or &lt;a href="https://developers.google.com/search/blog/2022/08/helpful-content-update" rel="noopener noreferrer"&gt;helpful content system updates&lt;/a&gt; that Google deploys. The two search engines are fundamentally solving different optimization problems.&lt;/p&gt;

&lt;p&gt;This means optimizing for Brave does not help you rank on Google. If you're an SEO tool provider giving recommendations based on Brave data, you're telling your users how to rank on a platform their customers don't use. There's no feedback loop between Brave performance and business outcomes because the traffic isn't there.&lt;/p&gt;

&lt;p&gt;Only SerpApi gives you access to the ranking signals, ad positions, and &lt;a href="https://serpapi.com/blog/what-is-a-serp" rel="noopener noreferrer"&gt;SERP features&lt;/a&gt; from the platforms where revenue actually happens. When your customer asks "Why did our traffic drop 40% last week?" you need to see what Google changed in their SERP, not what Brave might hypothetically show to its tiny user base.&lt;/p&gt;

&lt;p&gt;The use case for Brave data in SEO tooling is essentially academic. It's interesting for research on algorithmic diversity or privacy-preserving search. But if you're building a business that needs to &lt;a href="https://serpapi.com/blog/rank-tracking-in-the-age-of-ai-overviews-whats-changed/" rel="noopener noreferrer"&gt;predict, track, or optimize for search visibility that translates to revenue, you need Google data&lt;/a&gt;;. That's not an opinion, it's arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Rich Result" Gap: Where Brave Search API Limitations Become Critical
&lt;/h2&gt;

&lt;p&gt;Brave is primarily a text-link search engine. You query it, you get a list of URLs with titles and descriptions. For many use cases, that's sufficient. But the modern Google SERP is nothing like a list of ten blue links.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/appearance/structured-data/search-gallery" rel="noopener noreferrer"&gt;Google's "rich results"&lt;/a&gt; now dominate commercial queries. These include local packs, shopping carousels, knowledge panels, video results, featured snippets, and "People Also Ask" boxes. These elements aren't decorative; they're where clicks happen. Featured snippets can capture significant traffic on informational queries, often pulling clicks away from traditional organic results.&lt;/p&gt;

&lt;p&gt;Brave has none of this. Here's what SerpApi captures that Brave misses:&lt;/p&gt;

&lt;h3&gt;
  
  
  Local SEO Data API Capabilities
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Local Data:&lt;/strong&gt; Brave has no equivalent to Google's Local Pack. When you search "coffee shop near me" on Google, you get a map, business hours, real-time busy times, photos, and review scores. SerpApi connects to &lt;a href="https://serpapi.com/google-maps-api" rel="noopener noreferrer"&gt;Google Maps data&lt;/a&gt;, pulling these structured signals in real-time. If you're building a &lt;strong&gt;local SEO data API&lt;/strong&gt; solution, a competitive analysis tool for restaurants, or a location intelligence platform, Brave literally cannot provide the data you need. It doesn't have relationships with local business databases or review platforms at the scale Google has built through &lt;a href="https://www.google.com/business/" rel="noopener noreferrer"&gt;Google Business Profile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shopping Data:&lt;/strong&gt; Brave has no Shopping Graph. When you search "Sony WH-1000XM5" on Google, you see prices from a dozen retailers, shipping estimates, availability, and product ratings aggregated across the web. SerpApi &lt;a href="https://serpapi.com/blog/how-to-scrape-google-shopping-results/" rel="noopener noreferrer"&gt;scrapes Google Shopping&lt;/a&gt; to give you real-time price comparisons, seller reputations, and stock levels. If you're building a &lt;a href="https://serpapi.com/use-cases/price-monitoring" rel="noopener noreferrer"&gt;price monitoring tool&lt;/a&gt;, a dropshipping product research platform, or an AI shopping assistant, Brave gives you a link to Sony's website. SerpApi provides you with the competitive market landscape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual Search:&lt;/strong&gt; Brave's image search is generic web crawl data. SerpApi scrapes &lt;a href="https://serpapi.com/google-lens-api" rel="noopener noreferrer"&gt;Google Lens&lt;/a&gt; and &lt;a href="https://serpapi.com/blog/uploading-images-and-searching-with-google-lens-via-serpapi" rel="noopener noreferrer"&gt;reverse image search&lt;/a&gt;, which use sophisticated computer vision models to identify products, landmarks, plants, and more. The difference is the layer of machine learning that Google applies to visual data, something Brave's index simply doesn't have at a comparable scale or accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Use Case Comparison
&lt;/h3&gt;

&lt;p&gt;Here's a concrete example. You're building an AI travel agent. A user asks, "Find me a hotel in Barcelona near Park Güell under $150 per night."&lt;/p&gt;

&lt;p&gt;Using Brave Search API, your agent searches "hotels near Park Güell Barcelona" and gets back a list of hotel websites and booking aggregators. It might scrape a few of those sites to find prices, assuming they're not blocked. You get generic results with no structured data.&lt;/p&gt;

&lt;p&gt;Using SerpApi's &lt;a href="https://serpapi.com/google-hotels-api" rel="noopener noreferrer"&gt;Google Hotels integration&lt;/a&gt;, your agent gets back structured JSON with exact prices per night, availability for specific dates, review scores, photos, amenities, distance to Park Güell calculated by Google Maps, and direct booking links. You can filter by price programmatically before even showing results to the user.&lt;/p&gt;

&lt;p&gt;The first implementation requires you to build your own scraping infrastructure for dozens of hotel sites. The second gives you production-ready data in one API call. This isn't a minor difference; it's the difference between a prototype and a product customers will pay for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance &amp;amp; "Ludicrous Speed"
&lt;/h2&gt;

&lt;p&gt;Let's acknowledge the elephant in the room: Brave is fast. Query response times are typically under 500 milliseconds because you're hitting a purpose-built API on Brave's infrastructure. There's no scraping, no proxy rotation, no parsing complexity.&lt;/p&gt;

&lt;p&gt;SerpApi is scraping live web pages, which adds latency. That's a trade-off.&lt;/p&gt;

&lt;p&gt;But here's the counter: SerpApi's &lt;a href="https://serpapi.com/ludicrous-speed" rel="noopener noreferrer"&gt;Ludicrous Speed Max&lt;/a&gt; brings Google scraping down to approximately 1-2 seconds. For most business intelligence use cases, that's fast enough. If you're running hourly rank checks, daily price monitoring, or enriching a CRM with company data pulled from search, 1-2 seconds is completely acceptable.&lt;/p&gt;

&lt;p&gt;Ask yourself this: Is saving 800 milliseconds worth missing 90% of the market? Is it worth losing access to local data, shopping data, and rich results that drive actual business decisions?&lt;/p&gt;

&lt;p&gt;For real-time, user-facing search where latency is critical, consider using Brave as a fallback or for generic queries. But for the business intelligence layer, the data that informs product decisions, marketing spend, and competitive strategy, the marginal latency of SerpApi is worth the accuracy and completeness.&lt;/p&gt;

&lt;p&gt;Speed is a feature. Relevance is a requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  SerpApi vs Brave Search API: Feature-by-Feature Comparison
&lt;/h2&gt;

&lt;p&gt;Here's how &lt;strong&gt;SerpApi vs Brave Search API&lt;/strong&gt; stack up across the dimensions that matter for business intelligence:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SerpApi&lt;/th&gt;
&lt;th&gt;Brave Search API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Data Source&lt;/td&gt;
&lt;td&gt;Live scraping of Google, Bing, Yahoo, DuckDuckGo, Yandex, Baidu, YouTube, and 50+ sources&lt;/td&gt;
&lt;td&gt;Brave's independent index (~20B pages)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Market Coverage&lt;/td&gt;
&lt;td&gt;~90% (Google's market share)&lt;/td&gt;
&lt;td&gt;&amp;lt;1% (Brave's market share)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopping Data&lt;/td&gt;
&lt;td&gt;Full Google Shopping integration with prices, sellers, shipping, reviews&lt;/td&gt;
&lt;td&gt;No shopping-specific data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Business Data&lt;/td&gt;
&lt;td&gt;Google Maps API with reviews, photos, hours, busy times, Q&amp;amp;A&lt;/td&gt;
&lt;td&gt;No local pack or structured business data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rich Results&lt;/td&gt;
&lt;td&gt;Featured snippets, People Also Ask, Knowledge Graph, video carousels&lt;/td&gt;
&lt;td&gt;Basic organic results only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legal Protection&lt;/td&gt;
&lt;td&gt;$2M indemnification via US Legal Shield&lt;/td&gt;
&lt;td&gt;Standard API terms (no scraping protection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average Response Time&lt;/td&gt;
&lt;td&gt;Depend on the API and the speed mode&lt;/td&gt;
&lt;td&gt;&amp;lt;500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;Starts at $75/month for 5,000 searches. Getting cheaper as you you scale&lt;/td&gt;
&lt;td&gt;$5 per 1,000 queries (~$0.005 per query)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supported Search Engines&lt;/td&gt;
&lt;td&gt;50+ including Google, Bing, Yahoo, DuckDuckGo, Brave, Yandex, Baidu&lt;/td&gt;
&lt;td&gt;Brave only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Search&lt;/td&gt;
&lt;td&gt;Google Lens, reverse image search with ML identification&lt;/td&gt;
&lt;td&gt;Generic image index&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Understanding SerpApi Pricing
&lt;/h3&gt;

&lt;p&gt;The pricing difference is real. Brave is more affordable per query at face value. But when evaluating SerpApi pricing, consider the value proposition. If those Brave queries return data that doesn't reflect your customers' reality, you're not saving money; you're buying irrelevant data at a discount. SerpApi's &lt;a href="https://serpapi.com/pricing" rel="noopener noreferrer"&gt;pricing structure&lt;/a&gt; reflects the complexity of delivering real-time, structured data from the search engines where actual business happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Do You Want "Clean" Data or "Right" Data?
&lt;/h2&gt;

&lt;p&gt;Brave is a great backup search engine. It's a solid training dataset for generic knowledge retrieval. If you're building a RAG system for a chatbot that answers encyclopedia-style questions, Brave will work fine.&lt;/p&gt;

&lt;p&gt;But Brave is an island. Its index is independent, its algorithm is independent, and its user base is a rounding error in the global search market. If your business depends on understanding what consumers see when they search, which is Google 90% of the time, you cannot afford to look at a different index.&lt;/p&gt;

&lt;p&gt;The data might be clean, but it's not right. It's not representative. It doesn't show you where your customers' ads appear, where their competitors rank, what prices are displayed, or which local businesses get the map pack visibility.&lt;/p&gt;

&lt;p&gt;When choosing between &lt;strong&gt;Google Search API vs Brave&lt;/strong&gt; , or more accurately, between SerpApi (which provides Google data) and Brave's independent index, the choice becomes clear. SerpApi is the universal adapter for the actual digital economy. It connects to Google, where the revenue happens, Bing, where enterprise users search, YouTube, where product reviews live, Amazon, where e-commerce pricing is set, and yes, even Brave if you need it for comparison. You're not locked into one provider's view of the web; you get the web as it actually exists across platforms.&lt;/p&gt;

&lt;p&gt;If you're building &lt;strong&gt;SEO rank tracking APIs&lt;/strong&gt; , &lt;strong&gt;price monitoring&lt;/strong&gt; , &lt;strong&gt;local SEO data API&lt;/strong&gt; solutions, ad intelligence, or competitive analysis tools, your users don't care about Brave's independent index. They care about Google's SERP because that's where their traffic comes from. Give them data from the source that matters.&lt;/p&gt;

&lt;p&gt;See the market as it really is. Get 100% accurate Google data with SerpApi. &lt;a href="https://serpapi.com/users/sign_up" rel="noopener noreferrer"&gt;Start your free trial&lt;/a&gt; and query the real web, not a sanitized approximation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also read &lt;a href="https://serpapi.com/blog/serpapi-alternatives-best-web-search-apis/" rel="noopener noreferrer"&gt;best Web Search API options in 2026&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;After understanding the fundamental differences between SerpApi and Brave Search API, here are practical ways to get started with real-world search data:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start Building with SerpApi&lt;/strong&gt; : Try SerpApi's &lt;a href="https://serpapi.com/playground" rel="noopener noreferrer"&gt;interactive playground&lt;/a&gt; to test queries and see structured results immediately. Explore our &lt;a href="https://serpapi.com/integrations" rel="noopener noreferrer"&gt;integration libraries&lt;/a&gt; for Python, Node.js, PHP, Ruby, Java, and other languages to get production-ready code snippets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track Your Rankings&lt;/strong&gt; : Build a &lt;a href="https://serpapi.com/blog/serp-tracking-api-create-a-whiltelabel-rank-tracker-app/" rel="noopener noreferrer"&gt;rank tracking application&lt;/a&gt; to monitor keyword positions across locations and devices. Learn how to &lt;a href="https://serpapi.com/blog/calculating-search-credit-usage-with-serpapi" rel="noopener noreferrer"&gt;calculate search credit usage&lt;/a&gt; to optimize your monitoring strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract Competitive Intelligence&lt;/strong&gt; : Discover how to &lt;a href="https://serpapi.com/blog/scrape-competitors-google-ads-data-using-python" rel="noopener noreferrer"&gt;scrape Google Ads data&lt;/a&gt; from competitors and analyze &lt;a href="https://serpapi.com/blog/how-to-scrape-google-shopping-results" rel="noopener noreferrer"&gt;shopping results&lt;/a&gt; to understand pricing strategies in your market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Location-Based Tools&lt;/strong&gt; : Learn to &lt;a href="https://serpapi.com/blog/scrape-google-maps-data-and-reviews-using-python" rel="noopener noreferrer"&gt;scrape Google Maps data and reviews&lt;/a&gt; for local business intelligence, or create a &lt;a href="https://serpapi.com/blog/making-a-hotel-price-tracker-with-google-hotels-and-n8n" rel="noopener noreferrer"&gt;hotel price tracker&lt;/a&gt; with our Google Hotels API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrate with AI Workflows&lt;/strong&gt; : Connect SerpApi to &lt;a href="https://serpapi.com/blog/connect-assistant-api-to-the-internet-openai-x-google" rel="noopener noreferrer"&gt;OpenAI's Assistant API&lt;/a&gt; for real-time web knowledge, or build an &lt;a href="https://serpapi.com/blog/ai-powered-seo-research-agent-with-openai-serpapi" rel="noopener noreferrer"&gt;AI-powered SEO research agent&lt;/a&gt; that analyzes search trends automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://serpapi.com/use-cases/web-search-api" rel="noopener noreferrer"&gt;SerpApi is the Web Search API&lt;/a&gt; you need for your applications and AI agents.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SerpApi Alternatives: Best Web Search APIs in 2026</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Fri, 24 Jul 2026 09:39:07 +0000</pubDate>
      <link>https://dev.to/serpapi/serpapi-alternatives-best-web-search-apis-in-2026-2hnn</link>
      <guid>https://dev.to/serpapi/serpapi-alternatives-best-web-search-apis-in-2026-2hnn</guid>
      <description>&lt;p&gt;If you’re building products that rely on web search — AI apps, research tools, lead collectors, price trackers, or internal dashboards — you’ve probably heard of SerpApi. It’s a popular choice because it turns messy search engine pages into clean, structured data with zero scraping pain.&lt;/p&gt;

&lt;p&gt;That said, SerpApi isn’t the only option. You may have heard some of the Search API alternatives. In this article, we’ll walk through the most popular SerpApi alternatives &lt;a href="https://serpapi.com/blog/the-web-search-api-for-ai-applications/" rel="noopener noreferrer"&gt;as a Web Search API&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Brave Search API&lt;/li&gt;
&lt;li&gt;Exa&lt;/li&gt;
&lt;li&gt;Tavily&lt;/li&gt;
&lt;li&gt;Firecrawl&lt;/li&gt;
&lt;li&gt;Serperdev and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll break down what each tool is good at, who it’s best for, and when you might choose it over SerpApi.&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%2Fx7yacz84jkt4b65rfp31.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%2Fx7yacz84jkt4b65rfp31.png" alt="SerpApi alternatives as a Web Search API" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SerpApi alternatives as a Web Search API&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do many companies choose SerpApi?
&lt;/h2&gt;

&lt;p&gt;Before jumping into alternatives, here’s why developers typically use SerpApi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete &lt;a href="https://serpapi.com/search-engine-apis" rel="noopener noreferrer"&gt;Search Engine APIs&lt;/a&gt;: We cover more than 100 APIs that give you programmatic access to Google, Bing, DuckDuckGo, etc.&lt;/li&gt;
&lt;li&gt;Real-time results: We always perform live scraping for you; it's not coming from a database. &lt;/li&gt;
&lt;li&gt;Structured JSON responses (no HTML parsing). No more DIY that forces you to spend time and resources on keeping up to date with the latest layout. Use a simple API, and you are done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise-ready:&lt;/strong&gt; We're &lt;a href="https://serpapi.com/security" rel="noopener noreferrer"&gt;SOC2 Type II certified&lt;/a&gt; and can handle a high volume of searches for your team.&lt;/li&gt;
&lt;li&gt;We've been in business since 2017. So, we faced many issues that our competitor hasn't. We're focusing on scraping search engine results, not just any websites. That keeps us focused on our vision and our daily work.&lt;/li&gt;
&lt;li&gt;Location, language, device, and other parameters to narrow your search to what you need.&lt;/li&gt;
&lt;li&gt;Use cases: AI agents, SEO tools, market research, price monitoring, and lead collection. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If all of that sounds good to you. You can register for free at &lt;a href="https://serpapi.com/" rel="noopener noreferrer"&gt;serpapi.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Now, let's see some options for web search API:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. SerpApi
&lt;/h3&gt;

&lt;p&gt;SerpApi provides public search results data from various search engines in a structured JSON format. We currently cover more than 100+ APIs.&lt;/p&gt;

&lt;p&gt;We don't manipulate the data in any way; we leave it to the developers to decide what to do with it, including providing responses to AI tools for various &lt;a href="https://serpapi.com/use-cases" rel="noopener noreferrer"&gt;use cases&lt;/a&gt;. &lt;strong&gt;No hidden algorithm.&lt;/strong&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%2Ffnrq38hh2q1b332ev97t.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%2Ffnrq38hh2q1b332ev97t.png" alt="SerpApi - web search API" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SerpApi - web search API&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unedited real-time results&lt;/li&gt;
&lt;li&gt;Support 100+ APIs, you can pick depending on your application needs&lt;/li&gt;
&lt;li&gt;Simple API through a single HTTP GET request&lt;/li&gt;
&lt;li&gt;Flexible use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depending on the volume, SerpApi is not the cheapest option, as it prioritizes result quality. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Brave Search API
&lt;/h3&gt;

&lt;p&gt;Brave Search API gives you access to Brave’s own search index, not Google or Bing results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Brave’s independent search index&lt;/li&gt;
&lt;li&gt;Want an alternative to Google/Bing dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited to only Brave's own algorithm and database&lt;/li&gt;
&lt;li&gt;Not a drop-in replacement for Google SERP and other engines scraping&lt;/li&gt;
&lt;li&gt;Not ideal for product or business research&lt;/li&gt;
&lt;li&gt;Less SEO-specific data (ads, rich snippets, local packs, etc.)&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%2Fknfo4wkb3fej2vgdcv7w.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%2Fknfo4wkb3fej2vgdcv7w.png" alt="Brave Search API alternatives" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Brave Search API alternatives&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to choose Brave over SerpApi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 When your apps need generic knowledge from search results and want to be independent from Google, Bing, or other search engines.&lt;/p&gt;

&lt;p&gt;Read more &lt;a href="https://serpapi.com/blog/serpapi-vs-brave-search-api/" rel="noopener noreferrer"&gt;Brave Search API vs SerpApi&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Exa
&lt;/h3&gt;

&lt;p&gt;Instead of returning raw SERP-like results, Exa focuses on semantic search or "natural language search query".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Exa's own search index. It can be faster as the source is coming from a database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not designed to perform live scraping on search engines&lt;/li&gt;
&lt;li&gt;Limited to their own algorithm and database&lt;/li&gt;
&lt;li&gt;Not ideal for product or business research&lt;/li&gt;
&lt;li&gt;Less useful for SEO monitoring, rank tracking, or ad data&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%2Fs1y0dk7xkyoi6dn3ob0o.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%2Fs1y0dk7xkyoi6dn3ob0o.png" alt="Exa AI Search API Alternatives" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exa AI Search API Alternatives&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to choose Exa over SerpApi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 When you’re building apps that don't really need real-time or raw data from a search engine.&lt;/p&gt;

&lt;p&gt;Read more &lt;a href="https://serpapi.com/blog/serpapi-vs-exa-ai/" rel="noopener noreferrer"&gt;Exa.ai vs SerpApi&lt;/a&gt;👉&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Tavily
&lt;/h3&gt;

&lt;p&gt;Tavily takes a different approach. It aggregates content from multiple web sources and processes it on its side before returning the response to the customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregated search results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Returned results are based on their algorithm&lt;/li&gt;
&lt;li&gt;Summarized search responses might not be what people want in many cases&lt;/li&gt;
&lt;li&gt;Not ideal for product research or SEO tooling that needs real-time data.&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%2Foi6tza1pcx9js06r1gm1.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%2Foi6tza1pcx9js06r1gm1.png" alt="Tavily alternatives" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tavily alternatives&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to choose Tavily over SerpApi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 When your app doesn't need the exact live data results from search engines, and you're fine using Tavily's algorithm for the returned results.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Firecrawl
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Firecrawl&lt;/strong&gt; &amp;nbsp;is a website content scraper. You point it at a URL,&amp;nbsp;&lt;code&gt;techcrunch.com/article/some-news&lt;/code&gt;&amp;nbsp;and it returns clean, LLM-ready markdown of that article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SerpApi&lt;/strong&gt; &amp;nbsp;is a&amp;nbsp;&lt;a href="https://serpapi.com/use-cases/ai-search-engine-api" rel="noopener noreferrer"&gt;search engine scraper&lt;/a&gt;. You query it with "best CRM software," and it returns what Google (and other top search engines) shows for that query: the top 10 organic results, the paid ads, the featured snippet, the "People Also Ask" box, and the local map pack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Return the full content of a link  &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;You have to supply the individual link/URL
&lt;/li&gt;
&lt;li&gt;You can't scrape specific search engine results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read more: &lt;a href="https://serpapi.com/blog/serpapi-vs-firecrawl/" rel="noopener noreferrer"&gt;SerpApi VS Firecrawl, which one to use?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Serperdev
&lt;/h2&gt;

&lt;p&gt;Serperdev is closer to SerpApi in how they return raw search engine results.&lt;/p&gt;

&lt;p&gt;Please read our blog post on SerpApi vs Serper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://serpapi.com/blog/compare-serpapi-with-the-alternatives-serper-and-searchapi/" rel="noopener noreferrer"&gt;Alternatives to SerpApi 2026 (Serper, SearchApi, and ValueSERP)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to choose Serper over SerpApi&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 When you want something closer to traditional SERP scraping with simpler needs or lighter usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Every web search API has its place. Tools like Brave, Exa, Tavily, or others shine for specific use cases.&lt;/p&gt;

&lt;p&gt;If your product depends on reliable, production-grade SERP data at scale across search, shopping, maps, reviews, and more, long-term stability matters. As you move from prototypes to real users, mature infrastructure, consistent parsers, strong uptime, and predictable data formats become critical.&lt;/p&gt;

&lt;p&gt;This is where SerpApi stands out. It’s more than a web search API. It’s a battle-tested web data platform teams trust to power AI features, SEO tools, lead generation, price monitoring, and product research.&lt;/p&gt;

&lt;p&gt;If you’re evaluating options, the best move is to try a few APIs side by side. But when reliability, coverage, and developer experience start to matter more than experiments, you’ll likely want something built for the long haul.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Scrape Google Flights Deals with a simple API</title>
      <dc:creator>Hilman Ramadhan</dc:creator>
      <pubDate>Tue, 14 Jul 2026 06:34:07 +0000</pubDate>
      <link>https://dev.to/serpapi/how-to-scrape-google-flights-deals-with-a-simple-api-l32</link>
      <guid>https://dev.to/serpapi/how-to-scrape-google-flights-deals-with-a-simple-api-l32</guid>
      <description>&lt;p&gt;Google Flights Deals helps travelers discover discounted flights without a specific destination. Instead of searching one route at a time, you can start with a departure airport or city and explore discounted destinations.&lt;/p&gt;

&lt;p&gt;This data can be useful for building flight-deal trackers, &lt;a href="https://serpapi.com/use-cases/travel-information" rel="noopener noreferrer"&gt;travel discovery tools&lt;/a&gt;, &lt;a href="https://serpapi.com/use-cases/price-monitoring" rel="noopener noreferrer"&gt;price-comparison&lt;/a&gt; dashboards, or destination-recommendation apps. In this tutorial, we'll use SerpApi's Google Flights Deals API to retrieve flight deals as structured JSON with cURL, Python, and JavaScript.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr8pm9l751ze9p3crrfwj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr8pm9l751ze9p3crrfwj.png" alt="Scrape Google Flights Deals website" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scrape Google Flights Deals website&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use the Google Flights Deals API
&lt;/h2&gt;

&lt;p&gt;A basic request uses these parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;engine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Set to &lt;code&gt;google_flights_deals&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api_key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Your private SerpApi API key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;departure_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Departure airport code or location ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;currency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Currency used for returned prices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An airport ID is normally an uppercase three-letter IATA code such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CDG&lt;/code&gt; for Paris Charles de Gaulle Airport&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AUS&lt;/code&gt; for Austin-Bergstrom International Airport&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MAD&lt;/code&gt; for Adolfo Suárez Madrid-Barajas Airport&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also provide a Google location ID beginning with &lt;code&gt;/m/&lt;/code&gt; or &lt;code&gt;/g/&lt;/code&gt;. Multiple departure locations can be separated with commas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get your API key
&lt;/h3&gt;

&lt;p&gt;Create a SerpApi account and retrieve your private key from the &lt;a href="https://serpapi.com/manage-api-key" rel="noopener noreferrer"&gt;API key page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Replace the following placeholder in each example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Never expose your API key in public source code or commit it to a public repository.&lt;/p&gt;

&lt;p&gt;The complete parameter reference is available in the official &lt;a href="https://serpapi.com/google-flights-deals-api" rel="noopener noreferrer"&gt;Google Flights Deals API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  cURL implementation
&lt;/h3&gt;

&lt;p&gt;Let's retrieve flight deals departing from Paris Charles de Gaulle Airport:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--get&lt;/span&gt; https://serpapi.com/search &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"google_flights_deals"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;departure_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"CDG"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SERPAPI_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters in this request are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;engine=google_flights_deals&lt;/code&gt; selects the Google Flights Deals engine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;departure_id=CDG&lt;/code&gt; searches for deals departing from Paris Charles de Gaulle Airport.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;currency=USD&lt;/code&gt; returns prices in US dollars.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API returns JSON by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python implementation
&lt;/h3&gt;

&lt;p&gt;Create a new Python file:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;Add the following code:&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;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_flights_deals&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;departure_id&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;CDG&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;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="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;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;departure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&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;departure_informations&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;departure_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;departure&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;airport_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;departure&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;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown departure&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;Flight deals from: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;departure_name&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&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;deals&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;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deal&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;flight_duration&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;duration&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;divmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;formatted_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;h &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;formatted_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not available&lt;/span&gt;&lt;span class="sh"&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;Destination:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;name&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;Unknown&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;Country:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown&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;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;deal&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;price&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;Not available&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;Average 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;deal&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;average_price&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;Not available&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;Discount:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;deal&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;discount_percentage&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;Not available&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;Outbound date:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;outbound_date&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;Not available&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;Return date:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;return_date&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;One-way flight&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;Route:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deal&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;departure_airport_code&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;?&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="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deal&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;arrival_airport_code&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;?&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="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;Duration:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatted_duration&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;Stops:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;stops&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;Not available&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;Airline:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;airline&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;Not available&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;Google Flights:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal&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;flight_link&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;Not available&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;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code prints selected fields from every item in the &lt;code&gt;deals&lt;/code&gt; array. It also converts the documented &lt;code&gt;flight_duration&lt;/code&gt; value from minutes into hours and minutes.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrs8hfe1lfy6iwo077ff.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrs8hfe1lfy6iwo077ff.png" alt="Flights Deals API scraper" width="799" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Flights Deals API scraper&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fields are accessed with &lt;code&gt;.get()&lt;/code&gt; because some values are not guaranteed to appear in every deal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export the flight deals to CSV
&lt;/h3&gt;

&lt;p&gt;You can also save the results to a CSV file for further analysis:&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;csv&lt;/span&gt;


&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&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;deals&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;rows&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destination&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;name&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destination_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;destination_id&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="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;deal&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;price&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;average_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;deal&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;average_price&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discount_percentage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;discount_percentage&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="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;outbound_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;outbound_date&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;return_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;return_date&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;departure_airport&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;departure_airport_code&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="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;arrival_airport&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;arrival_airport_code&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="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;flight_duration_minutes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;flight_duration&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="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;stops&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;stops&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;airline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;airline&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;airline_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;airline_code&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google_flights_link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;flight_link&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="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;serpapi_flight_link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deal&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;serpapi_flight_link&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="p"&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;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;destination&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;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destination_id&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;price&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;average_price&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;discount_percentage&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;outbound_date&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;return_date&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;departure_airport&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;arrival_airport&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;flight_duration_minutes&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;stops&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;airline&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;airline_code&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_flights_link&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;serpapi_flight_link&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="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;google_flights_deals.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="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csv_file&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;csv_file&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="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="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&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;Saved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; deals to &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google_flights_deals.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a file named &lt;code&gt;google_flights_deals.csv&lt;/code&gt; with one row for each deal.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc5tn6123mxl53k18pigf.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc5tn6123mxl53k18pigf.png" alt="Exported result in CSV file" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exported result in CSV file&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;SerpApi provides an official &lt;a href="https://github.com/serpapi/serpapi-javascript/" rel="noopener noreferrer"&gt;JavaScript package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install it with npm:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create an &lt;code&gt;index.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getJson&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serpapi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SERPAPI_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;getJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google_flights_deals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;departure_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CGK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SERPAPI_API_KEY&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&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="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;departure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;departure_informations&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deals&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Flight deals from: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
        &lt;span class="nx"&gt;departure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;airport_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="nx"&gt;departure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown departure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;deals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;averagePrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;average_price&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;discountPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discount_percentage&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;outboundDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outbound_date&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;returnDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;return_date&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;One-way flight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;departure_airport_code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; → `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
          &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arrival_airport_code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flight_duration&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stops&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;airline&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;flightLink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flight_link&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not available&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The official SerpApi JavaScript package exposes the &lt;code&gt;getJson&lt;/code&gt; function and supports both callbacks and promises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize Google Flights Deals results
&lt;/h2&gt;

&lt;p&gt;The API provides several ways to customize the returned deals. Here are three useful examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search using exact or flexible dates
&lt;/h3&gt;

&lt;p&gt;For an exact round trip, provide one &lt;code&gt;outbound_date&lt;/code&gt; and one &lt;code&gt;return_date&lt;/code&gt; in &lt;code&gt;YYYY-MM-DD&lt;/code&gt; format:&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;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;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_flights_deals&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;departure_id&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;FCO&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;outbound_date&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;2026-08-10&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;return_date&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;2026-08-17&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;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For flexible dates, use two comma-separated dates as the departure window:&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;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;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_flights_deals&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;departure_id&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;BCN&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;outbound_date&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;2026-09-01,2026-09-30&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;travel_duration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible &lt;code&gt;travel_duration&lt;/code&gt; values are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weekend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can alternatively use &lt;code&gt;trip_length&lt;/code&gt; for a custom duration:&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;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trip_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5,10&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requests trips lasting between 5 and 10 days.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;travel_duration&lt;/code&gt;, &lt;code&gt;trip_length&lt;/code&gt;, and &lt;code&gt;return_date&lt;/code&gt; cannot be used together. For a round trip with one exact outbound date, one exact return date is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter by stops and maximum price
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;stops&lt;/code&gt; to control how many connections are allowed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any number of stops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nonstop only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One stop or fewer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two stops or fewer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can combine it with &lt;code&gt;max_price&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;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;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_flights_deals&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;departure_id&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;CDG&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;stops&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This searches for nonstop deals costing no more than 500 in the selected currency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Include or exclude airlines
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;include_airlines&lt;/code&gt; to return deals from selected airlines:&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;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;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_flights_deals&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;departure_id&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;/m/0947l&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;include_airlines&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;W4,FR&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;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;W4&lt;/code&gt; — Wizz Air&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FR&lt;/code&gt; — Ryanair&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple airline codes are separated with commas.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;exclude_airlines&lt;/code&gt; instead when you want to remove specific airlines from the 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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exclude_airlines&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;include_airlines&lt;/code&gt; and &lt;code&gt;exclude_airlines&lt;/code&gt; parameters cannot be used together.&lt;/p&gt;

&lt;p&gt;The API also accepts the following alliance values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;STAR_ALLIANCE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SKYTEAM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ONEWORLD&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Searching for one-way flight deals
&lt;/h2&gt;

&lt;p&gt;Round trip is the default flight type. Set &lt;code&gt;type&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt; to search for one-way deals:&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;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;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_flights_deals&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;departure_id&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;MAD&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;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&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;USD&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;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The supported values are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Flight type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Round trip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One way&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a one-way search with a specific date, provide only &lt;code&gt;outbound_date&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;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outbound_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-09-15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not use &lt;code&gt;return_date&lt;/code&gt;, &lt;code&gt;travel_duration&lt;/code&gt;, or &lt;code&gt;trip_length&lt;/code&gt; with one-way flights. One-way deal results do not contain an &lt;code&gt;return_date&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What data can be retrieved from the Google Flights Deals API?
&lt;/h2&gt;

&lt;p&gt;The API returns information about the departure location and a list of available flight deals.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;departure_informations&lt;/code&gt; object can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Departure airport name and IATA code&lt;/li&gt;
&lt;li&gt;Departure city and city ID&lt;/li&gt;
&lt;li&gt;Departure country&lt;/li&gt;
&lt;li&gt;Airport latitude and longitude&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;deals&lt;/code&gt; array can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Destination name and country&lt;/li&gt;
&lt;li&gt;Destination Google Knowledge Graph ID&lt;/li&gt;
&lt;li&gt;Deal price&lt;/li&gt;
&lt;li&gt;Average price for the route&lt;/li&gt;
&lt;li&gt;Discount percentage&lt;/li&gt;
&lt;li&gt;Outbound and return dates&lt;/li&gt;
&lt;li&gt;Departure and arrival airport codes&lt;/li&gt;
&lt;li&gt;Flight duration in minutes&lt;/li&gt;
&lt;li&gt;Number of stops&lt;/li&gt;
&lt;li&gt;Airline name and code&lt;/li&gt;
&lt;li&gt;Destination description and highlights&lt;/li&gt;
&lt;li&gt;Destination thumbnail&lt;/li&gt;
&lt;li&gt;Direct Google Flights link&lt;/li&gt;
&lt;li&gt;SerpApi link for retrieving detailed flight information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some fields, including &lt;code&gt;average_price&lt;/code&gt;, &lt;code&gt;discount_percentage&lt;/code&gt;, &lt;code&gt;airline&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt;, are not always present. The &lt;code&gt;return_date&lt;/code&gt; field is also omitted for one-way flights.&lt;/p&gt;

&lt;p&gt;Here is a simplified example of a deal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/m/0k02q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"São Vicente"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cape Verde"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;468&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"average_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discount_percentage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outbound_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"return_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-27"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"departure_airport_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CDG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"arrival_airport_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VXE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flight_duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stops"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"airline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cabo Verde Airlines"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"airline_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flight_link"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.google.com/travel/flights?..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main results are available inside the &lt;code&gt;deals&lt;/code&gt; array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping Google Flights
&lt;/h2&gt;

&lt;p&gt;If you're interested in scraping the regular Google Flights website, we also have an API for that: &lt;a href="https://serpapi.com/google-flights-api" rel="noopener noreferrer"&gt;Google Flights API&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Here is the blog tutorial on &lt;a href="https://serpapi.com/blog/how-to-scrape-google-flights/" rel="noopener noreferrer"&gt;how to scrape Google Flights&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a video tutorial if you prefer to watch&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/embed/zqZSvuj7JMs?feature=oembed" rel="noopener noreferrer"&gt;https://www.youtube.com/embed/zqZSvuj7JMs?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;The SerpApi Google Flights Deals API makes it possible to retrieve Google Flights deal discovery results as structured JSON.&lt;/p&gt;

&lt;p&gt;With one request, you can collect destinations, deal prices, average prices, discount percentages, travel dates, airport codes, airlines, flight durations, stops, and links to Google Flights.&lt;/p&gt;

&lt;p&gt;You can also customize the search using exact dates, flexible date ranges, trip durations, maximum prices, stop limits, travel classes, and airline filters.&lt;/p&gt;

&lt;p&gt;For the complete and current parameter list, review the official Google Flights Deals API documentation.&lt;/p&gt;

</description>
    </item>
    <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://dev.to/serpapi/grok-ai-api-tutorial-from-chat-to-web-search-32bo</link>
      <guid>https://dev.to/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://dev.to/serpapi/how-to-scrape-100-search-results-on-google-num-parameter-4jm1</link>
      <guid>https://dev.to/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 - &amp;nbsp;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://dev.to/serpapi/how-to-scrape-tripadvisor-2025-tutorial-57o0</link>
      <guid>https://dev.to/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&amp;nbsp;&lt;code&gt;main.py&lt;/code&gt;&amp;nbsp;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&amp;nbsp;&lt;code&gt;tripadvisor.com&lt;/code&gt;. Head to&amp;nbsp;&lt;a href="https://serpapi.com/tripadvisor-domains" rel="noopener noreferrer"&gt;Tripadvisor domains&lt;/a&gt;&amp;nbsp;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;&amp;nbsp;- All Results&lt;br&gt;&lt;br&gt;
&lt;code&gt;r&lt;/code&gt;&amp;nbsp;- Restaurants&lt;br&gt;&lt;br&gt;
&lt;code&gt;A&lt;/code&gt;&amp;nbsp;- Things to Do&lt;br&gt;&lt;br&gt;
&lt;code&gt;h&lt;/code&gt;&amp;nbsp;- Hotels&lt;br&gt;&lt;br&gt;
&lt;code&gt;g&lt;/code&gt;&amp;nbsp;- Destinations&lt;br&gt;&lt;br&gt;
&lt;code&gt;v&lt;/code&gt;&amp;nbsp;- Vacation Rentals&lt;br&gt;&lt;br&gt;
&lt;code&gt;f&lt;/code&gt;&amp;nbsp;- 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://dev.to/serpapi/scrape-youtube-videos-in-python-1p6d</link>
      <guid>https://dev.to/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&amp;nbsp;&lt;code&gt;main.py&lt;/code&gt;&amp;nbsp;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://dev.to/serpapi/build-a-serp-rank-tracker-app-with-this-api-4nbj</link>
      <guid>https://dev.to/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://dev.to/serpapi/create-a-super-fast-ai-assistant-with-groq-without-a-database-7e7</link>
      <guid>https://dev.to/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://dev.to/serpapi/build-an-ai-voice-assistant-like-siri-use-openai-ai-assistant-24do</link>
      <guid>https://dev.to/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>
  </channel>
</rss>
