# How do I add a backup to Context.dev's scrape API?

Keep Context.dev as your primary, since on its larger plans a page costs about the same as on UpRock or less, and send UpRock only the scrapes that come back failed, which arrive inside a normal HTTP 200 with success set to false. Leave alone the failures a second crawler can't fix, like a real 404 or a file type it doesn't support.

Updated 21 Oct 2026. Web page: https://uprock.ai/compare/context-dev

## On this page

- [What Context.dev does well](#what-contextdev-does-well)
- [A failed scrape still answers 200](#a-failed-scrape-still-answers-200)
- [What a page costs by plan](#what-a-page-costs-by-plan)
- [Context.dev and UpRock side by side](#contextdev-and-uprock-side-by-side)
- [The fallback, in TypeScript and Python](#the-fallback-in-typescript-and-python)
- [FAQ](#faq)
- [Other use cases to explore](#explore)
- [Related articles](#related)
- [References](#references)
- [Get started](#get-started)

## What Context.dev does well

Context.dev (formerly Brand.dev) is a web scraping API for AI agents with a lot bundled into one key: single-page scrapes that return markdown, HTML or JSON extracted against a schema, site crawls of up to 500 pages, URL maps, search, answers, batches, monitors that watch pages for changes, PDF and Word parsing, and the brand and company data it started with. Every request loads in a real browser with JavaScript, it can fetch from a chosen country, and there are Python and TypeScript SDKs and an official MCP server with 42 tools. It claims a 99.96% scrape success rate.

And on its bigger plans it's priced like UpRock. If it covers your pages, keep it.

## A failed scrape still answers 200

Context.dev's docs are direct about it: inspect each output's `success` "even after HTTP 200". When a scrape fails, the markdown output comes back with `success: false`, `data: null` and an `error_code`. Here's how we'd route each code:

| `error_code` | What it means | What to do |
|---|---|---|
| `WEBSITE_BLOCKED` | The site blocked Context.dev's request | Send to UpRock |
| `REQUEST_TIMEOUT` | The page didn't load in time | Send to UpRock |
| `WEBSITE_ACCESS_ERROR` | Context.dev couldn't reach the site | Send to UpRock |
| `INTERNAL_ERROR` | Something failed on Context.dev's side | Send to UpRock |
| `NOT_FOUND` | A real 404 from the site (still billed) | Don't retry |
| `UNSUPPORTED_CONTENT`, `CONTENT_TOO_LARGE`, `PDF_IMAGES_ONLY` | The content itself is the problem | Don't retry |

Problems with the request itself come back as HTTP errors, which the SDKs throw: 401 for the key, 402 when you're out of credits, 408 at the deadline, 429 at the concurrency limit, 5xx on Context.dev's side, all worth sending to UpRock. A 403 can mean a domain restriction, and a 400 a bad request; fix or respect those.

One more trap: the SDKs wait up to 60 seconds per attempt and retry twice, while a scrape's own deadline defaults to 90 seconds. Raise the timeout and turn off retries, so a slow page fails over instead of being tried three times.

## What a page costs by plan

At one credit per markdown page (two with browser actions, five with JSON extraction), from Context.dev's pricing page, checked 4 Nov 2026:

| Plan | Monthly | Credits | Per 1,000 pages, monthly | Per 1,000, billed yearly | Overage per 1,000 |
|---|---|---|---|---|---|
| Free | $0 | 1,000 | | | none |
| Developer | $20 | 10,000 | $2.00 | $1.67 | $3.00 |
| Pro | $90 | 100,000 | $0.90 | $0.75 | $2.00 |
| Growth | $300 | 500,000 | $0.60 | $0.50 | $1.50 |
| Scale | $500 | 1,000,000 | $0.50 | $0.42 | $1.00 |
| Scale 2.5M | $950 | 2,500,000 | $0.38 | $0.32 | $0.75 |
| UpRock | prepaid | | $0.50 | $0.50 | |

The per-page prices assume you use the whole allowance; past it, overage costs $0.75 to $3.00 per 1,000. Cache hits count as pages.

## Context.dev and UpRock side by side

| | Context.dev | UpRock |
|---|---|---|
| Price per 1,000 pages | [$2.00 to $0.32](https://www.context.dev/pricing) by plan (table above) | $0.50 by card or USDC, $0.40 with UPT |
| Failed scrapes | [Not charged](https://docs.context.dev/account/credits) unless an output succeeded; a 404 is | Not documented yet |
| Crawl, map, search, monitors, JSON extraction, brand data | Yes | No. One page per call |
| Country | [`sharedParams.country`](https://docs.context.dev/scrape/location-and-headers), two-letter code | Every request: 63 countries, or a region |
| Where requests come from | Proxies; their type and location aren't stated | Devices whose owners opted in |
| Concurrency | [1 to 500](https://docs.context.dev/account/billing) by plan | Not published yet |
| MCP server | [Official](https://docs.context.dev/install-mcp), hosted, 42 tools, OAuth or key in a header | Yes, remote (streamable HTTP) |
| Video | YouTube captions and metadata; a TikTok video URL | Search public video by moment, $1 per 1,000 searches |
| To try it | 1,000 credits a month, no card | Prepaid credits |

Where Context.dev is better: everything bundled beyond the page fetch, structured extraction, monitors, brand data, low prices on the bigger plans, and a free tier. Where UpRock fits: a second, independent network for the scrapes that fail, one price with no plan, and video search.

## The fallback, in TypeScript and Python

Every version ask Context.dev for fresh markdown (`maxAgeMs: 0`) in the country you pass, with the SDK's retries off and its timeout raised past the scrape deadline. A successful output is used. A failed output with a routable code, or an HTTP error that isn't 400 or 403, goes to UpRock; anything else raises. 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).

### TypeScript (HTTP)

```ts
// npm i context.dev   (Node 18+)   env: CONTEXT_DEV_API_KEY, UPROCK_API_KEY
import ContextDev, { APIError } from "context.dev";

// A scrape's deadline is 90 s by default, longer than the SDK's 60 s per attempt, and the SDK
// retries twice. Give it room and fail over to UpRock instead of retrying.
const ctx = new ContextDev({
  apiKey: process.env.CONTEXT_DEV_API_KEY,
  timeout: 120_000,
  maxRetries: 0,
});

// Output-level error codes worth a second try on another network. NOT_FOUND (a real 404,
// billed), UNSUPPORTED_CONTENT, CONTENT_TOO_LARGE and PDF_IMAGES_ONLY won't change.
const TRY_UPROCK = new Set([
  "WEBSITE_BLOCKED",
  "REQUEST_TIMEOUT",
  "WEBSITE_ACCESS_ERROR",
  "INTERNAL_ERROR",
]);

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") {
  try {
    const res = await ctx.web.scrape({
      url,
      formats: { markdown: true },
      sharedParams: { country },
      maxAgeMs: 0, // fresh fetch; cached copies (up to 3 days old by default) are billed too
    });
    // HTTP 200 doesn't mean the page came back: check the output itself.
    if (res.markdown.success && res.markdown.data)
      return { via: "context.dev", markdown: res.markdown.data };
    const code = res.markdown.error_code ?? "UNKNOWN";
    if (!TRY_UPROCK.has(code))
      throw new Error(`Context.dev: ${code} ${res.markdown.message ?? ""}`);
    console.warn(`Context.dev: ${code}; trying UpRock`);
  } catch (err) {
    if (err instanceof APIError) {
      // 400 is a bad request and 403 covers domain restrictions: fix or respect those.
      if (err.status === 400 || err.status === 403) throw err;
      // 401 (key), 402 (out of credits), 408 (deadline), 429 (concurrency), 5xx.
      console.warn(`Context.dev ${err.status}; trying UpRock`);
    } else if ((err as Error).message.startsWith("Context.dev:")) {
      throw err;
    } else {
      console.warn(
        `Context.dev unreachable (${(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 DE
const [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));
```

### Python (HTTP)

```python
# pip install context.dev requests   env: CONTEXT_DEV_API_KEY, UPROCK_API_KEY
import os
import sys

import requests
from context.dev import APIConnectionError, APIStatusError, ContextDev

# A scrape's deadline is 90 s by default, longer than the SDK's 60 s per attempt, and the SDK
# retries twice. Give it room and fail over to UpRock instead of retrying.
ctx = ContextDev(
    api_key=os.environ.get("CONTEXT_DEV_API_KEY"), timeout=120, max_retries=0
)

# Output-level error codes worth a second try on another network. NOT_FOUND (a real 404,
# billed), UNSUPPORTED_CONTENT, CONTENT_TOO_LARGE and PDF_IMAGES_ONLY won't change.
TRY_UPROCK = {
    "WEBSITE_BLOCKED",
    "REQUEST_TIMEOUT",
    "WEBSITE_ACCESS_ERROR",
    "INTERNAL_ERROR",
}


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:
    try:
        res = ctx.web.scrape(
            url=url,
            formats={"markdown": True},
            shared_params={"country": country},
            max_age_ms=0,  # fresh fetch; cached copies (up to 3 days old by default) are billed too
        )
    except APIStatusError as err:
        # 400 is a bad request and 403 covers domain restrictions: fix or respect those.
        if err.status_code in (400, 403):
            raise
        # 401 (key), 402 (out of credits), 408 (deadline), 429 (concurrency), 5xx.
        print(f"Context.dev {err.status_code}; trying UpRock", file=sys.stderr)
    except APIConnectionError as err:
        print(f"Context.dev unreachable ({err}); trying UpRock", file=sys.stderr)
    else:
        # HTTP 200 doesn't mean the page came back: check the output itself.
        if res.markdown.success and res.markdown.data:
            return {"via": "context.dev", "markdown": res.markdown.data}
        code = res.markdown.error_code or "UNKNOWN"
        if code not in TRY_UPROCK:
            raise RuntimeError(f"Context.dev: {code} {res.markdown.message or ''}")
        print(f"Context.dev: {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 DE
    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])
```

### TypeScript (MCP)

```ts
// npm i context.dev @modelcontextprotocol/sdk   (Node 18+)   env: CONTEXT_DEV_API_KEY, UPROCK_API_KEY
import ContextDev, { APIError } from "context.dev";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

// A scrape's deadline is 90 s by default, longer than the SDK's 60 s per attempt, and the SDK
// retries twice. Give it room and fail over to UpRock instead of retrying.
const ctx = new ContextDev({
  apiKey: process.env.CONTEXT_DEV_API_KEY,
  timeout: 120_000,
  maxRetries: 0,
});
const uprock = new Client({ name: "context-dev-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}` } } },
  ),
);

// Output-level error codes worth a second try on another network. NOT_FOUND (a real 404,
// billed), UNSUPPORTED_CONTENT, CONTENT_TOO_LARGE and PDF_IMAGES_ONLY won't change.
const TRY_UPROCK = new Set([
  "WEBSITE_BLOCKED",
  "REQUEST_TIMEOUT",
  "WEBSITE_ACCESS_ERROR",
  "INTERNAL_ERROR",
]);

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") {
  try {
    const res = await ctx.web.scrape({
      url,
      formats: { markdown: true },
      sharedParams: { country },
      maxAgeMs: 0, // fresh fetch; cached copies (up to 3 days old by default) are billed too
    });
    // HTTP 200 doesn't mean the page came back: check the output itself.
    if (res.markdown.success && res.markdown.data)
      return { via: "context.dev", markdown: res.markdown.data };
    const code = res.markdown.error_code ?? "UNKNOWN";
    if (!TRY_UPROCK.has(code))
      throw new Error(`Context.dev: ${code} ${res.markdown.message ?? ""}`);
    console.warn(`Context.dev: ${code}; trying UpRock`);
  } catch (err) {
    if (err instanceof APIError) {
      // 400 is a bad request and 403 covers domain restrictions: fix or respect those.
      if (err.status === 400 || err.status === 403) throw err;
      // 401 (key), 402 (out of credits), 408 (deadline), 429 (concurrency), 5xx.
      console.warn(`Context.dev ${err.status}; trying UpRock`);
    } else if ((err as Error).message.startsWith("Context.dev:")) {
      throw err;
    } else {
      console.warn(
        `Context.dev unreachable (${(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 DE
const [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();
}
```

### Python (CLI)

```python
# pip install context.dev; npm i -g uprock   env: CONTEXT_DEV_API_KEY, UPROCK_API_KEY
import json
import os
import subprocess
import sys

from context.dev import APIConnectionError, APIStatusError, ContextDev

# A scrape's deadline is 90 s by default, longer than the SDK's 60 s per attempt, and the SDK
# retries twice. Give it room and fail over to UpRock instead of retrying.
ctx = ContextDev(
    api_key=os.environ.get("CONTEXT_DEV_API_KEY"), timeout=120, max_retries=0
)

# Output-level error codes worth a second try on another network. NOT_FOUND (a real 404,
# billed), UNSUPPORTED_CONTENT, CONTENT_TOO_LARGE and PDF_IMAGES_ONLY won't change.
TRY_UPROCK = {
    "WEBSITE_BLOCKED",
    "REQUEST_TIMEOUT",
    "WEBSITE_ACCESS_ERROR",
    "INTERNAL_ERROR",
}


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:
    try:
        res = ctx.web.scrape(
            url=url,
            formats={"markdown": True},
            shared_params={"country": country},
            max_age_ms=0,  # fresh fetch; cached copies (up to 3 days old by default) are billed too
        )
    except APIStatusError as err:
        # 400 is a bad request and 403 covers domain restrictions: fix or respect those.
        if err.status_code in (400, 403):
            raise
        # 401 (key), 402 (out of credits), 408 (deadline), 429 (concurrency), 5xx.
        print(f"Context.dev {err.status_code}; trying UpRock", file=sys.stderr)
    except APIConnectionError as err:
        print(f"Context.dev unreachable ({err}); trying UpRock", file=sys.stderr)
    else:
        # HTTP 200 doesn't mean the page came back: check the output itself.
        if res.markdown.success and res.markdown.data:
            return {"via": "context.dev", "markdown": res.markdown.data}
        code = res.markdown.error_code or "UNKNOWN"
        if code not in TRY_UPROCK:
            raise RuntimeError(f"Context.dev: {code} {res.markdown.message or ''}")
        print(f"Context.dev: {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 DE
    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 against the live UpRock API with an invalid Context.dev key: the SDKs raised a 401 and both versions fell back to UpRock. Without an account, the output-level codes follow Context.dev's error docs and SDK types (context.dev 3.13 for Node, 2.35 for Python). On 6 Nov 2026 we ran the HTTP and MCP versions again with an UpRock API key in an `Authorization` header: with an invalid Context.dev key, each fell back to UpRock and got the page.

## FAQ

### How does Context.dev report a page it couldn't scrape?

Inside an HTTP 200. Its docs say to inspect each output's success field even after a 200; a failed markdown output has success false, no data and an error_code such as WEBSITE_BLOCKED or REQUEST_TIMEOUT. HTTP errors (401, 402, 408, 429, 5xx) are for problems with the request itself.

### Is UpRock cheaper than Context.dev?

Not always. Used fully, Context.dev's plans work out to $2.00 per 1,000 pages on Developer down to $0.38 on Scale 2.5M, and yearly billing takes two months off. UpRock is $0.50 per 1,000 pages without a plan. The case for UpRock here is a second network for the pages that fail, not price.

### Does Context.dev charge for failed scrapes?

Its credits docs say the charge applies when at least one output succeeds, including cache hits, and a target 404 is still charged. Validation, authentication, permission, timeout, rate-limit and server errors aren't.

### Why set maxAgeMs to 0?

Context.dev serves cached copies up to three days old by default, and a cache hit is still billed. maxAgeMs 0 asks for a fresh fetch, which is what you want before deciding a page failed.

### Is there a risk in sending lots of failing requests?

On the free plan, yes. Context.dev's troubleshooting docs say free organizations can have all keys disabled after repeated failed requests, so it's worth moving stubborn pages elsewhere instead of retrying them.

## Other use cases to explore

- [Catch failures hidden in a successful response](https://uprock.ai/compare/exa#how-exa-reports-a-page-it-couldnt-fetch): another provider that reports failed URLs inside a 200, and how to read them.
- [Don't let a cache hide a failed fetch](https://uprock.ai/compare/parallel#the-cache-fallback-trap): a provider that quietly serves an older copy when a live fetch fails, and the setting that stops it.
- [Fetch from countries your crawler doesn't cover](https://uprock.ai/compare/firecrawl#when-a-second-crawler-helps): such as South Korea and Indonesia, sent straight to a device in that country.
- [Search public video by moment](https://uprock.ai/video-semantic-search): find the second something is shown or said, across platforms.

## Related articles

- [How do I add a backup crawler to Firecrawl?](https://uprock.ai/compare/firecrawl.md): When to send a page from Firecrawl to UpRock, published prices side by side, and a tested fallback in TypeScript and Python.
- [How do I add a backup for Tavily Extract?](https://uprock.ai/compare/tavily.md): Retry the URLs in Tavily Extract's failed results on a real device, and read pages as they appear in a chosen country. Prices side by side and tested code.
- [How do I add a fallback for Exa contents errors?](https://uprock.ai/compare/exa.md): Read the per-URL statuses in Exa's /contents response and retry the right ones live on a real device. Error tags explained, prices side by side, tested code.
- [How do I add a backup to Jina Reader, and when is it cheaper?](https://uprock.ai/compare/jina-reader.md): Spot a failed read in Reader's JSON (it still answers 200), retry it on a real device, and see what six real pages cost in Reader tokens next to a flat per-page price.
- [How do I add a hosted fallback to Crawl4AI?](https://uprock.ai/compare/crawl4ai.md): Plug UpRock into Crawl4AI's fallback_fetch_function so blocked pages are fetched on a real device in the country you choose, then run through Crawl4AI's own markdown pipeline.
- [How do I add a backup to Bright Data's Web Unlocker?](https://uprock.ai/compare/bright-data.md): Which Web Unlocker errors to retry, which to send to a second network and which to respect, with Bright Data's published prices next to ours and tested fallback code.
- [How do I add a fallback to an Apify or Crawlee crawler?](https://uprock.ai/compare/apify.md): Hand the requests Crawlee gives up on to a second network with failedRequestHandler, see what a fetched page costs on Apify's own Actors, and run the tested code.
- [When should a Browserbase agent read a page without opening a browser?](https://uprock.ai/compare/browserbase.md): What 1,000 page reads cost on Browserbase three ways, when to send a read to UpRock instead (including when every browser is busy), and tested fallback code.
- [How do I cap ScrapingBee's credit cost and send the rest to a backup?](https://uprock.ai/compare/scrapingbee.md): ScrapingBee charges 1 to 75 credits a page depending on the options. Cap it with Auto-Mode and max_cost, watch the country fallback, and send the rest to UpRock with tested code.
- [Spider costs less per page than UpRock. When is a backup still worth it?](https://uprock.ai/compare/spider.md): Spider is cheaper per page, so keep it as your primary. How Spider reports a page it couldn't get (from our own tests), and tested code that sends only those pages to UpRock.
- [How do I add a fallback to Parallel's Extract API?](https://uprock.ai/compare/parallel.md): Make Parallel Extract report a failed live fetch instead of quietly serving an old cached copy, then retry those URLs on UpRock. Prices side by side and tested code.
- [How do I add a backup to ZenRows?](https://uprock.ai/compare/zenrows.md): Which ZenRows error codes to send to a second network and which to respect, what a page from one country costs once premium proxies are required, and tested fallback code.
- [How do I retry a ScraperAPI batch's failed jobs on another network?](https://uprock.ai/compare/scraperapi.md): Send the jobs ScraperAPI's async batch gives up on to UpRock, see which countries each ScraperAPI plan can target, and compare credit costs. Tested code for both.
- [How do I add a backup to Oxylabs' new Web API?](https://uprock.ai/compare/oxylabs.md): Which Oxylabs Web API failures to send to a second network and which to respect, what a generic page really costs after its October 2026 pricing change, and tested fallback code.
- [How do I add a backup to Scrapfly instead of paying for residential?](https://uprock.ai/compare/scrapfly.md): A Scrapfly page costs 1 to 30 credits depending on proxy pool and browser. What that is per 1,000 pages on each plan, which errors to send on, and tested code.
- [How do I keep Crawlbase's site tiers from surprising my bill?](https://uprock.ai/compare/crawlbase.md): Crawlbase bills each site at 1 to 20 times its base rate. What that does to the cost per 1,000 pages, and tested code that moves pricey domains to UpRock.
- [Which unlocker API fits: Bright Data, Oxylabs, Zyte or UpRock?](https://uprock.ai/compare/web-unlocker-apis.md): Bright Data's Web Unlocker, Oxylabs' Web API and Zyte API priced per 1,000 hard pages, with what each bills and when a second network helps.
- [Compare: all articles](https://uprock.ai/compare.md)

## References

1. [Context.dev, Pricing](https://www.context.dev/pricing), checked 21 Oct 2026
2. [Context.dev docs, Credits (what's charged)](https://docs.context.dev/account/credits), checked 21 Oct 2026
3. [Context.dev docs, Scrape timeouts and errors](https://docs.context.dev/scrape/timeouts-and-errors), checked 21 Oct 2026
4. [Context.dev docs, Location and headers (country)](https://docs.context.dev/scrape/location-and-headers), checked 21 Oct 2026
5. [Context.dev docs, SDKs (timeouts and retries)](https://docs.context.dev/sdks), checked 21 Oct 2026
6. [Context.dev docs, Billing and concurrency](https://docs.context.dev/account/billing), checked 21 Oct 2026
7. [Context.dev docs, Troubleshooting](https://docs.context.dev/optimization/troubleshooting), checked 21 Oct 2026
8. [Context.dev docs, Install the MCP server](https://docs.context.dev/install-mcp), checked 21 Oct 2026
9. [Context.dev, llms.txt (product overview)](https://context.dev/llms.txt), checked 21 Oct 2026
10. [UpRock, everything in one file (API, CLI, MCP, pricing)](https://uprock.ai/llms-full.txt), checked 21 Oct 2026

## Get started

Get an API key: https://uprock.ai/app/api-keys

Paste this into your agent:

```text
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.
```

Setup for every client (MCP, skill, CLI): https://uprock.ai/agents.md
