The pages a scraping API can’t reach

Sites that demand a session, a consent click or a solved challenge need a browser you can drive — and a residential exit instead of a datacenter one.

What actually stops the request

A logged-in page fails for one of four reasons, and each has its own answer. None of them is a flag you flip and hope — every one is a surface you call.

Wall 01

It wants a session

A form, a redirect, a cookie set three hops later. Open a CDP connection and the login is ordinary Playwright code — fill, click, wait for the URL. The browser context keeps its cookies for the rest of the run.

wss://browser.wayfern.com/ws

Wall 02

It wants a consent click

Consent overlays swallow clicks and scroll-lock the document. On screenshot and PDF captures Wayfern clicks the accept control it recognises, hides whatever is left with CSS and unlocks scrolling — on by default.

capture.blockCookieBanners: true

Wall 03

It wants a challenge solved

reCAPTCHA, hCaptcha, FunCaptcha, GeeTest, Turnstile or a plain image. Post the sitekey to the captcha endpoint, get the provider’s solution back, and write it into the field the form posts.

POST /api/captcha/solve

Wall 04

It has judged your IP

A datacenter exit a site has already made up its mind about. In auto mode a 401, 402, 403 or 407 — or a redirect onto a known challenge path — is retried once through a residential exit. Set residential to start there.

proxy: 'auto' | 'residential'

It is just Playwright, pointed somewhere else

Connect over CDP with a link token and you get a full browser you control: multi-step logins, second factors typed from your own secret store, consent clicks, pagination behind the wall. A session is created when the connection opens — there is no endpoint to call first, and nothing to install beyond the driver you already use.

login-with-turnstile.ts
import { chromium } from "playwright";

// One CDP connection is one browser session. Exit selectors ride on the query
// string: "country" turns on a residential exit, "session" pins it for the run.
const browser = await chromium.connectOverCDP(
  `wss://browser.wayfern.com/ws?token=${LINK_TOKEN}&country=us&session=run4711`,
);
const context = browser.contexts()[0];
const page = await context.newPage();

await page.goto("https://app.example.com/login");
await page.fill("#email", process.env.TARGET_EMAIL);
await page.fill("#password", process.env.TARGET_PASSWORD);

// A Turnstile widget guarding the form: solve it, then write the token into
// the field the form actually posts.
const widget = page.locator(".cf-turnstile[data-sitekey]").first();
if ((await widget.count()) > 0) {
  const websiteKey = await widget.getAttribute("data-sitekey");

  const solved = await fetch("https://api.wayfern.com/api/captcha/solve", {
    method: "POST",
    headers: {
      // An API token (wf_...) carrying the "captcha" scope.
      Authorization: `Bearer ${WAYFERN_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      task: {
        type: "TurnstileTaskProxyless",
        websiteURL: page.url(),
        websiteKey,
      },
    }),
  });

  // { "solution": { ... } } — the fields depend on the task type.
  const { solution } = await solved.json();
  await page.evaluate((token) => {
    const field = document.querySelector('input[name="cf-turnstile-response"]');
    if (field instanceof HTMLInputElement) field.value = token;
  }, solution.token);
}

await page.click("button[type=submit]");
await page.waitForURL("**/dashboard");

// Logged in. Every later navigation reuses this context — same cookies, same
// sticky exit — until you close the connection.
await page.goto("https://app.example.com/reports/q1");
console.log(await page.title());
await browser.close();

Both endpoints in that script are real: the CDP gateway at wss://browser.wayfern.com/ws and POST /api/captcha/solve on https://api.wayfern.com. The captcha response is { "solution": { … } }, whose fields depend on the task type — see the API reference.

One exit, from login to last page

A cookie issued to one IP and replayed from another is the cheapest signal a site has. If the run is logged in, the exit has to hold still.

On a browser session — sticky

Add country and a session id (letters and digits) to the gateway URL and that id is encoded into the upstream credentials with a 24-hour TTL, so a multi-step flow keeps one exit IP for as long as the session lasts. Narrow it further with region, city or isp.

?token=…&country=us&session=run4711

On the Web Data API — rotating

A one-shot call resolves a residential exit without a session id, so the IP rotates per connection. That is the right default for independent page fetches and the wrong tool for a multi-step logged-in flow: for those, either drive a session, or send the credential yourself on every call with a per-request header.

proxy: 'residential', country: 'us'

8 task types, one endpoint

POST /api/captcha/solve takes an anti-captcha task object and returns the solution. Submit it from inside a session, or from anywhere else you happen to be holding a sitekey.

RecaptchaV2TaskProxyless
reCAPTCHA v2, checkbox or invisible
websiteURL, websiteKey, isInvisible
RecaptchaV2EnterpriseTaskProxyless
reCAPTCHA v2 Enterprise
websiteURL, websiteKey, enterprisePayload
RecaptchaV3TaskProxyless
reCAPTCHA v3, score-based
websiteURL, websiteKey, minScore, pageAction
HCaptchaTaskProxyless
hCaptcha, including invisible
websiteURL, websiteKey, isInvisible
FunCaptchaTaskProxyless
FunCaptcha / Arkose Labs
websiteURL, websitePublicKey, data
GeeTestTaskProxyless
GeeTest v3 and v4
websiteURL, gt, challenge, version
TurnstileTaskProxyless
Cloudflare Turnstile
websiteURL, websiteKey, action
ImageToTextTask
Plain image or text captcha
body (base64), phrase, numeric, math

Rules Only proxyless task variants are accepted — proxy fields and softId are rejected, and websiteURL must be an absolute http(s) URL. A solve is debited on success only: if no solution comes back, nothing is charged.

Not every gate needs a session

If you already hold the credential — a session cookie, a bearer token, an API key the page accepts — the Web Data API will carry it for you. A scrape is 1 credit per page at that point, with no session to babysit.

scrape-with-cookie.sh
curl -X POST https://api.wayfern.com/api/v1/scrape \
  -H "Authorization: Bearer $WAYFERN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://app.example.com/reports/q1",
    "proxy": "residential",
    "country": "us",
    "headers": { "Cookie": "sid=<YOUR_SESSION_COOKIE>" },
    "formats": ["markdown"]
  }'

Your headers, per request

Up to 20 custom headers per call, an authorization cookie among them. Wayfern rejects the four it must own itself — Host, Content-Length, Connection and Transfer-Encoding — rather than dropping them silently and leaving you to debug the wrong page.

Escalate only when told to

proxy: 'auto', the default, tries a datacenter exit and retries once through a residential one when the response reads as a block. Other failures surface immediately — retrying a timeout on a slower path just spends the budget twice.

Three meters, one balance

An authenticated run spends on three separate meters, all drawn from the same credits. Plans start at $19 a month with 1,520 credits included, and non-expiring packs top up from there.

Session time

A browser session meters the seconds each page is open, pro-rated from 8 credits per open-page hour, with a 60-second floor on every page opened. Close the connection and the meter stops.

Captcha solves

A solve is debited once the provider returns a solution. A solve that errors, or that never comes back before the timeout, debits nothing — you are billed for answers, not attempts.

Residential bytes

Datacenter egress is not metered. Residential bytes are, per byte, at 320 credits per GB — so in auto mode you only pay for the traffic that a bot wall actually forced onto a residential exit.

Every per-unit rate is published on the pricing page, and each /api/v1 response hands back the exact charge in its credits field.

Ready to scale?

Log in once, hold the session, and hand the challenges to an endpoint. The same balance covers the browsers, the solves and the bytes.