How do I cap ScrapingBee's credit cost and send the rest to a backup?
Turn on ScrapingBee's Auto-Mode with a max_cost. It tries its cheaper configurations first and charges nothing if none works within your cap, so the pages that would need its 25- or 75-credit tiers can go to UpRock at a flat $0.50 per 1,000. Watch the country as well, because ScrapingBee's cheaper tiers cover 42 countries and quietly use the US for any other.
Updated · Read as markdown
What ScrapingBee does well
ScrapingBee is a scraping API with a lot of control per request. It renders JavaScript, runs scripted steps on the page (click, scroll, wait, run your own JavaScript), takes screenshots, extracts data with CSS selectors or plain-language AI rules, and returns HTML, text or markdown. It has dedicated APIs for Google, Amazon, Walmart and YouTube, a hosted MCP server, and integrations with n8n, Make, Zapier and LangChain. Its premium proxies cover more than 200 countries, and every plan, starting at $19 a month, includes geotargeting and premium proxies.
If you need scripted steps or structured extraction in one call, ScrapingBee does a lot for one price. Keep it.
What a page costs in credits
ScrapingBee bills in credits, and how many a page uses depends on the options. JavaScript rendering is on by default. Here's ScrapingBee's credit table, and what 1,000 pages cost on three of its plans (prices checked 21 Oct 2026; Hobby is $19 for 75,000 credits, Business + is $599 for 8 million, Enterprise 120 is $5,799 for 120 million):
| Configuration | Credits a page | Hobby | Business + | Enterprise 120 |
|---|---|---|---|---|
| Classic proxy, no JavaScript | 1 | $0.25 | $0.07 | $0.05 |
| Classic proxy with JavaScript (the default) | 5 | $1.27 | $0.37 | $0.24 |
| Premium proxy, no JavaScript | 10 | $2.53 | $0.75 | $0.48 |
| Premium proxy with JavaScript | 25 | $6.33 | $1.87 | $1.21 |
| Stealth proxy (JavaScript only) | 75 | $19.00 | $5.62 | $3.62 |
| UpRock, any page, any of 63 countries | $0.50 | $0.50 | $0.50 |
AI extraction adds 5 credits a page, and unused credits don't roll over to the next month. Compare each row with UpRock's flat price: a classic page without JavaScript costs less on every plan in the table, and with JavaScript on the bigger ones, while the 25- and 75-credit tiers cost more on every plan.
That's where Auto-Mode comes in. With mode=auto, ScrapingBee tries the configurations from cheapest to most expensive, stops at the first that works and charges only for that one, or 0 credits if none worked. max_cost stops it from climbing past a tier you choose. Pages that would need more go to UpRock.
The country trap
country_code works on both of ScrapingBee's proxy pools, but they cover different lists. Classic proxies, used by the 1- and 5-credit tiers, cover 42 countries, and for any other country ScrapingBee's docs say the request quietly uses the US. Premium proxies cover more than 200 countries but start at 10 credits.
So with a low max_cost, a request for Turkey, Vietnam, the Philippines, South Africa or the UAE (all outside the classic list, all on UpRock's) would come back from the US. The code below sends those straight to UpRock.
How ScrapingBee reports a failure
| What ScrapingBee returns | What it means | Billed? | Send it to UpRock? |
|---|---|---|---|
| 200 | It worked (any 2xx from the site becomes 200) | Yes | No |
| 500 | It couldn't get the page: a block, a paywall or a site error all arrive as 500. In Auto-Mode: no configuration within max_cost worked | No | Yes |
| 429 | Too many requests at once for your plan (25 on Hobby, up to 900) | No | Yes, or wait |
| 401 | Out of credits or a bad API key | No | Yes, or top up |
| 404, 410 | The site says the page is gone | Yes | No |
The Spb-cost header (and Spb-auto-cost in Auto-Mode) says how many credits a request used.
ScrapingBee and UpRock side by side
| ScrapingBee | UpRock | |
|---|---|---|
| Price per 1,000 pages | $0.05 to $19 depending on plan and options (table above) | $0.50 by card or USDC, $0.40 with UPT |
| Failed pages | Not billed (500); 404 and 410 are | Not documented yet |
| Countries | 42 on classic proxies, 200+ on premium | 63, or a region, at one price |
| Where requests come from | Classic proxies; premium proxies (residential, per its docs); stealth pool | Devices whose owners opted in |
| Scripted steps, screenshots, extraction | Yes | No |
| Markdown output | Yes (return_page_markdown) | Yes |
| MCP server | Hosted, same credit rates | Yes, remote (streamable HTTP) |
| Video | YouTube search, metadata and subtitles, 5 credits each | Search public video by moment, $1 per 1,000 searches |
| Concurrency | 25 to 900 requests at once, by plan | Not published yet |
| To try it | 1,000 free API credits, no card | Prepaid credits |
Where ScrapingBee is better: control over each request, scripted steps, extraction, dedicated search and shopping APIs, failed pages that cost nothing, very low prices for simple pages on big plans, and a free trial. Where UpRock fits: the pages that would need ScrapingBee's premium or stealth tiers, countries outside its classic list, and one price whatever the page needs.
The fallback, in TypeScript and Python
Every version ask ScrapingBee for markdown in Auto-Mode with max_cost set to 5, so it tries the 1- and 5-credit tiers and charges nothing if both fail. Countries outside the classic list go straight to UpRock. A 500, 429 or 401 falls through to UpRock; a 404 or 410 is treated as the site's answer. UpRock gets one POST /v1/crawl request with your key in an Authorization: Bearer header; the MCP and CLI tabs do the same through UpRock's MCP server (TypeScript) or CLI (Python).
// npm i scrapingbee (Node 18+) env: SCRAPINGBEE_API_KEY, UPROCK_API_KEYimport { ScrapingBeeClient } from "scrapingbee";const bee = new ScrapingBeeClient(process.env.SCRAPINGBEE_API_KEY ?? "");// Auto-Mode tries 1, 5, 10, 25 and 75 credits in turn and charges only the tier that works,// or 0 if none does. max_cost stops it early; pages that need a pricier tier go to UpRock.const MAX_COST = 5;// Countries ScrapingBee's classic proxies (the 1- and 5-credit tiers) cover. For any other// country they quietly fall back to the US (scrapingbee.com/documentation, checked 21 Oct 2026).const CLASSIC = new Set( "AR AU AT BE BR CA CL CN CO HR DK EE FI FR DE GR HU IN ID IE IL IT JP KR LV LT MX NL NZ NO PL PT RO SA SG SI ES SE CH TH GB US".split( " ", ),);async function viaUpRock(url: string, country: string) { // One call: UpRock loads the page on a real device in that country, tries a fresh device // every 7 s (up to 3 tries) and returns markdown. Failed calls aren't billed. const res = await fetch("https://edge.uprock.com/v1/crawl", { method: "POST", headers: { Authorization: `Bearer ${process.env.UPROCK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ url, country }), signal: AbortSignal.timeout(90_000), }); const page = await res.json(); if (!res.ok) throw new Error(`UpRock ${res.status} ${page.code}: ${page.error}`); return page.markdown ?? "";}export async function read(url: string, country = "US") { if (CLASSIC.has(country) || MAX_COST >= 10) { try { const res = await bee.htmlApi({ url, params: { mode: "auto", max_cost: MAX_COST, country_code: country.toLowerCase(), return_page_markdown: true, }, retries: 0, }); const credits = res.headers["spb-auto-cost"]; return { via: `scrapingbee (${credits} credits)`, markdown: new TextDecoder().decode(res.data), }; } catch (err) { const status = (err as { response?: { status: number } }).response?.status; // 404 and 410 are the site's answer (and billed): a second crawler will see the same. if (status === 404 || status === 410) throw new Error(`The page is gone (${status})`); // 500: no tier within max_cost worked (not billed). 429: no free concurrency slot. // 401: out of credits or a bad key. console.warn(`ScrapingBee ${status ?? (err as Error).message}; trying UpRock`); } } return { via: "uprock", markdown: await viaUpRock(url, country) };}// node read.ts https://developer.mozilla.org/en-US/docs/Web/HTTP TRconst [url = "https://developer.mozilla.org/en-US/docs/Web/HTTP", country = "US"] = process.argv.slice(2);const page = await read(url, country);console.log(page.via, page.markdown.slice(0, 200));# pip install scrapingbee requests env: SCRAPINGBEE_API_KEY, UPROCK_API_KEYimport osimport sysimport requestsfrom scrapingbee import ScrapingBeeClientbee = ScrapingBeeClient(api_key=os.environ.get("SCRAPINGBEE_API_KEY", ""))# Auto-Mode tries 1, 5, 10, 25 and 75 credits in turn and charges only the tier that works,# or 0 if none does. max_cost stops it early; pages that need a pricier tier go to UpRock.MAX_COST = 5# Countries ScrapingBee's classic proxies (the 1- and 5-credit tiers) cover. For any other# country they quietly fall back to the US (scrapingbee.com/documentation, checked 21 Oct 2026).CLASSIC = set( "AR AU AT BE BR CA CL CN CO HR DK EE FI FR DE GR HU IN ID IE IL IT JP KR LV LT MX " "NL NZ NO PL PT RO SA SG SI ES SE CH TH GB US".split())def via_uprock(url: str, country: str) -> str: # One call: UpRock loads the page on a real device in that country, tries a fresh device # every 7 s (up to 3 tries) and returns markdown. Failed calls aren't billed. res = requests.post( "https://edge.uprock.com/v1/crawl", headers={"Authorization": f"Bearer {os.environ['UPROCK_API_KEY']}"}, json={"url": url, "country": country}, timeout=90, ) page = res.json() if not res.ok: raise RuntimeError(f"UpRock {res.status_code} {page.get('code')}: {page.get('error')}") return page.get("markdown", "")def read(url: str, country: str = "US") -> dict: if country in CLASSIC or MAX_COST >= 10: params = { "mode": "auto", "max_cost": MAX_COST, "country_code": country.lower(), "return_page_markdown": True, } try: res = bee.html_api( url, params=params, retries=0 ) # returns a requests.Response except Exception as err: # network error res = None print(f"ScrapingBee unreachable ({err}); trying UpRock", file=sys.stderr) if res is not None and res.ok: credits = res.headers.get("spb-auto-cost") return {"via": f"scrapingbee ({credits} credits)", "markdown": res.text} if res is not None and res.status_code in (404, 410): # The site's answer (and billed): a second crawler will see the same. raise LookupError(f"The page is gone ({res.status_code})") if res is not None: # 500: no tier within max_cost worked (not billed). 429: no free concurrency # slot. 401: out of credits or a bad key. print(f"ScrapingBee {res.status_code}; trying UpRock", file=sys.stderr) return {"via": "uprock", "markdown": via_uprock(url, country)}if __name__ == "__main__": # python read.py https://developer.mozilla.org/en-US/docs/Web/HTTP TR url = sys.argv[1] if len(sys.argv) > 1 else "https://developer.mozilla.org/en-US/docs/Web/HTTP" country = sys.argv[2] if len(sys.argv) > 2 else "US" page = read(url, country) print(page["via"], page["markdown"][:200])// npm i scrapingbee @modelcontextprotocol/sdk (Node 18+) env: SCRAPINGBEE_API_KEY, UPROCK_API_KEYimport { ScrapingBeeClient } from "scrapingbee";import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";const bee = new ScrapingBeeClient(process.env.SCRAPINGBEE_API_KEY ?? "");const uprock = new Client({ name: "scrapingbee-fallback", version: "1.0.0" });await uprock.connect( new StreamableHTTPClientTransport( new URL("https://mcp.uprock.ai/mcp"), { requestInit: { headers: { Authorization: `Bearer ${process.env.UPROCK_API_KEY}` } } }, ),);// Auto-Mode tries 1, 5, 10, 25 and 75 credits in turn and charges only the tier that works,// or 0 if none does. max_cost stops it early; pages that need a pricier tier go to UpRock.const MAX_COST = 5;// Countries ScrapingBee's classic proxies (the 1- and 5-credit tiers) cover. For any other// country they quietly fall back to the US (scrapingbee.com/documentation, checked 21 Oct 2026).const CLASSIC = new Set( "AR AU AT BE BR CA CL CN CO HR DK EE FI FR DE GR HU IN ID IE IL IT JP KR LV LT MX NL NZ NO PL PT RO SA SG SI ES SE CH TH GB US".split( " ", ),);const textOf = (content: unknown) => (content as { type: string; text?: string }[]).find((c) => c.type === "text")?.text ?? "";async function viaUpRock(url: string, country: string) { const res = await uprock.callTool( { name: "crawl_fetch", arguments: { url, country } }, undefined, { timeout: 300_000 }, ); if (res.isError) throw new Error(`UpRock: ${textOf(res.content)}`); const uri = JSON.parse(textOf(res.content)).content?.markdown?.resource; if (!uri) throw new Error(`UpRock: no markdown for ${url}`); return textOf( (await uprock.callTool({ name: "resource_fetch", arguments: { uri } })).content, );}export async function read(url: string, country = "US") { if (CLASSIC.has(country) || MAX_COST >= 10) { try { const res = await bee.htmlApi({ url, params: { mode: "auto", max_cost: MAX_COST, country_code: country.toLowerCase(), return_page_markdown: true, }, retries: 0, }); const credits = res.headers["spb-auto-cost"]; return { via: `scrapingbee (${credits} credits)`, markdown: new TextDecoder().decode(res.data), }; } catch (err) { const status = (err as { response?: { status: number } }).response?.status; // 404 and 410 are the site's answer (and billed): a second crawler will see the same. if (status === 404 || status === 410) throw new Error(`The page is gone (${status})`); // 500: no tier within max_cost worked (not billed). 429: no free concurrency slot. // 401: out of credits or a bad key. console.warn(`ScrapingBee ${status ?? (err as Error).message}; trying UpRock`); } } return { via: "uprock", markdown: await viaUpRock(url, country) };}// node read.ts https://developer.mozilla.org/en-US/docs/Web/HTTP TRconst [url = "https://developer.mozilla.org/en-US/docs/Web/HTTP", country = "US"] = process.argv.slice(2);try { const page = await read(url, country); console.log(page.via, page.markdown.slice(0, 200));} finally { await uprock.close();}# pip install scrapingbee; npm i -g uprock env: SCRAPINGBEE_API_KEY, UPROCK_API_KEYimport jsonimport osimport subprocessimport sysfrom scrapingbee import ScrapingBeeClientbee = ScrapingBeeClient(api_key=os.environ.get("SCRAPINGBEE_API_KEY", ""))# Auto-Mode tries 1, 5, 10, 25 and 75 credits in turn and charges only the tier that works,# or 0 if none does. max_cost stops it early; pages that need a pricier tier go to UpRock.MAX_COST = 5# Countries ScrapingBee's classic proxies (the 1- and 5-credit tiers) cover. For any other# country they quietly fall back to the US (scrapingbee.com/documentation, checked 21 Oct 2026).CLASSIC = set( "AR AU AT BE BR CA CL CN CO HR DK EE FI FR DE GR HU IN ID IE IL IT JP KR LV LT MX " "NL NZ NO PL PT RO SA SG SI ES SE CH TH GB US".split())def via_uprock(url: str, country: str) -> str: run = subprocess.run( ["uprock", "ai", "crawl", url, "--country", country, "--content"], capture_output=True, text=True, timeout=330, ) if run.returncode != 0: raise RuntimeError(f"UpRock: {run.stderr.strip()}") return json.loads(run.stdout).get("inlined_markdown", "")def read(url: str, country: str = "US") -> dict: if country in CLASSIC or MAX_COST >= 10: params = { "mode": "auto", "max_cost": MAX_COST, "country_code": country.lower(), "return_page_markdown": True, } try: res = bee.html_api( url, params=params, retries=0 ) # returns a requests.Response except Exception as err: # network error res = None print(f"ScrapingBee unreachable ({err}); trying UpRock", file=sys.stderr) if res is not None and res.ok: credits = res.headers.get("spb-auto-cost") return {"via": f"scrapingbee ({credits} credits)", "markdown": res.text} if res is not None and res.status_code in (404, 410): # The site's answer (and billed): a second crawler will see the same. raise LookupError(f"The page is gone ({res.status_code})") if res is not None: # 500: no tier within max_cost worked (not billed). 429: no free concurrency # slot. 401: out of credits or a bad key. print(f"ScrapingBee {res.status_code}; trying UpRock", file=sys.stderr) return {"via": "uprock", "markdown": via_uprock(url, country)}if __name__ == "__main__": # python read.py https://developer.mozilla.org/en-US/docs/Web/HTTP TR url = sys.argv[1] if len(sys.argv) > 1 else "https://developer.mozilla.org/en-US/docs/Web/HTTP" country = sys.argv[2] if len(sys.argv) > 2 else "US" page = read(url, country) print(page["via"], page["markdown"][:200])Tested 21 Oct 2026 with an invalid ScrapingBee key (ScrapingBee answers 401) and with the country set to Turkey, which takes the direct route. Without an account, Auto-Mode's success path follows ScrapingBee's docs and SDK types. On 6 Nov 2026 we ran the HTTP and MCP versions again with an UpRock API key in an Authorization header: with an invalid ScrapingBee key, each fell back to UpRock and got the page.
FAQ
Does ScrapingBee charge for a page it couldn't scrape?
No. Its docs say a blocked or failing page reaches you as a 500, and a 500 isn't billed; 404 and 410 are passed through and billed. In Auto-Mode, a request where every configuration failed costs 0 credits.
What's a sensible max_cost?
It depends on your plan, because a credit costs different amounts on each. On Hobby ($19 for 75,000 credits) one credit is about $0.25 per 1,000 pages, so a 5-credit page costs $1.27 per 1,000. On Enterprise 120 a credit is about $0.05 per 1,000. Compare the tier you'd pay for with $0.50 per 1,000 on UpRock and cap below it.
Why send a page to UpRock just because of its country?
ScrapingBee's classic proxies, used by its 1- and 5-credit tiers, cover 42 countries. For any other country they fall back to the US without an error. Premium proxies cover more than 200 countries but start at 10 credits a page. UpRock covers 63 countries at one credit.
Can ScrapingBee return markdown?
Yes, with return_page_markdown, which returns the page's main content as markdown. The code on this page uses it, so both providers hand back the same format.
Does ScrapingBee have an MCP server?
Yes, a hosted one at mcp.scrapingbee.com, billed at the same credit rates as the API.
Other use cases to explore
- Work out what a browser read really costs: per-minute billing, proxy megabytes and a fetch API, side by side for 1,000 pages.
- Fetch from countries your crawler doesn't cover: such as South Korea and Indonesia, sent straight to a device in that country.
- Tell a retryable failure from a refusal: which errors to retry, which to send elsewhere, and which to respect, including robots.txt.
- Search public video by moment: find the second something is shown or said, across platforms.
References
- ScrapingBee, Pricing, checked 21 Oct 2026
- ScrapingBee docs (credit costs, Auto-Mode, max_cost, status codes), checked 21 Oct 2026
- ScrapingBee docs, Classic proxy country codes, checked 21 Oct 2026
- ScrapingBee docs, Premium proxy country codes, checked 21 Oct 2026
- ScrapingBee docs, Remote MCP, checked 21 Oct 2026
- ScrapingBee docs, YouTube API, checked 21 Oct 2026
- ScrapingBee, Acceptable use policy, checked 21 Oct 2026
- ScrapingBee Node SDK on GitHub, checked 21 Oct 2026
- UpRock, everything in one file (API, CLI, MCP, pricing), checked 21 Oct 2026
Get started
Add UpRock in one paste.
Paste this into Claude Code, Codex, Cursor or any agent with a terminal. It reads our docs, sets up the MCP server or CLI, and tells you the cost before it spends anything.
Set up UpRock for me: read https://uprock.ai/agents.md, connect the UpRock MCP server, and run one test crawl. Tell me what it costs before you spend anything.