Your request, rerouted.
One endpoint in front of every scraping API. When a provider gets blocked, the next one runs, and you are told which, and why.
No provider works on every target. Teams end up with two or three accounts and a pile of glue code that switches between them. Providers degrade for hours before they fail outright, and they will never fail over to a competitor.
And a blocked request looks like a successful one. A captcha page arrives as HTTP 200 with a body. Anything checking status codes records a success and stores the challenge page.
Tried scraperapi, SOFT_BLOCK. then scrapingbee, PROVIDER_ERROR. then scrapfly, OK. Final outcome OK.
Runs on your account, not ours. Each deploys a blueprint you can read first.
quickstart
No signup, nothing to paste
One hostname, and the chain is running. The keys already in your environment are found without configuration. This is the gateway: four providers, failover, and the detector, in the first command you type.
$ npx proxlane doctor ok key:scraperapi $SCRAPERAPI_KEY set (32 chars) ok key:scrapingbee $SCRAPINGBEE_KEY set (80 chars) ok cooldowns on. A provider that just refused a domain is skipped $ docker run -p 8787:8787 --env-file .env ghcr.io/proxlane/gateway proxlane gateway 0.7.0 on :8787 providers: scraperapi > scrapfly > scrapingbee > brightdata (in order) retries: 1 extra at the last provider (PROXLANE_TERMINAL_RETRIES) $ curl -sD- "localhost:8787/v1?api_key=$KEY&url=https://example.com" HTTP/1.1 200 OK x-outcome OK x-outcome-class ok x-attempts 2 x-chain scraperapi:SOFT_BLOCK>scrapfly:OK x-provider-used scrapfly x-cost-estimate 2.000000 x-cost-unit provider-credits
detection
A 200 is not a success
Detection is table stakes. Showing you the rule that fired is not. Three behaviours instead of the adjective.
- The detector reads the body.
- A block page returns 200 with a body. The detector calls it SOFT_BLOCK and attaches the rule that fired, so you can see why rather than trust that we looked.
- The taxonomy can grow without breaking you.
- 19 outcomes, 6 classes. Branch on the class, which never grows; read the outcome for detail. Adding an outcome cannot break your switch.
- Failed attempts are still billed, and still reported.
- Every attempt is priced, including the ones that failed. A failover that burned two charged hops reports two, not one.
migration
Change one hostname
Same parameters, same status codes. Your code keeps branching on the 404 it already branches on.
curl "https://api.scraperapi.com?api_key=KEY&url=..."
curl "http://localhost:8787/v1?api_key=KEY&url=..."response
Every answer says how it got there
Branch on the class, read the outcome for detail. The class is closed and will not grow, so adding an outcome cannot break your switch. Headers follow the chain selected above.
- x-outcome
- OK
- x-outcome-class
- ok
- x-attempts
- 3
- x-chain
- scraperapi:SOFT_BLOCK>scrapingbee:PROVIDER_ERROR>scrapfly:OK
- x-provider-used
- scrapfly
- x-cost-estimate
- 3.000000
- x-cost-unit
- provider-credits
- server-timing
- gw;dur=2, up;dur=3870, total;dur=3872
pricing
Free on your own keys
You pay your providers directly. We are not in the payment path. The gateway is AGPL and runs on your own machine. There is no account to create and nothing to enter a card into.
- Bring your own keysFree foreverYour provider keys, your provider bill. Proxlane adds nothing to it.
- Self-hostFree foreverAGPL-3.0. One container, your infrastructure, no telemetry.
- Hosted creditsProvider cost + 5%Not available yet. One bill instead of three, when it lands.
lines
Four providers, four lines
They are not interchangeable, which is the point. Printed by proxlane providers, straight from the capability registry. One does sessions, one reaches 42 countries, and rendering JavaScript is free on one and 10x the price on another. A line colour is assigned here too, and every surface reuses it.
| provider | geo | sessions | render-js |
|---|---|---|---|
| scraperapi | all | yes | ×10 |
| scrapingbee | 42 codes | no | ×5 |
| scrapfly | all | no | ×6 |
| brightdata | all | no | ×1 |
agents
Every command speaks JSON
Built to be driven by a script. A --json flag on every command, a stable envelope, and exit codes that separate a bad answer from a bad call. The repo ships a use-proxlane skill, so an agent can read the contract instead of guessing it.
$ proxlane outcomes SOFT_BLOCK --json { "ok": true, "command": "outcomes", "data": [ { "outcome": "SOFT_BLOCK", "class": "blocked", "httpStatus": 502, "chargeable": false, "failover": true, "cooldown": "blk", "pages": false, "meaning": "200 but our detector fired; a rule ID is attached" } ] }
- 0
- good, proceed
- 1
- the command worked, the answer is bad. Read the outcome. Not a crash.
- 2
- you called it wrong. Retrying the same call will not help.
- 3
- the environment is wrong, no key. Stop and fix setup.