# Web Search Results API — Bing & DuckDuckGo SERP, No Key (`insight.solutions/web-search-api`) Actor

Run a list of queries against DuckDuckGo and Bing and get organic search results in one schema: title, real destination URL, display URL, snippet, position and page. A relevance guard suppresses pages of unrelated results and never bills for them. No API key, no headless browser, no Google.

- **URL**: https://apify.com/insight.solutions/web-search-api.md
- **Developed by:** [Insight Solutions](https://apify.com/insight.solutions) (community)
- **Categories:** SEO tools, Developer tools, AI
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.36 / 1,000 search result returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Web Search Results API — Bing & DuckDuckGo SERP, No Key

Search the web from code. Give this Actor a list of queries and it returns **organic search results from
DuckDuckGo and Bing in one schema** — title, the real destination URL, display URL, snippet, position,
page and engine — with no API key, no headless browser and no captcha solving.

It also does something no other search Actor does: it **checks that the results answer the query you
asked**, and when they do not, it returns a free diagnostic row instead of a bill.

- **Two engines, one table.** DuckDuckGo first, Bing second, the same columns for both, duplicates
  across the two dropped and named in `alsoOn`.
- **Real URLs.** Both engines wrap every link in a redirect. This Actor decodes it, strips the click ids
  (`msockid`, `utm_*`) and hands you the page itself.
- **Organic results only.** Ad blocks are not read at all, so `isAd` is `false` by construction rather
  than by filtering.
- **Free per-query summaries** telling you what came back, how many pages it took, what the engine
  estimated the total at, and why paging stopped.
- **$0.60 per 1,000 results**, and nothing at all for a query that could not be answered.

### At a glance

**Input** — this is the Store prefill; paste it and run:

```json
{ "queries": ["web scraping tools"], "engines": ["duckduckgo", "bing"], "maxResultsPerQuery": 10, "country": "US", "language": "en", "safeSearch": "moderate", "deduplicate": true, "relevanceGuard": "on" }
```

**Output** — one row per organic result; the fields you will use most are `query`, `engine`, `position`, `title`, `url` and `snippet` (full list under *Output reference*). Anything that could not be answered comes back as a free diagnostic row (`ok: false`, `errorType`, `error`) instead of a charge.

**Price** — $0.60 per 1,000 results on the FREE tier (+ $0.001 per run); query summaries, diagnostic rows, unrelated pages and their residential retry, dropped duplicates and a query that returned nothing are all free. Pay-per-event, no API key, no browser, limited permissions — works over the Apify MCP server (`mcp.apify.com`) and with agentic (x402) payments.

**From code** — `client.actor("insight.solutions/web-search-api").call(run_input={"queries": ["web scraping tools"]})` with `apify-client`, or `POST https://api.apify.com/v2/acts/insight.solutions~web-search-api/run-sync-get-dataset-items`.

***

### What you get

One row per organic result. A real row, from the query `apify web scraping`:

```json
{
  "rowType": "result",
  "query": "apify web scraping",
  "engine": "duckduckgo",
  "position": 1,
  "page": 1,
  "positionOnPage": 1,
  "title": "Web Scraper - Apify",
  "url": "https://apify.com/apify/web-scraper",
  "urlRaw": "//duckduckgo.com/l/?uddg=https%3A%2F%2Fapify.com%2Fapify%2Fweb%2Dscraper&rut=f5520a2f…",
  "urlDecoded": true,
  "displayUrl": "apify.com/apify/web-scraper",
  "domain": "apify.com",
  "snippet": "What is Web Scraper? Web Scraper is a tool for extracting data from any website…",
  "sitelinks": [],
  "faviconUrl": "//external-content.duckduckgo.com/ip3/apify.com.ico",
  "isAd": false,
  "alsoOn": ["bing"],
  "relevanceChecked": true,
  "relevanceRatio": 1,
  "relevanceVerdict": "ok",
  "viaResidential": false,
  "serpUrl": "https://html.duckduckgo.com/html/?q=apify+web+scraping&kl=us-en",
  "ok": true,
  "scrapedAt": "2026-09-15T20:20:43.000Z"
}
```

Plus, free, one `query-summary` row per query per engine (and one across both engines), and a
`diagnostic` row for anything that could not be used.

***

### Quick start

1. Put your searches in **Search queries**, one per line.
2. Leave **Engines** as `DuckDuckGo` + `Bing`.
3. Set **Results per query** (10 by default, up to 50).
4. Run it. The dataset opens on the **Results** view.

Defaults are chosen so the first run is cheap and honest: ten results per query per engine, the
relevance guard on, the datacenter proxy, a four-minute time budget.

***

### Use cases

- **Feed a RAG pipeline or an agent** with fresh, real URLs instead of a model's memory of the web.
- **Track a brand or a product** across two independent indexes and see where it ranks.
- **Check link coverage**: who is cited for your topic, and on which domains.
- **Discover sources** for a scraper: run a query, take the domains, scrape them with something else.
- **Compare two engines** on the same query — the rows carry `engine` and `alsoOn`, so overlap is one
  group-by away.

***

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `queries` | string\[] | `["web scraping tools"]` | Up to 200 search strings. Trimmed, de-duplicated case-insensitively |
| `engines` | multi-select | `["duckduckgo","bing"]` | Which engines, in the order they are tried |
| `maxResultsPerQuery` | integer | `10` | 1–50, **per engine** |
| `country` | string | `"US"` | Bing's market (`cc`) |
| `language` | string | `"en"` | Bing's interface language (`setlang`) |
| `region` | string | `""` | DuckDuckGo's region (`kl`), country-first. Empty derives it from `country` + `language` |
| `safeSearch` | enum | `"moderate"` | `off` / `moderate` / `strict`. Best-effort — see Limitations |
| `deduplicate` | boolean | `true` | One row per URL per query, across engines |
| `relevanceGuard` | enum | `"on"` | `on` / `flag-only` / `off` — see below |
| `ddgDeepPaging` | boolean | `false` | Advanced and unverified. Off by default |
| `residentialFallback` | boolean | `true` | Allow one residential retry of a Bing page that came back unrelated |
| `maxConcurrency` | integer | `4` | Queries in flight, 1–10 |
| `maxRunSecs` | integer | `240` | Time budget, 30–3600 |
| `proxyConfiguration` | object | `{"useApifyProxy": true}` | Apify Proxy. See Proxy below |

***

### Output reference

Every row — result, summary or diagnostic — carries the same columns, null where they do not apply, so
the dataset exports as one rectangular table.

**Result rows** (`rowType: "result"`, charged)

| Column | Meaning |
|---|---|
| `query`, `queryIndex`, `engine` | Which search this answers, and which engine answered it |
| `position`, `page`, `positionOnPage` | Rank within the query for that engine, the page it came from, and its place on that page |
| `title`, `snippet` | Text as shown, highlighting removed, entities decoded |
| `url`, `urlRaw`, `urlDecoded` | The real destination, the engine's redirect wrapper, and whether the decode worked |
| `displayUrl`, `domain`, `siteName` | The breadcrumb the engine showed, the host, and Bing's own label for the site |
| `datePublished`, `datePublishedRaw` | Only when the engine gave a date. Usually relative ("20 hours ago"), so the ISO field is often null |
| `sitelinks` | `{title, url, snippet}` deep links, when Bing shows them |
| `alsoOn` | The other engine that returned this same URL — dropped, not charged |
| `relevanceChecked`, `relevanceRatio`, `relevanceVerdict` | What the relevance guard made of the page this row came from |
| `viaResidential`, `serpUrl` | Which exit the page came from, and the exact search URL that was fetched |

**Summary rows** (`rowType: "query-summary"`, free) carry `resultsReturned`, `resultsAfterDedupe`,
`duplicatesDropped`, `pagesFetched`, `totalEstimated` and `totalEstimatedRaw` (Bing's own count line,
in whatever language it answered in), `relatedSearches`, `peopleAlsoAsk`, `fuzzyFallback`,
`pagingStoppedReason`, `challengeCount`, `decoyCount`, `residentialRetries`, `marketServed` and
`queryDurationMs`.

**Diagnostic rows** (`rowType: "diagnostic"`, free) carry `errorType` — `blocked`, `decoy`,
`unavailable`, `no-results`, `invalid-input`, `timeout`, `paging-exhausted` or `unparsed` — plus
`challenge`, `challengeReason`, `httpStatus`, `resultsSeen`, `queryTokenMatches` and `bodyBytes`.

***

### The relevance guard

Search engines do not always refuse an automated visitor. Sometimes they answer.

In our measurements, Bing answered **every** request with a full page of ten well-formed results —
and through a datacenter proxy those results frequently had nothing to do with the query, while the
page's title, search box and pagination all echoed the query faithfully. There is no marker in the
HTML that separates such a page from a real one. Only the content differs.

So this Actor reads the content:

1. Your query is split into words, accents folded, stop words and bare numbers dropped.
2. A result counts as an answer when it carries **two** of those words (or the only word, for a
   one-word query) in its title, URL, display URL or snippet.
3. If fewer than one result in five matches, the page is not an answer to your query.

When that happens, with the guard `on`: the page is retried once from a residential exit, and if it
still does not match, you get **one free diagnostic row** saying so and **no charge at all** for that
page. If you asked for both engines, DuckDuckGo still answers the question — which is why both are on
by default.

`flag-only` returns and charges those rows with `relevanceVerdict: "decoy"` on them, for semantic
queries whose answers legitimately share no word with the question. `off` does no scoring at all.

***

### What you are never charged for

- Query summaries and every diagnostic row.
- A page of results that did not match your query, and the residential retry of it.
- A page behind an anti-bot challenge.
- A duplicate URL dropped because the other engine had already returned it.
- A page that repeated results already returned, which is what ends paging.
- A query that returned nothing.
- **A run that returns no result at all**: it finishes FAILED and bills nothing, start fee included.

***

### Pricing

| Event | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| Run started (`actor-start`) | $0.001 | $0.001 | $0.001 | $0.001 |
| Search result (`result`) | $0.0006 | $0.0006 | $0.00048 | $0.00036 |

**$0.60 per 1,000 results**, down to **$0.36 per 1,000** at GOLD. A run of 10 queries × 10 results
across both engines costs about $0.12 plus the $0.001 start fee, before duplicates are dropped free.
The start fee is charged once per run and only after the first paid row.

***

### Proxy

The default is Apify's datacenter proxy, which is the right default for DuckDuckGo: it answered every
datacenter request in our measurements with results that matched the query.

Bing is the engine that needs a better address, and it gets one on its own: its first request goes out
**direct**, with no proxy at all, and a page that comes back unrelated is retried **once** through a
residential exit. Residential transfer costs forty times datacenter transfer, so it is spent
deliberately and never on a whole run.

If you need Bing specifically, and a lot of it, set the proxy to residential yourself:

```json
{ "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
```

Bing's relevance was materially better from residential exits in our measurements. That is a
measurement on one day, not a guarantee.

***

### Use it from an AI agent, or from code

The Actor is one call with a JSON input and a JSON dataset out, which is all an agent framework needs.

```bash
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~web-search-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "queries": ["vector database comparison", "rag evaluation tools"],
    "engines": ["duckduckgo", "bing"],
    "maxResultsPerQuery": 10
  }'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/web-search-api").call(run_input={
    "queries": ["vector database comparison"],
    "engines": ["duckduckgo", "bing"],
    "maxResultsPerQuery": 10,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["rowType"] == "result":
        print(row["position"], row["engine"], row["title"], row["url"])
```

```javascript
// npm i apify-client
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('insight.solutions/web-search-api').call({
    queries: ['vector database comparison'],
    maxResultsPerQuery: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const results = items.filter((row) => row.rowType === 'result');
```

Point an agent at the `result` rows and ignore the rest, or read the `query-summary` row to decide
whether the answer was complete enough to act on.

***

### FAQ

**Which engines does this read?**
Bing and DuckDuckGo. Nothing else.

**Does it search Google?**
No. Google is behind a captcha wall for automated visitors and is out of scope; nothing in this Actor
touches it, and nothing here is derived from it.

**Do I get the same results as in my browser?**
Not necessarily, and no honest search Actor can promise that. Search engines personalise by location,
history and device, and they treat an automated visitor differently from a browser. What you get is
what the engine served this request, from this exit, at this moment — and `serpUrl` on every row tells
you exactly what was asked.

**Do I get ads?**
No. Only organic result blocks are read. Ad containers are never parsed.

**Why is DuckDuckGo first?**
Because it answered the question that was asked, every time, in every capture we took — including
through a datacenter proxy. Bing never refused a request but frequently answered a different question.
DuckDuckGo carries the correctness; Bing adds reach.

**How many results per query can I get?**
Up to 50 from Bing, paged ten at a time by following the search page's own Next link, and up to 10 from
DuckDuckGo, which this version reads one page of. The free summary row tells you what you actually got
and why it stopped.

**What happens when an engine refuses?**
DuckDuckGo answers an image CAPTCHA behind HTTP 202 when it has seen too much traffic from one
address. The Actor rotates to a fresh exit once, tries DuckDuckGo's lightweight endpoint once, and then
stops and gives you a free `blocked` row. It never loops on a challenge, and it never charges for one.

**Can I get results in another language or country?**
`country` and `language` set Bing's market and interface language — verified. `region` sets
DuckDuckGo's `kl` — verified. Whether they change the *result set*, rather than the wrapper, is not
something our captures could prove. Leave them empty and both engines answer according to where the
proxy exit is, which is why a German exit returns German results.

**Is there a monitor mode?**
Not in this version. Schedule the Actor and diff the datasets, or use the `alsoOn` and `position`
columns to compare two runs.

***

### Limitations

- **Web results only.** No images, videos, maps, shopping, news or instant answers.
- **No ads**, by design.
- **People-also-ask answers are not in the page.** Bing loads them with JavaScript, so the summary row
  carries the questions and their source URLs, and `answer` is always null.
- **Related searches and sitelinks appear only when the search page includes them**, which through a
  proxy is rare.
- **DuckDuckGo carries no dates.** Bing's dates are usually relative, so `datePublished` is often null
  and `datePublishedRaw` holds the string the engine wrote.
- **DuckDuckGo deep paging is unverified** and off by default. Every attempt we captured came back
  empty, renumbered or behind a CAPTCHA.
- **Bing paging beyond page one is followed from the page's own Next link and stopped the moment a page
  repeats itself.** You are never charged for a repeated result.
- **Safe search is best-effort.** Both engines document a parameter for it; neither confirmed it in any
  capture we took. The value you asked for is recorded on the summary row.
- **The upstream HTML can change at any time.** Both engines rewrite their result pages without notice.
  When that happens you get `unparsed` diagnostic rows, free, and this Actor is updated.

***

### Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

**Video, audio & social**

- [YouTube Transcript API](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.
- [Hacker News API](https://apify.com/insight.solutions/hacker-news-api) — stories, comments, users, front page and a structured "Who is hiring?" parser from the official HN APIs.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Website Technology Detector](https://apify.com/insight.solutions/website-tech-detector) — the tech stack behind any site, with the evidence for each detection.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.
- [Company Enrichment API](https://apify.com/insight.solutions/company-enrichment-api) — a domain in, a company profile out: firmographics, contacts, tech stack, DNS and hiring signal.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [Federal Contracts, Grants & Lobbying API](https://apify.com/insight.solutions/federal-contracts-grants-api) — SAM.gov opportunities, USAspending awards, Grants.gov notices and Senate lobbying filings in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Clinical Trials & FDA API](https://apify.com/insight.solutions/clinical-trials-fda-api) — ClinicalTrials.gov studies plus openFDA recalls, labels, approvals, 510(k)s and adverse-event reports.
- [Product & Vehicle Recalls API](https://apify.com/insight.solutions/product-recalls-api) — CPSC, NHTSA, FDA and USDA recalls, vehicle complaints and ratings, plus a VIN decoder.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Remote Jobs API](https://apify.com/insight.solutions/remote-jobs-api) — RemoteOK, Remotive, We Work Remotely, Himalayas, Jobicy and more in one schema, deduplicated.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.
- [Shopify Store Monitor](https://apify.com/insight.solutions/shopify-store-monitor) — price drops, sales, restocks, sell-outs and new products on any Shopify store, one row per change.

**Apps & games**

- [App Store & Google Play Reviews API](https://apify.com/insight.solutions/app-reviews-api) — reviews from both stores with ratings, versions and developer replies.
- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [App Store Keyword Rank Tracker](https://apify.com/insight.solutions/app-store-keyword-rank-tracker) — where any app ranks for any keyword on the App Store and Google Play, with rank changes and ASO suggestions.
- [Steam Reviews API](https://apify.com/insight.solutions/steam-reviews-api) — Steam reviews with playtime, helpfulness and game details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

## `queries` (type: `array`):

The search strings to run, one per line, up to 200 per run. Trimmed, emptied entries dropped, duplicates removed case-insensitively. A URL typed in here is still just a search string — this Actor searches, it does not fetch pages.

## `engines` (type: `array`):

Which search engines to read, in the order they should be tried. DuckDuckGo is first by default: in our measurements it answered every query with results that matched it, while Bing answered proxied requests with a full page of unrelated links most of the time. When both are selected the same URL found by both is returned once, with the second engine named in `alsoOn`. Google is not available and is not scraped by this Actor.

## `maxResultsPerQuery` (type: `integer`):

How many organic results to return for each query from each engine, 1 to 50. Bing serves 10 per page and is paged by following the search page's own Next link; DuckDuckGo returns 8 to 10 and this version reads one page per query. Ask for 50 with both engines on and you get up to 50 from Bing plus up to 10 from DuckDuckGo, minus any duplicate. The free summary row tells you exactly what you got and why it stopped.

## `country` (type: `string`):

Two-letter country code for Bing's market (`cc`), for example `US`, `GB`, `DE`. Verified to set the market and the interface language; whether it changes the result set is not something our captures could prove. An unknown code is reported as a free diagnostic row and the run continues with US.

## `language` (type: `string`):

Bing's interface language (`setlang`), for example `en`, `de`, `fr`. Verified: it changes the page language, the result-count wording and the title suffix.

## `region` (type: `string`):

DuckDuckGo's region (`kl`), written country-first: `us-en`, `de-de`, `uk-en`, `wt-wt` for worldwide. Leave it empty and the country and language above are combined into one — `US` + `en` becomes `us-en`. Clear the country as well and DuckDuckGo decides by the exit IP, which is how a German exit answers an English query with German pages.

## `safeSearch` (type: `string`):

Best-effort, and not verified against a capture: `moderate` sends nothing (both engines' own default), `off` and `strict` add Bing's `adlt` and DuckDuckGo's `kp` parameter. The value you asked for is recorded on every summary row so you can check what a run was told to do.

## `deduplicate` (type: `boolean`):

When both engines return the same page for one query, return it once — from the engine you listed first — and name the other in `alsoOn`. The dropped copy is never charged. URLs are compared with the host lower-cased, `www.` and a trailing slash removed and click ids such as `msockid` and `utm_*` stripped.

## `relevanceGuard` (type: `string`):

Bing sometimes answers a proxied request with a full page of well-formed results that have nothing to do with your query. `on` detects that, retries the page once from a residential exit, and if it still does not match returns a free diagnostic row and charges you nothing for it. `flag-only` returns and charges those rows with `relevanceVerdict: "decoy"` on them, for semantic queries that legitimately share no word with their answers. `off` does no scoring at all.

## `ddgDeepPaging` (type: `boolean`):

Advanced, and unverified. DuckDuckGo's second page is requested with the values read from the search page's own Next form and nothing else. In our captures every attempt at a second page came back empty, renumbered or behind a CAPTCHA, so this is off by default; when it is on, repeated results are dropped and never charged, and paging stops at the first page that adds nothing.

## `residentialFallback` (type: `boolean`):

Allow one retry through a residential exit when Bing answers with a page of unrelated results. One page, one request, at most once per query — residential transfer costs forty times datacenter transfer, so it is used deliberately and never for a whole run.

## `maxConcurrency` (type: `integer`):

How many queries to search at once, 1 to 10. Each query gets its own proxy session, so four in flight means four exit IPs each making at most one DuckDuckGo request every two seconds.

## `maxRunSecs` (type: `integer`):

Stop the run after this many seconds, 30 to 3600. Results already returned are kept, and the queries that were not reached get a free `timeout` diagnostic row.

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. The datacenter default is right for DuckDuckGo, which answered every datacenter request in our measurements with results that matched the query. Bing was materially more reliable from residential exits: if you need Bing specifically, set `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US"}`.

## Actor input object example

```json
{
  "queries": [
    "web scraping tools"
  ],
  "engines": [
    "duckduckgo",
    "bing"
  ],
  "maxResultsPerQuery": 10,
  "country": "US",
  "language": "en",
  "region": "",
  "safeSearch": "moderate",
  "deduplicate": true,
  "relevanceGuard": "on",
  "ddgDeepPaging": false,
  "residentialFallback": true,
  "maxConcurrency": 4,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per organic result, deduplicated across engines, with free per-query summaries and free diagnostics. Delivered as JSON items in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "web scraping tools"
    ],
    "engines": [
        "duckduckgo",
        "bing"
    ],
    "maxResultsPerQuery": 10,
    "country": "US",
    "language": "en",
    "region": "",
    "safeSearch": "moderate",
    "deduplicate": true,
    "relevanceGuard": "on",
    "ddgDeepPaging": false,
    "residentialFallback": true,
    "maxConcurrency": 4,
    "maxRunSecs": 240,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/web-search-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["web scraping tools"],
    "engines": [
        "duckduckgo",
        "bing",
    ],
    "maxResultsPerQuery": 10,
    "country": "US",
    "language": "en",
    "region": "",
    "safeSearch": "moderate",
    "deduplicate": True,
    "relevanceGuard": "on",
    "ddgDeepPaging": False,
    "residentialFallback": True,
    "maxConcurrency": 4,
    "maxRunSecs": 240,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/web-search-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "web scraping tools"
  ],
  "engines": [
    "duckduckgo",
    "bing"
  ],
  "maxResultsPerQuery": 10,
  "country": "US",
  "language": "en",
  "region": "",
  "safeSearch": "moderate",
  "deduplicate": true,
  "relevanceGuard": "on",
  "ddgDeepPaging": false,
  "residentialFallback": true,
  "maxConcurrency": 4,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call insight.solutions/web-search-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/web-search-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/aSZ9muVvAuG6bKkUp/builds/esPdVU3RZahalnCyu/openapi.json
