Skip to content
Conduit

mcp server

proxlane

One endpoint in front of four scraping APIs. Automatic failover, honest block detection, per-request cost. BYOK, self-hosted, AGPL.

proxlane/proxlanecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • Node.js 20 or newer
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "proxlane": {
      "command": "npx",
      "args": [
        "-y",
        "@proxlane/sdk"
      ],
      "env": {
        "PROXLANE_API_KEY": "<your PROXLANE_API_KEY>"
      }
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 26, 2026

Proxlane

Self-host only, and pre-1.0. There is no hosted endpoint and no account: you run it, on your own provider keys, from a published image or an npm package. Everything the Status section lists as shipped is covered by a command you can run — and what is missing is missing by decision rather than by breakage: no dashboard, no database, no hosted credits. Docs are live at proxlane.dev/docs. Follow the repo if you want to know when that changes.

One lane to every scraping provider. Automatic failover, per-request cost visibility, and honest success detection.

Bring your own provider keys and it is free forever. Self-host it and it is free forever. Those are the two ways to run it. Hosted credits at provider cost plus 5% are designed and not built, and the rate is not settled — see Pricing.

- https://api.scraperapi.com/?api_key=KEY&url=https://example.com
+ http://localhost:8787/v1?api_key=KEY&url=https://example.com

That is the migration. Same parameter names, same response, one hostname — and today that hostname is your own, because the only way to run Proxlane is to run it yourself.


Why

No single scraping provider works on every target. Teams end up with two or three accounts and a pile of glue code that switches between them, retries the failures, and guesses which one is cheapest for a given domain.

Proxlane is that glue code, extracted and made honest.

Failover. When a provider starts returning blocks at 2am, the request retries through the next one. Providers will never fail over to their competitors. We will.

Real success detection. Providers return HTTP 200 with a CAPTCHA page in the body. ScraperAPI documents this and asks users to report it. Proxlane runs every response through a block detector before calling it a success, and tells you which rule fired.

Cost visibility across providers. Your provider's dashboard shows you their numbers. Proxlane shows you that a domain succeeds 98% of the time on provider A at a third of what provider B charges for the same page.

No lock-in. Switching providers is a config change, not a code change.

The reliability math

Suppose a hard target blocks 6% of requests on any given provider. One provider gives you 94%. Three independent providers in a failover chain give you 99.98% on the same target.

That independence assumption is the load-bearing part, and it is not true. Blocks are caused by the target's anti-bot, which is a common cause: a page that fingerprints one provider's traffic tends to fingerprint the next one's too. Real combined success will land below the independent-product bound, by an amount nobody has measured yet.

So take the arithmetic as illustrative of the shape, not as a claim. The number we will publish is the measured one, per domain, on /targets — which is the only version worth publishing anyway.


Quickstart

Two ways in. Both give you a gateway you control; neither gives you an account here, because there isn't one.

On someone else's machine

Deploy to Render Deploy to DigitalOcean

CostAwake
Renderfreespins down after 15 minutes idle, ~1 min to come back
DigitalOcean~$5/moalways

Free is the right default. Pick the paid one if a cold start on the first request of a batch would hurt — which for a scraper it often does.

Both read a blueprint committed here, not a template on someone's dashboard, so you can see exactly what they deploy before you click: render.yaml and .do/deploy.template.yaml. A pinned image, a health check, an in-flight ceiling sized to the instance, and your provider keys. Fill in the providers you pay for and leave the rest blank.

On Render PROXLANE_API_KEY is generated for you. App Platform has no equivalent, so that one asks you to paste a key — any random string, openssl rand -hex 16.

The image tag in both files is written by the release, not by hand, so a one-click deploy cannot quietly hand you a gateway five versions old.

On yours

Three commands, and the first one invents a key. There is no hosted endpoint to curl — the localhost below is not standing in for one, it is where Proxlane runs.

PROXLANE_API_KEY is the key you will present to your own gateway. Generate one, as below; nothing registers it anywhere. The gateway refuses to boot without it, because a gateway started open is a proxy funded by whoever deployed it, and refuses a placeholder such as changeme or any key that has appeared in a published example, for the same reason.

export PROXLANE_API_KEY=$(openssl rand -hex 16)
export SCRAPERAPI_KEY=…            # or SCRAPINGBEE_KEY / SCRAPFLY_KEY / BRIGHTDATA_KEY
docker run -p 8787:8787 \
  -e PROXLANE_API_KEY \
  -e SCRAPERAPI_KEY \
  ghcr.io/proxlane/gateway
curl "http://localhost:8787/v1?api_key=$PROXLANE_API_KEY&url=https://example.com"

One provider key is enough to start. Add a second and failover has somewhere to go.

JavaScript rendering and geotargeting:

curl "http://localhost:8787/v1?api_key=$PROXLANE_API_KEY\
&url=https://example.com&render=true&country_code=de"

Every response tells you what happened:

X-Outcome: OK
X-Outcome-Class: ok
X-Attempts: 1
X-Chain: scrapingbee:OK
X-Provider-Used: scrapingbee
X-Cost-Estimate: 1.000000
X-Cost-Unit: provider-credits
X-Cost-Source: reported

Node:

const res = await fetch(
  `http://localhost:8787/v1?api_key=${key}&url=${encodeURIComponent(target)}`
);
const html = await res.text();

Full parameter reference: proxlane.dev/docs/api. Every outcome, and what to do about each: proxlane.dev/docs/outcomes.

Scrapy:

pip install scrapy-proxlane

One downloader middleware, and each response carries the gateway's verdict in response.meta["proxlane"]. Settings and per-request options: proxlane.dev/docs/scrapy. Source: proxlane/scrapy-proxlane.

Self-hosting

git clone https://github.com/proxlane/proxlane
cd proxlane
cp .env.example .env
openssl rand -hex 32   # paste as PROXLANE_API_KEY in .env, then add your provider keys
docker compose -f docker/compose.yml --env-file .env up -d

--env-file is not decoration: Compose takes its project directory from the compose file's location, so without it your root .env is never read and the gateway refuses to boot. Full guide, including droplets and what Vercel's container support does and does not cover: docs/self-hosting.md.

One gateway on :8787. No dashboard, no Postgres, no worker — none of them exist yet, and shipping empty services would be furniture rather than a deployment. Valkey ships commented out: it is what lets you run more than one gateway, and a single one does not need it.

Your keys, your infrastructure, your scraped data. Nothing phones home — there is no telemetry in the gateway or the CLI, and the only hosts either one contacts are the provider you configured and the URL you asked for.

Providers

ProviderStatusJS renderGeoSessionsPOSTrender cost
ScraperAPIshippedyesallyesyes10×
ScrapingBeeshippedyes42 regions—yes5×
Scrapflyshippedyesall—yes6×
Bright Data Web Unlockershippedyesall—yes1×
Firecrawlshippedyes26 regions——1×
Zyteplanned
Oxylabs Web Scraper APIplanned
ScrapingAntplanned

Want one that is not here? Open an issue, or write the adapter yourself. See below.

Generated from the capability registry by scripts/readme-providers.ts, and asserted byte-identical by pnpm repo:check, so it cannot drift from what the router does. Every shipped row is a provider with a recorded fixture set and a place in the failover chain.

render cost is the multiplier on a rendered request, which is the number worth comparing: the same page costs 10× on one line and 1× on another.

Pricing

Two ways to run it today, both free. A third is designed and not built.

CostRequests run on
BYOKfree, foreveryour provider accountsships today
Self-hostfree, foreveryour provider accounts, your serversships today
Hosted creditsprovider cost + 5%our provider accountsnot built

Hosted credits are pay as you go, no subscription, and you are only charged for requests that pass block detection. A 200 with a CAPTCHA in it is not a success and you do not pay for it.

The 5% is not settled. Those two promises are in tension: the provider still bills us for the CAPTCHA-200 our detector rejects, which makes 5% negative-margin, and every improvement to detection makes it worse. Whether hosted credits ship at a higher rate, with a cap on absorbed blocks, or not at all is an open decision — see docs/state.md. BYOK and self-host are unaffected, and they are the launch modes.

How it works

your scraper
  -> Proxlane gateway
       auth, translate, route
  -> provider A         blocked, cooled down for this domain
  -> provider B         200, passes detection
  <- HTML + outcome headers

Adapters are pure functions. translate() turns a Proxlane request into a provider request, parse() turns a provider response into a typed outcome. All network I/O goes through one shared transport, which is why the test suite runs against recorded real provider traffic instead of hand-written mocks.

Every provider response is validated against a schema. When a provider changes their API, the parse fails loudly with a PROVIDER_DRIFT outcome instead of quietly returning garbage. A scheduled canary runs every adapter against the live APIs and opens an issue when something moves.

Architecture details: docs/integrations.md

Writing an adapter

pnpm new-adapter myprovider   # scaffolds capabilities, translate/parse, schema, fixtures
pnpm record --adapter=myprovider   # captures real responses with your trial key
pnpm conformance --adapter=myprovider

Green conformance means the adapter is correct: every parameter translates without leaking provider defaults, every response category maps to the right outcome, and declared capabilities match what the live API actually does.

That is the whole contribution bar. See CONTRIBUTING.md.

Status

Works today, and every line of it is covered by a command you can run:

  • Five adapters, ScraperAPI, ScrapingBee, Scrapfly, Bright Data and Firecrawl, each recorded against its live API, with pnpm conformance asserting purity, capability honesty and the outcome mapping. The table above is generated from the registry, so it cannot lag behind this line.
  • The failover chain: capability filtering, per-hop budgets that reserve time for the hops behind them, and retry semantics read from one central table rather than decided per adapter.
  • The edge guard, refusing private ranges and cloud metadata by every spelling the URL parser accepts. pnpm test:ssrf.
  • GET /v1?api_key=…&url=…, deliberately ScraperAPI-shaped so migration is a hostname change.
  • A CLI — npx proxlane scrape|providers|outcomes|doctor, --json on all of it.

Exists now: block detection, so SOFT_BLOCK is produced; cooldowns, on by default, with Retry-After when every provider is cooling; provider health, off by default because its calibration assumes failures that do not clump the way real ones do — PROXLANE_HEALTH=on, and read GET /health/providers.

Does not exist yet: any database or dashboard; hosted credits.

The gateway writes one NDJSON line per request to stdout, on by default. It carries the target's host, never the URL — a scrape URL's query string can hold session tokens, signed URLs and the gateway's own api_key, and logs get pasted into issues. PROXLANE_LOG=off silences it; PROXLANE_LOG_URLS=on opts into full URLs.

pnpm repo:check reports which of the 27 commands are real. It is asserted against the filesystem, so it cannot drift the way a status section can — and it caught this one lying twice.

Several commercial decisions are deliberately open and recorded in docs/state.md rather than assumed — including whether hosted credits ship at all, since at cost + 5% they are negative-margin against our own block-detection promise.

License

Split on purpose, and the split is the point.

Apache-2.0 — adapters, detect, sdk, shared. Write an adapter or build on the SDK without inheriting copyleft. Adapters are the thing we most want written by strangers, and a copyleft adapter layer would be a tax on exactly that.

AGPL-3.0-only — the gateway, the web app, api, db, ui, route-viz and the CLI. Use it, self-host it, modify it, run it for your company. If you offer it to others as a hosted service, your changes stay open.

pnpm repo:check enforces the direction: an Apache package may never depend on an AGPL one, because that dependency is what relicenses.

↑↓ navigate openesc close