One request returns ranked results from Google, Bing and DuckDuckGo in a stable envelope, with absolute ranks and ad flags — then feed the URLs straight into scrape or extract.
POST or GET /api/search/v1, with an
API token carrying the search scope. Providers you name run concurrently, a URL returned by more than one of them appears
once, and every result carries the engine that produced it.
curl -X POST https://api.wayfern.com/api/search/v1 \
-H "Authorization: Bearer wf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"text": "anti detect browser",
"engines": ["google", "bing", "duckduckgo"],
"site": "github.com",
"limit": 50
}'{
"query": {
"text": "anti detect browser",
"engines_requested": ["google", "bing", "duckduckgo"]
},
"meta": {
"engines_failed": [
{ "engine": "duckduckgo", "reason": "captcha_detected" }
],
"version": "2.1"
},
"results": [
{
"rank": 1,
"absolute_rank": 1,
"type": "ad",
"ad": true,
"url": "https://advertiser.example.com/browser",
"title": "Sponsored — Browser Automation",
"description": "Ad copy as the engine rendered it.",
"engine": "google"
},
{
"rank": 1,
"absolute_rank": 2,
"type": "organic",
"ad": false,
"url": "https://github.com/example/anti-detect-browser",
"title": "example/anti-detect-browser",
"description": "Fingerprint-aware Chromium automation.",
"engine": "google"
}
],
"serp_features": [
{
"type": "people_also_search",
"engine": "google",
"title": "People also search for",
"items": [
{
"query": "browser fingerprint spoofing",
"url": "https://www.google.com/search?q=browser+fingerprint+spoofing"
}
]
}
],
"pagination": { "page": 1, "has_more": true, "next_start": 50 }
}The position among organic results. Ads are counted on a separate counter, so a sponsored row can never push an organic result down a place — the number means what an SEO report needs it to mean.
The position among every row, ads included, in the order the engine rendered them. Pair it
with ad and type to reconstruct the page
as a human saw it.
Per-engine failures are reported, not swallowed. If one provider is blocked while the
others answer, you get the results plus a named failure — and meta also carries request_id, requested_at and took_ms.
Site, filetype, date range, language and region are request fields, validated before the call runs. A malformed date comes back as a 400 instead of a silently unfiltered result set, and each engine's own URL syntax is built for you.
Ask for up to 200 results per engine and page with a plain offset. The envelope hands back the next offset and whether another page exists, so the loop condition is a field rather than a heuristic.
Search returns ranked results — titles, descriptions and URLs. It does not open those pages,
and Wayfern will not quietly scrape them on your behalf or bill you for pages you did not
ask for. When you want the content, make a second call: POST /api/v1/scrape for one
page as markdown, or POST /api/v1/extract to hand a
list of URLs plus a prompt or a JSON Schema and get structured data back. Both live on the
Web Data API, so the token making the second call needs the web scope alongside search.
curl -X POST https://api.wayfern.com/api/v1/extract \
-H "Authorization: Bearer wf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://github.com/example/anti-detect-browser",
"https://github.com/example/fingerprint-suite"
],
"prompt": "project name, stated licence, and the primary language",
"maxPages": 25
}'Filter the SERP however you like — drop the ads, keep one domain, take the top ten — and send only that list on. Nothing is fetched that you did not name.
maxPages caps how many
source pages extract will fetch — default 10, maximum 100 — so a long result list cannot
turn into an unbounded charge.
Extract charges 1 credit per uncached source page, and every source comes back with its status and whether it was billed. Re-running the same extraction over cached pages is what makes iterating on a prompt cheap.
Engines push back — captchas, soft blocks, rate limits. Wayfern names what happened instead
of returning zero results and calling it a day. If some providers answered, you get their
results with the failures listed in meta.engines_failed. If every
requested engine failed and nothing usable came back, the sentinel becomes the HTTP status.
Envelope Errors render as a flat { error, code, request_id, message, reason } body, where reason names the
engine that failed, and the same id is repeated in the X-Request-ID header. Running
out of credits is its own case: a 402 billing_blocked, before
any browser opens.
Search draws on the same credit balance as browser sessions and the rest of the Web Data API — 2 credits for each provider result page actually fetched. Plans start at $19 a month, and non-expiring credit packs are the small way in.
A request costs 2 credits for every provider result page it fetches. Ask one engine for 10 results and that is one page; ask three engines and each one bills its own pages.
Page sizes differ by engine: Google returns up to 100 results a page, while Bing and DuckDuckGo page in tens. A limit of 50 is therefore one page on Google and several on the other two. Pages served from the SERP cache are counted the same way.
GET /api/search/engines costs nothing, so a client can discover the provider list without spending. Over MCP, list_search_engines is likewise free.
The engine list is served from the same registry the search path resolves against, so a provider cannot appear here without being callable.
curl https://api.wayfern.com/api/search/engines \
-H "Authorization: Bearer wf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# {"engines":[{"id":"google","label":"Google"},
# {"id":"bing","label":"Bing"},
# {"id":"duckduckgo","label":"DuckDuckGo"}]}An agent connected to the Wayfern MCP server gets search_web, which runs the
same search code path as the HTTP route: the same three engines, the same 1–200 limit and
start offset, and the same 2-credit rate per provider page.
The inputs are renamed for agents — query, language, filter_duplicates and include_related — and query is required, where the
HTTP route also accepts a site- or filetype-only search. Related queries come back in serp_features when include_related is on.
Mint a token with the search scope, send one request, and read ranked results from three engines out of a single envelope.