From e19b278665e4c7cda08a0b2803766644e714b2d7 Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Sun, 9 Aug 2026 06:55:51 -0500 Subject: [PATCH 1/4] [SDK] Set initial release version to 1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The serpapi package has never been published to NuGet — start at 1.0.0 rather than 2.0.0 for the first release. Co-Authored-By: Claude Fable 5 --- serpapi/serpapi.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serpapi/serpapi.csproj b/serpapi/serpapi.csproj index 71b50ba..b682209 100644 --- a/serpapi/serpapi.csproj +++ b/serpapi/serpapi.csproj @@ -3,7 +3,7 @@ netstandard2.0;net7.0;net8.0;net9.0;net10.0 SerpApi serpapi - 2.0.0 + 1.0.0 SerpApi SerpApi LLC MIT From 23a7dfe4b67dc002085371ca378669f0a45f8fd9 Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Sun, 9 Aug 2026 06:57:00 -0500 Subject: [PATCH 2/4] [Docs] Note unpublished package status, add migration guide - Flag that serpapi hasn't shipped its first NuGet release yet, so the badge/link aren't mistaken for broken. - Add a migration guide mapping the legacy google-search-results-dotnet API (SerpApi/Hashtable/JObject, synchronous) to this package's SerpApiClient/Dictionary/SerpApiResponse, async-first API. Co-Authored-By: Claude Fable 5 --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index 3cf1f3d..e4d0079 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![NuGet](https://img.shields.io/nuget/v/serpapi)](https://www.nuget.org/packages/serpapi) [![Build](https://github.com/serpapi/serpapi-dotnet/actions/workflows/ci.yml/badge.svg)](https://github.com/serpapi/serpapi-dotnet/actions/workflows/ci.yml) +> **Not yet published.** The `serpapi` package hasn't shipped its first NuGet release — the badge above will go green once v1.0.0 is out. Until then, build from source (see [Contributing](#contributing)) or reference the project directly. + Integrate search data into your .NET application, AI workflow, or LLM/RAG pipeline. This is the official .NET client for [SerpApi](https://serpapi.com). SerpApi supports Google, Google Maps, Google Shopping, Bing, Baidu, Yandex, Yahoo, DuckDuckGo, eBay, Walmart, YouTube, and [100+ engines](https://serpapi.com). @@ -34,6 +36,45 @@ CI builds and tests every target framework above on both **Linux** and **Windows dotnet add package serpapi ``` +## Migrating from google-search-results-dotnet + +This package (`serpapi`) is a ground-up rewrite of the previous official client, [`google-search-results-dotnet`](https://www.nuget.org/packages/google-search-results-dotnet). It's a new package ID, not an in-place upgrade — install `serpapi` alongside or instead of the old package; there's no automatic migration. + +| | `google-search-results-dotnet` (legacy) | `serpapi` (this package) | +|---|---|---| +| Client type | `SerpApi` | `SerpApiClient` | +| Construction | `new SerpApi(Hashtable defaultParameter)` | `new SerpApiClient(string apiKey, SerpApiClientOptions? options = null)` | +| Parameters | `Hashtable` | `Dictionary` | +| Search (JSON) | `JObject search(Hashtable)` — synchronous | `Task SearchAsync(Dictionary, CancellationToken)` — async (sync `Search(...)` wrapper also available) | +| Search (HTML) | `string html(Hashtable)` | `Task HtmlAsync(...)` / `Html(...)` | +| Archive | `JObject searchArchive(string searchId)` | `Task SearchArchiveAsync(string searchId, ...)` / `SearchArchive(...)` | +| Account info | `JObject account(string apiKey = "")` | `Task AccountAsync(...)` / `Account()` | +| Locations | `JArray location(Hashtable)` | `Task LocationAsync(string query, int limit, ...)` / `Location(...)` | +| Result parsing | `Newtonsoft.Json.Linq.JObject`/`JArray` | `System.Text.Json`-based `SerpApiResponse` (indexer, typed convenience properties, `As()`/`GetProperty()`) | +| Pagination | Manual — construct next-page requests yourself | Built in: `NextPageAsync(response)` and `SearchPagesAsync(parameters)` (`IAsyncEnumerable`) | +| Timeout | `setTimeoutSeconds(int)` on the client | `SerpApiClientOptions.Timeout` at construction | +| Errors | Exceptions from `Newtonsoft.Json`/`HttpClient` directly | Typed exceptions: `SerpApiKeyException`, `SerpApiHttpException`, `SerpApiTimeoutException`, `SerpApiException` | +| Dependency injection | Not supported | `services.AddSerpApi(...)` with `IHttpClientFactory` | +| Target frameworks | .NET Framework era APIs | .NET Standard 2.0, .NET 7–10 | + +Before: + +```csharp +var serp = new SerpApi(new Hashtable { { "api_key", apiKey } }); +JObject results = serp.search(new Hashtable { { "engine", "google" }, { "q", "coffee" } }); +``` + +After: + +```csharp +using var client = new SerpApiClient(apiKey); +using var results = await client.SearchAsync(new Dictionary +{ + ["engine"] = "google", + ["q"] = "coffee" +}); +``` + ## Simple Usage ```csharp From ea0e126df641530ea2bd5a8da012d3f16fbeb7dd Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Mon, 10 Aug 2026 06:38:15 -0500 Subject: [PATCH 3/4] [Docs] Correct migration guide to match published legacy API The previous guide assumed the legacy client's API from an early local commit (a single SerpApi class with lowercase methods). The published google-search-results-dotnet package actually ships per-engine classes (GoogleSearch, BingSearch, ...) with GetJson()/GetAccount()/etc. Fetched the real README from serpapi/google-search-results-dotnet to correct the table, and linked both repos directly instead of the NuGet listing. Co-Authored-By: Claude Fable 5 --- README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index e4d0079..ee7ed18 100644 --- a/README.md +++ b/README.md @@ -38,30 +38,29 @@ dotnet add package serpapi ## Migrating from google-search-results-dotnet -This package (`serpapi`) is a ground-up rewrite of the previous official client, [`google-search-results-dotnet`](https://www.nuget.org/packages/google-search-results-dotnet). It's a new package ID, not an in-place upgrade — install `serpapi` alongside or instead of the old package; there's no automatic migration. +`serpapi` replaces [`google-search-results-dotnet`](https://github.com/serpapi/google-search-results-dotnet), the previous official client. New package ID — install `serpapi` instead; there's no automatic upgrade path. -| | `google-search-results-dotnet` (legacy) | `serpapi` (this package) | +| | Old ([`google-search-results-dotnet`](https://github.com/serpapi/google-search-results-dotnet)) | New ([`serpapi-dotnet`](https://github.com/serpapi/serpapi-dotnet)) | |---|---|---| -| Client type | `SerpApi` | `SerpApiClient` | -| Construction | `new SerpApi(Hashtable defaultParameter)` | `new SerpApiClient(string apiKey, SerpApiClientOptions? options = null)` | +| Client | One class per engine: `GoogleSearch`, `BingSearch`, `BaiduSearch`, `YahooSearch`, `YandexSearch`, `EbaySearch`, or generic `SerpApiSearch(parameter, apiKey, engine)` | One `SerpApiClient` for every engine — set `["engine"] = "google"` in the parameters | +| Construction | `new GoogleSearch(Hashtable parameter, string apiKey)` | `new SerpApiClient(string apiKey)` | | Parameters | `Hashtable` | `Dictionary` | -| Search (JSON) | `JObject search(Hashtable)` — synchronous | `Task SearchAsync(Dictionary, CancellationToken)` — async (sync `Search(...)` wrapper also available) | -| Search (HTML) | `string html(Hashtable)` | `Task HtmlAsync(...)` / `Html(...)` | -| Archive | `JObject searchArchive(string searchId)` | `Task SearchArchiveAsync(string searchId, ...)` / `SearchArchive(...)` | -| Account info | `JObject account(string apiKey = "")` | `Task AccountAsync(...)` / `Account()` | -| Locations | `JArray location(Hashtable)` | `Task LocationAsync(string query, int limit, ...)` / `Location(...)` | -| Result parsing | `Newtonsoft.Json.Linq.JObject`/`JArray` | `System.Text.Json`-based `SerpApiResponse` (indexer, typed convenience properties, `As()`/`GetProperty()`) | -| Pagination | Manual — construct next-page requests yourself | Built in: `NextPageAsync(response)` and `SearchPagesAsync(parameters)` (`IAsyncEnumerable`) | -| Timeout | `setTimeoutSeconds(int)` on the client | `SerpApiClientOptions.Timeout` at construction | -| Errors | Exceptions from `Newtonsoft.Json`/`HttpClient` directly | Typed exceptions: `SerpApiKeyException`, `SerpApiHttpException`, `SerpApiTimeoutException`, `SerpApiException` | -| Dependency injection | Not supported | `services.AddSerpApi(...)` with `IHttpClientFactory` | -| Target frameworks | .NET Framework era APIs | .NET Standard 2.0, .NET 7–10 | +| Search | `JObject data = search.GetJson();` — synchronous | `await client.SearchAsync(parameters)` → `SerpApiResponse` (sync `Search(...)` also available) | +| Archive | `search.GetSearchArchiveJson(id)` | `await client.SearchArchiveAsync(id)` | +| Account | `search.GetAccount()` | `await client.AccountAsync()` | +| Locations | `search.GetLocation(query, limit)` | `await client.LocationAsync(query, limit)` | +| Timeout | `search.setTimeoutSeconds(int)` | `SerpApiClientOptions.Timeout` at construction | +| Cleanup | `search.Close()` | `using var client = ...` (implements `IDisposable`) | +| Result type | `Newtonsoft.Json.Linq.JObject`/`JArray` | `System.Text.Json`-based `SerpApiResponse` (indexer, `As()`, `GetProperty()`) | +| Errors | `SerpApiSearchException` | `SerpApiKeyException`, `SerpApiHttpException`, `SerpApiTimeoutException`, `SerpApiException` | +| Pagination | Manual | `NextPageAsync(response)` / `SearchPagesAsync(parameters)` (`IAsyncEnumerable`) | Before: ```csharp -var serp = new SerpApi(new Hashtable { { "api_key", apiKey } }); -JObject results = serp.search(new Hashtable { { "engine", "google" }, { "q", "coffee" } }); +var ht = new Hashtable { { "q", "coffee" } }; +GoogleSearch search = new GoogleSearch(ht, apiKey); +JObject data = search.GetJson(); ``` After: From c0f4ba285a15eaa0cb641417ef1bed4d50d08e49 Mon Sep 17 00:00:00 2001 From: Victor Benarbia Date: Mon, 10 Aug 2026 06:54:39 -0500 Subject: [PATCH 4/4] [Docs] Restructure README to match serpapi-ruby layout Align section order and naming with the serpapi-ruby README: - Installation now points to the legacy library + migration guide and the supported-versions matrix, matching Ruby's installation intro. - Simple Usage gains playground/signup/env-var guidance. - New "Search API advanced usage with Google search engine" section with a commented full-parameter example and a Documentations link list. - Advanced usage groups concurrency, pagination, error handling, DI, resilience, and proxy under one section. - "APIs supported" now hosts Location/Search Archive/Account APIs. - Per-engine examples renamed to "Basic example per search engine" with Ruby-style doc links. - Migration section moved near the end as "Migration quick guide" and rewritten in Ruby's old-way/new-way commented-code style. - Compatibility table renamed "Supported .NET versions" and moved before Contributing, mirroring "Supported Ruby versions". Co-Authored-By: Claude Fable 5 --- README.md | 441 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 251 insertions(+), 190 deletions(-) diff --git a/README.md b/README.md index ee7ed18..a611eca 100644 --- a/README.md +++ b/README.md @@ -5,74 +5,32 @@ > **Not yet published.** The `serpapi` package hasn't shipped its first NuGet release — the badge above will go green once v1.0.0 is out. Until then, build from source (see [Contributing](#contributing)) or reference the project directly. -Integrate search data into your .NET application, AI workflow, or LLM/RAG pipeline. This is the official .NET client for [SerpApi](https://serpapi.com). +Integrate search data into your AI workflow, RAG / fine-tuning, or .NET application using this official wrapper for [SerpApi](https://serpapi.com). -SerpApi supports Google, Google Maps, Google Shopping, Bing, Baidu, Yandex, Yahoo, DuckDuckGo, eBay, Walmart, YouTube, and [100+ engines](https://serpapi.com). +SerpApi supports Google, Google Maps, Google Shopping, Bing, Baidu, Yandex, Yahoo, DuckDuckGo, eBay, Walmart, YouTube, App Stores, and [more](https://serpapi.com). -## Features +Query a vast range of data at scale, including web search results, flight schedules, stock market data, news headlines, and [more](https://serpapi.com). -- Async-first with full `CancellationToken` support -- Sync convenience wrappers -- `IAsyncEnumerable` pagination -- Dependency injection integration (`IHttpClientFactory`) -- Targets .NET Standard 2.0, .NET 7, 8, 9, and 10 -- Zero external runtime dependencies +## Features -## Compatibility +- `async`-first → non-blocking API with full `CancellationToken` support, plus sync convenience wrappers +- `IAsyncEnumerable` pagination → stream every page of results with one loop +- Dependency injection → `IHttpClientFactory` integration for ASP.NET Core / generic host +- Broad reach → targets .NET Standard 2.0 and .NET 7, 8, 9, 10 +- Zero external runtime dependencies on modern .NET +- Extensive documentation and real-world examples included throughout -| Target framework | Minimum consumer runtime | Notes | -|---|---|---| -| `netstandard2.0` | .NET Framework 4.6.1+, .NET Core 2.0+, Mono, Xamarin, UWP | Ships `Microsoft.Bcl.AsyncInterfaces` and `System.Text.Json` as polyfills | -| `net7.0` | .NET 7 | Out of support upstream, still built and tested | -| `net8.0` | .NET 8 (LTS) | | -| `net9.0` | .NET 9 (STS) | | -| `net10.0` | .NET 10 (LTS) | Used for `dotnet pack` and local dev builds | +## Installation -CI builds and tests every target framework above on both **Linux** and **Windows**. Building locally requires the [.NET 10 SDK](https://dotnet.microsoft.com/download) (a single SDK at or above the highest TFM can build all lower ones). +.NET 7 and higher are supported, plus .NET Framework 4.6.1+ via .NET Standard 2.0. Check [Supported .NET versions](#supported-net-versions) for the full matrix. -## Installation +If you are upgrading from the legacy [google-search-results-dotnet](https://github.com/serpapi/google-search-results-dotnet) library, check our [migration guide](#migration-quick-guide). ```bash dotnet add package serpapi ``` -## Migrating from google-search-results-dotnet - -`serpapi` replaces [`google-search-results-dotnet`](https://github.com/serpapi/google-search-results-dotnet), the previous official client. New package ID — install `serpapi` instead; there's no automatic upgrade path. - -| | Old ([`google-search-results-dotnet`](https://github.com/serpapi/google-search-results-dotnet)) | New ([`serpapi-dotnet`](https://github.com/serpapi/serpapi-dotnet)) | -|---|---|---| -| Client | One class per engine: `GoogleSearch`, `BingSearch`, `BaiduSearch`, `YahooSearch`, `YandexSearch`, `EbaySearch`, or generic `SerpApiSearch(parameter, apiKey, engine)` | One `SerpApiClient` for every engine — set `["engine"] = "google"` in the parameters | -| Construction | `new GoogleSearch(Hashtable parameter, string apiKey)` | `new SerpApiClient(string apiKey)` | -| Parameters | `Hashtable` | `Dictionary` | -| Search | `JObject data = search.GetJson();` — synchronous | `await client.SearchAsync(parameters)` → `SerpApiResponse` (sync `Search(...)` also available) | -| Archive | `search.GetSearchArchiveJson(id)` | `await client.SearchArchiveAsync(id)` | -| Account | `search.GetAccount()` | `await client.AccountAsync()` | -| Locations | `search.GetLocation(query, limit)` | `await client.LocationAsync(query, limit)` | -| Timeout | `search.setTimeoutSeconds(int)` | `SerpApiClientOptions.Timeout` at construction | -| Cleanup | `search.Close()` | `using var client = ...` (implements `IDisposable`) | -| Result type | `Newtonsoft.Json.Linq.JObject`/`JArray` | `System.Text.Json`-based `SerpApiResponse` (indexer, `As()`, `GetProperty()`) | -| Errors | `SerpApiSearchException` | `SerpApiKeyException`, `SerpApiHttpException`, `SerpApiTimeoutException`, `SerpApiException` | -| Pagination | Manual | `NextPageAsync(response)` / `SearchPagesAsync(parameters)` (`IAsyncEnumerable`) | - -Before: - -```csharp -var ht = new Hashtable { { "q", "coffee" } }; -GoogleSearch search = new GoogleSearch(ht, apiKey); -JObject data = search.GetJson(); -``` - -After: - -```csharp -using var client = new SerpApiClient(apiKey); -using var results = await client.SearchAsync(new Dictionary -{ - ["engine"] = "google", - ["q"] = "coffee" -}); -``` +[NuGet package page](https://www.nuget.org/packages/serpapi) ## Simple Usage @@ -93,64 +51,74 @@ foreach (var result in results.OrganicResults!.Value.EnumerateArray()) } ``` -### Error handling +This example runs a search for "coffee" on Google Light. See the [playground](https://serpapi.com/playground) to generate your own code. -```csharp -try -{ - using var results = await client.SearchAsync(parameters); -} -catch (SerpApiKeyException) { /* 401 — invalid API key */ } -catch (SerpApiHttpException ex) { /* 429, 500, etc — ex.StatusCode */ } -catch (SerpApiTimeoutException) { /* request timed out */ } -catch (SerpApiException ex) { /* catch-all */ } -``` +The SerpApi key can be obtained from [serpapi.com/signup](https://serpapi.com/users/sign_up?plan=free). + +Environment variables are a secure, safe, and easy way to manage secrets. +Set `export SERPAPI_KEY=` in your shell. +.NET accesses these variables via `Environment.GetEnvironmentVariable("SERPAPI_KEY")`. -## Search API usage +## Search API advanced usage with Google search engine -### Get JSON results +This example dives into the available parameters for the Google search engine. +The list of parameters depends on the chosen search engine. ```csharp -using var results = await client.SearchAsync(new Dictionary +using SerpApi; + +// serpapi client created with an API key and optional configuration +using var client = new SerpApiClient( + Environment.GetEnvironmentVariable("SERPAPI_KEY")!, + new SerpApiClientOptions + { + Timeout = TimeSpan.FromSeconds(30) // HTTP timeout (default: 60s) + }); + +// search query overview (more fields available depending on search engine) +var parameters = new Dictionary { - ["engine"] = "google_light", - ["q"] = "coffee", - ["num"] = "10" -}); + // select the search engine (full list: https://serpapi.com/) + ["engine"] = "google", + // actual search query + ["q"] = "Coffee", + // then add search engine specific options. + // for example: google specific parameters: https://serpapi.com/search-api + ["google_domain"] = "google.com", + ["location"] = "Austin, Texas", // see: Location API + ["device"] = "desktop", // desktop|mobile|tablet + ["hl"] = "en", // Google UI language + ["gl"] = "us", // Google country + ["safe"] = "active", // safe search flag + ["start"] = "0", // pagination offset + ["num"] = "10" // number of results +}; +// search results as a parsed response +using var results = await client.SearchAsync(parameters); Console.WriteLine(results.SearchId); Console.WriteLine(results.OrganicResults); Console.WriteLine(results["local_results"]); -``` - -### Get HTML results -```csharp -string html = await client.HtmlAsync(new Dictionary -{ - ["engine"] = "google_light", - ["q"] = "coffee" -}); +// search results as a raw HTML string +string rawHtml = await client.HtmlAsync(parameters); ``` -### Pagination +→ [SerpApi documentation](https://serpapi.com/search-api). -```csharp -// Next page -using var page2 = await client.NextPageAsync(results); +### Documentations -// Iterate all pages as an async stream -await foreach (var page in client.SearchPagesAsync(parameters, maxPages: 5)) -{ - using (page) - { - Console.WriteLine($"Page has {page.OrganicResults?.GetArrayLength()} results"); - } -} -``` +- [Full documentation on SerpApi.com](https://serpapi.com) +- [Library GitHub page](https://github.com/serpapi/serpapi-dotnet) +- [Library NuGet page](https://www.nuget.org/packages/serpapi) +- [API health status](https://serpapi.com/status) + +## Advanced search API usage ### Search concurrently +A single `SerpApiClient` can run many searches at once — no thread pool or connection juggling required. + ```csharp var tasks = new[] { @@ -179,6 +147,78 @@ finally } ``` +### Pagination + +```csharp +// Next page +using var page2 = await client.NextPageAsync(results); + +// Iterate all pages as an async stream +await foreach (var page in client.SearchPagesAsync(parameters, maxPages: 5)) +{ + using (page) + { + Console.WriteLine($"Page has {page.OrganicResults?.GetArrayLength()} results"); + } +} +``` + +### Error handling + +```csharp +try +{ + using var results = await client.SearchAsync(parameters); +} +catch (SerpApiKeyException) { /* 401 — invalid API key */ } +catch (SerpApiHttpException ex) { /* 429, 500, etc — ex.StatusCode */ } +catch (SerpApiTimeoutException) { /* request timed out */ } +catch (SerpApiException ex) { /* catch-all */ } +``` + +### Dependency Injection + +```csharp +builder.Services.AddSerpApi(options => +{ + options.ApiKey = builder.Configuration["SerpApi:ApiKey"]!; + options.Timeout = TimeSpan.FromSeconds(30); +}); +``` + +Uses `IHttpClientFactory` for connection management. + +#### Resilience + +Install the `Microsoft.Extensions.Http.Resilience` package: + +```bash +dotnet add package Microsoft.Extensions.Http.Resilience +``` + +```csharp +builder.Services.AddSerpApi(options => +{ + options.ApiKey = builder.Configuration["SerpApi:ApiKey"]!; +}) +.AddStandardResilienceHandler(); +``` + +#### Corporate proxy + +```csharp +var handler = new HttpClientHandler +{ + Proxy = new WebProxy("http://proxy.corp.example:8080"), + UseProxy = true +}; +using var client = new SerpApiClient( + new HttpClient(handler), + new SerpApiClientOptions { ApiKey = "YOUR_API_KEY" }); +``` + +## APIs supported + ### Location API ```csharp @@ -189,12 +229,18 @@ foreach (var loc in locations.EnumerateArray()) } ``` +NOTE: api_key is not required for this endpoint. + ### Search Archive API -Retrieve a previous search (0 credits): +This API allows retrieving previous search results (free of charge). +First, run a search and save the search ID; then fetch it back from the archive. ```csharp -using var archived = await client.SearchArchiveAsync("previous_search_id"); +using var results = await client.SearchAsync(parameters); +string searchId = results.SearchId!; + +using var archived = await client.SearchArchiveAsync(searchId); ``` ### Account API @@ -204,9 +250,11 @@ using var account = await client.AccountAsync(); Console.WriteLine(account["plan_id"]); ``` -## Basic examples per search engine +It prints your account information: plan, searches left, usage this month, and more. -### Search Google +## Basic example per search engine + +### Search google ```csharp using var results = await client.SearchAsync(new Dictionary @@ -217,9 +265,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/search-api +see: [https://serpapi.com/search-api](https://serpapi.com/search-api) -### Search Google Light +### Search google light ```csharp using var results = await client.SearchAsync(new Dictionary @@ -229,9 +277,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-light-api +see: [https://serpapi.com/google-light-api](https://serpapi.com/google-light-api) -### Search Google Scholar +### Search google scholar ```csharp using var results = await client.SearchAsync(new Dictionary @@ -241,9 +289,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-scholar-api +see: [https://serpapi.com/google-scholar-api](https://serpapi.com/google-scholar-api) -### Search Google News +### Search google news ```csharp using var results = await client.SearchAsync(new Dictionary @@ -253,9 +301,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-news-api +see: [https://serpapi.com/google-news-api](https://serpapi.com/google-news-api) -### Search Google Maps +### Search google maps ```csharp using var results = await client.SearchAsync(new Dictionary @@ -266,9 +314,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-maps-api +see: [https://serpapi.com/google-maps-api](https://serpapi.com/google-maps-api) -### Search Google Shopping +### Search google shopping ```csharp using var results = await client.SearchAsync(new Dictionary @@ -278,9 +326,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-shopping-api +see: [https://serpapi.com/google-shopping-api](https://serpapi.com/google-shopping-api) -### Search Google Jobs +### Search google jobs ```csharp using var results = await client.SearchAsync(new Dictionary @@ -290,9 +338,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-jobs-api +see: [https://serpapi.com/google-jobs-api](https://serpapi.com/google-jobs-api) -### Search Google Images +### Search google images ```csharp using var results = await client.SearchAsync(new Dictionary @@ -302,9 +350,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/images-results +see: [https://serpapi.com/images-results](https://serpapi.com/images-results) -### Search Google Finance +### Search google finance ```csharp using var results = await client.SearchAsync(new Dictionary @@ -314,9 +362,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/google-finance-api +see: [https://serpapi.com/google-finance-api](https://serpapi.com/google-finance-api) -### Search Bing +### Search bing ```csharp using var results = await client.SearchAsync(new Dictionary @@ -326,9 +374,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/bing-search-api +see: [https://serpapi.com/bing-search-api](https://serpapi.com/bing-search-api) -### Search DuckDuckGo +### Search duckduckgo ```csharp using var results = await client.SearchAsync(new Dictionary @@ -338,9 +386,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/duckduckgo-search-api +see: [https://serpapi.com/duckduckgo-search-api](https://serpapi.com/duckduckgo-search-api) -### Search Baidu +### Search baidu ```csharp using var results = await client.SearchAsync(new Dictionary @@ -350,9 +398,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/baidu-search-api +see: [https://serpapi.com/baidu-search-api](https://serpapi.com/baidu-search-api) -### Search Yahoo +### Search yahoo ```csharp using var results = await client.SearchAsync(new Dictionary @@ -362,9 +410,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/yahoo-search-api +see: [https://serpapi.com/yahoo-search-api](https://serpapi.com/yahoo-search-api) -### Search YouTube +### Search youtube ```csharp using var results = await client.SearchAsync(new Dictionary @@ -374,9 +422,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/youtube-search-api +see: [https://serpapi.com/youtube-search-api](https://serpapi.com/youtube-search-api) -### Search Walmart +### Search walmart ```csharp using var results = await client.SearchAsync(new Dictionary @@ -386,9 +434,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/walmart-search-api +see: [https://serpapi.com/walmart-search-api](https://serpapi.com/walmart-search-api) -### Search eBay +### Search ebay ```csharp using var results = await client.SearchAsync(new Dictionary @@ -398,9 +446,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/ebay-search-api +see: [https://serpapi.com/ebay-search-api](https://serpapi.com/ebay-search-api) -### Search Amazon +### Search amazon ```csharp using var results = await client.SearchAsync(new Dictionary @@ -410,9 +458,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/amazon-search-api +see: [https://serpapi.com/amazon-search-api](https://serpapi.com/amazon-search-api) -### Search Naver +### Search naver ```csharp using var results = await client.SearchAsync(new Dictionary @@ -422,9 +470,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/naver-search-api +see: [https://serpapi.com/naver-search-api](https://serpapi.com/naver-search-api) -### Search Apple App Store +### Search apple app store ```csharp using var results = await client.SearchAsync(new Dictionary @@ -434,9 +482,9 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/apple-app-store +see: [https://serpapi.com/apple-app-store](https://serpapi.com/apple-app-store) -### Search Home Depot +### Search home depot ```csharp using var results = await client.SearchAsync(new Dictionary @@ -446,57 +494,7 @@ using var results = await client.SearchAsync(new Dictionary }); ``` -* see: https://serpapi.com/home-depot-search-api - -## Configuration - -```csharp -using var client = new SerpApiClient("YOUR_API_KEY", new SerpApiClientOptions -{ - Timeout = TimeSpan.FromSeconds(30) -}); -``` - -### Dependency Injection - -```csharp -builder.Services.AddSerpApi(options => -{ - options.ApiKey = builder.Configuration["SerpApi:ApiKey"]!; - options.Timeout = TimeSpan.FromSeconds(30); -}); -``` - -Uses `IHttpClientFactory` for connection management. - -#### Resilience - -Install the `Microsoft.Extensions.Http.Resilience` package: - -```bash -dotnet add package Microsoft.Extensions.Http.Resilience -``` - -```csharp -builder.Services.AddSerpApi(options => -{ - options.ApiKey = builder.Configuration["SerpApi:ApiKey"]!; -}) -.AddStandardResilienceHandler(); -``` - -#### Corporate proxy - -```csharp -var handler = new HttpClientHandler -{ - Proxy = new WebProxy("http://proxy.corp.example:8080"), - UseProxy = true -}; -using var client = new SerpApiClient( - new HttpClient(handler), - new SerpApiClientOptions { ApiKey = "YOUR_API_KEY" }); -``` +see: [https://serpapi.com/home-depot-search-api](https://serpapi.com/home-depot-search-api) ## Examples @@ -519,9 +517,72 @@ cd examples/LeadFinder dotnet run ``` +## Migration quick guide + +If you were already using the [google-search-results-dotnet](https://github.com/serpapi/google-search-results-dotnet) package, here are the changes. It's a new package ID — install `serpapi` instead; there's no automatic upgrade path. + +```csharp +// define a search +// old way: one class per engine (GoogleSearch, BingSearch, BaiduSearch, ...) +var ht = new Hashtable { { "q", "coffee" } }; +GoogleSearch search = new GoogleSearch(ht, apiKey); +// new way: one client for every engine, selected via the "engine" parameter +using var client = new SerpApiClient(apiKey); +var parameters = new Dictionary { ["engine"] = "google", ["q"] = "coffee" }; + +// search returns JSON +// old way (synchronous, Newtonsoft JObject) +JObject data = search.GetJson(); +// new way (async, System.Text.Json based SerpApiResponse) +using var results = await client.SearchAsync(parameters); + +// search returns raw HTML +// old way +string html = search.GetHtml(); +// new way +string html = await client.HtmlAsync(parameters); + +// other methods: the Get prefix is removed, Async suffix added +// old -> new way +// search.GetSearchArchiveJson(id) -> await client.SearchArchiveAsync(id) +// search.GetAccount() -> await client.AccountAsync() +// search.GetLocation(q, limit) -> await client.LocationAsync(q, limit) + +// timeout +// old way +search.setTimeoutSeconds(30); +// new way (at construction) +using var client = new SerpApiClient(apiKey, new SerpApiClientOptions { Timeout = TimeSpan.FromSeconds(30) }); + +// cleanup +// old way +search.Close(); +// new way: the client implements IDisposable +// (and each SerpApiResponse is IDisposable too) +``` + +Most notable improvements: +- Async-first API with `CancellationToken` support (sync wrappers still available). +- `System.Text.Json` instead of `Newtonsoft.Json` — zero external dependencies on modern .NET. +- Typed errors: `SerpApiKeyException`, `SerpApiHttpException`, `SerpApiTimeoutException`, `SerpApiException` (was a single `SerpApiSearchException`). +- Built-in pagination: `NextPageAsync(response)` and `SearchPagesAsync(parameters)` (`IAsyncEnumerable`). +- Dependency injection via `services.AddSerpApi(...)`. + +## Supported .NET versions + +| Target framework | Minimum consumer runtime | Notes | +|---|---|---| +| `netstandard2.0` | .NET Framework 4.6.1+, .NET Core 2.0+, Mono, Xamarin, UWP | Ships `Microsoft.Bcl.AsyncInterfaces` and `System.Text.Json` as polyfills | +| `net7.0` | .NET 7 | Out of support upstream, still built and tested | +| `net8.0` | .NET 8 (LTS) | | +| `net9.0` | .NET 9 (STS) | | +| `net10.0` | .NET 10 (LTS) | Used for `dotnet pack` and local dev builds | + +CI builds and tests every target framework above on both **Linux** and **Windows**. Building locally requires the [.NET 10 SDK](https://dotnet.microsoft.com/download) (a single SDK at or above the highest TFM can build all lower ones). + ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/serpapi/serpapi-dotnet. See [Compatibility](#compatibility) for SDK requirements. +Bug reports and pull requests are welcome on GitHub at https://github.com/serpapi/serpapi-dotnet. See [Supported .NET versions](#supported-net-versions) for SDK requirements. ```bash git clone https://github.com/serpapi/serpapi-dotnet.git