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

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

Pricing

from $0.36 / 1,000 search result returneds

Go to Apify Store
Web Search Results API — Bing & DuckDuckGo SERP, No Key

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

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.

Pricing

from $0.36 / 1,000 search result returneds

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

a day ago

Last modified

Share

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:

{ "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 codeclient.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:

{
"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

FieldTypeDefaultWhat it does
queriesstring[]["web scraping tools"]Up to 200 search strings. Trimmed, de-duplicated case-insensitively
enginesmulti-select["duckduckgo","bing"]Which engines, in the order they are tried
maxResultsPerQueryinteger101–50, per engine
countrystring"US"Bing's market (cc)
languagestring"en"Bing's interface language (setlang)
regionstring""DuckDuckGo's region (kl), country-first. Empty derives it from country + language
safeSearchenum"moderate"off / moderate / strict. Best-effort — see Limitations
deduplicatebooleantrueOne row per URL per query, across engines
relevanceGuardenum"on"on / flag-only / off — see below
ddgDeepPagingbooleanfalseAdvanced and unverified. Off by default
residentialFallbackbooleantrueAllow one residential retry of a Bing page that came back unrelated
maxConcurrencyinteger4Queries in flight, 1–10
maxRunSecsinteger240Time budget, 30–3600
proxyConfigurationobject{"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)

ColumnMeaning
query, queryIndex, engineWhich search this answers, and which engine answered it
position, page, positionOnPageRank within the query for that engine, the page it came from, and its place on that page
title, snippetText as shown, highlighting removed, entities decoded
url, urlRaw, urlDecodedThe real destination, the engine's redirect wrapper, and whether the decode worked
displayUrl, domain, siteNameThe breadcrumb the engine showed, the host, and Bing's own label for the site
datePublished, datePublishedRawOnly 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
alsoOnThe other engine that returned this same URL — dropped, not charged
relevanceChecked, relevanceRatio, relevanceVerdictWhat the relevance guard made of the page this row came from
viaResidential, serpUrlWhich 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 errorTypeblocked, 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

EventFREEBRONZESILVERGOLD
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:

{ "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.

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
}'
# 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"])
// 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

News, documents & the web

Business, finance & jobs

Apps & games