Skip to content

API reference · v1

ScrapingBot API documentation

Scrape any website, and pull structured TikTok, Instagram, Google, Amazon and ChatGPT data, with one API key. It's plain HTTPS and JSON, so any language works, and failed requests are refunded automatically.

Base URL

https://scrapingbot.io/api/v1

Get an API key

100 free credits, no credit card. Examples use YOUR_API_KEY until you sign in.

Start free

Choose an API

Authentication

Every request is authenticated with an API key. Your keys live in the dashboard, where you can also create more and delete old ones. Send the key in whichever of these is most convenient:

  • x-api-keyheader
    Recommended. x-api-key: YOUR_API_KEY
  • Authorizationheader
    Bearer token form: Authorization: Bearer YOUR_API_KEY. Handy for HTTP clients that have built-in bearer auth.
  • api_keyquery parameter
    Useful for a quick test in the browser. URLs end up in logs and browser history, so prefer a header in real code.
Keep your key on the server. Anyone who has it can spend your credits. If a key leaks, create a new one in the dashboard and delete the old one.

What is checked, in this order

  1. A key is present and valid. Otherwise 401.
  2. Your balance is above zero. Otherwise 402.
  3. You have a free concurrency slot. Otherwise 429 (see concurrency).
  4. Your balance covers this request's cost. Otherwise 402 with the exact amount needed.

None of these rejections cost anything.

Send your key

curl "https://scrapingbot.io/api/v1/scrape?url=https://example.com" \
  -H "x-api-key: YOUR_API_KEY"

Rejections

{
  "success": false,
  "error": "API key required. Get yours at /dashboard"
}

Quick start

Your first request takes a minute:

  1. Create a free account. You get 100 credits, no card needed.
  2. Copy your API key from the dashboard.
  3. Run the request on the right. It fetches example.com over plain HTTP for 1 credit.
  4. Read the page from html. credits_used tells you what the call cost and job_id lets you look it up again for 24 hours.

Where to go next

Request

curl -G "https://scrapingbot.io/api/v1/scrape" \
  -H "x-api-key: YOUR_API_KEY" \
  --data-urlencode "url=https://example.com"
import requests

response = requests.get(
    "https://scrapingbot.io/api/v1/scrape",
    headers={"x-api-key": "YOUR_API_KEY"},
    params={"url": "https://example.com"},
    timeout=60,
)
data = response.json()
print(data["status"], data["credits_used"])
const params = new URLSearchParams({ url: "https://example.com" });
const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, {
  headers: { "x-api-key": "YOUR_API_KEY" },
});
const data = await response.json();
console.log(data.status, data.credits_used);
<?php
$query = http_build_query(["url" => "https://example.com"]);
$ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"],
]);
$data = json_decode(curl_exec($ch), true);
echo $data["status"], " ", $data["credits_used"], PHP_EOL;

Response

200 OK
{
  "success": true,
  "url": "https://example.com",
  "html": "<!doctype html><html lang=en><head><meta charset=utf-8><ti…",
  "status": 200,
  "duration": "0.79",
  "credits_used": 1,
  "job_id": "b8a5d61d-6148-4fb9-b2e6-84a268200472"
}

Credits & charging

Every API draws from the same credit balance. The cost of a request is fixed by what you ask for, and responses report what was actually charged: credits_used on every Website API response, creditsUsed on successful TikTok, Instagram, Google and Amazon responses.

Website API costs don't stack

The most capable option you turn on sets the price: stealth proxy, then premium proxy, then rendering, then plain HTTP. AI extraction adds 5 on top. For example, render_js with premium_proxy is 10 credits, and stealth_proxy with ai_query is 80.

When you're charged

  • Website API: when the target answers with a 2xx or 3xx status, or with 400 or 404 (the page really doesn't exist or rejected the request, which is still a completed scrape). These come back with fault: "user".
  • Refunded automatically: timeouts, blocks and other 4xx answers such as 401, 403 and 429, any 5xx from the target, and errors on our side. These report credits_used: 0.
  • Data APIs (TikTok, Instagram, Google, Amazon, ChatGPT): only a successful 200 is charged. Any error is refunded. Amazon /products also refunds each ASIN it couldn't fetch.
  • Free: requests rejected before they run, such as a missing parameter, an invalid key or a concurrency limit, and job lookups.

Your balance must cover the full cost when a request starts; otherwise you get a 402 that tells you how many credits are needed.

Cost per request

RequestCredits
Website, plain HTTP1
Website, JavaScript renderingrender_js, screenshot or js_scenario5
Website, premium proxypremium_proxy, rendered or not10
Website, stealth proxystealth_proxy, always rendered75
AI extraction add-onai_query or ai_schema+5
TikTokany endpoint1
Instagramany endpoint5
Google Searchper search10
Amazonany endpoint except /products10
Amazon batch products/products, per product returned10
ChatGPTper prompt10

Concurrency & plans

Limits are based on how many requests you have in flight at the same time, not on requests per minute. Each plan has a concurrency limit; a slot is taken when a request starts and freed as soon as its response is sent.

Going over the limit returns 429 immediately and costs nothing. Size your worker pool to your plan's limit, and retry a 429 after a short, growing delay.

Calls made through the MCP server use the same slots and credits as direct API calls.

PlanPriceCreditsConcurrent
Free$0100 on sign-up1
Starter$49.99 / mo275,000 / mo10
Startup$99.99 / mo1,100,000 / mo50
Pro$249.99 / mo3,300,000 / mo100
Business$599.99 / mo9,000,000 / mo200
Business+$999.99 / mo15,500,000 / mo200

Paid plans refresh their credits every billing cycle. Compare plans on the plans page.

A worker pool sized to your plan

Python
import time
import requests
from concurrent.futures import ThreadPoolExecutor

API_KEY = "YOUR_API_KEY"
CONCURRENCY = 10  # your plan's limit

def scrape(url):
    for attempt in range(5):
        r = requests.get(
            "https://scrapingbot.io/api/v1/scrape",
            headers={"x-api-key": API_KEY},
            params={"url": url},
            timeout=60,
        )
        if r.status_code != 429:
            return r.json()
        time.sleep(2 ** attempt)  # every slot is busy: wait, then retry
    raise RuntimeError("still at the concurrency limit")

urls = ["https://example.com/a", "https://example.com/b"]
with ThreadPoolExecutor(max_workers=CONCURRENCY) as pool:
    for result in pool.map(scrape, urls):
        print(result["status"], result["credits_used"])

Errors

Errors come back as JSON with success: false and a readable error message. Depending on the API you'll also see:

  • status: the HTTP status, on Website API responses and on most credit and concurrency errors.
  • fault: on Website API failures, who caused it. user means the target page answered 400 or 404 (charged), system means the site or our side failed (refunded), timeout means it ran past 45 seconds (refunded).
  • code: on TikTok errors, the same number as the HTTP status.

Retrying

Retry 408, 429, 502 and other 5xx responses with exponential backoff; they're refunded, so a retry never pays twice. Don't retry 400, 401 or 402 without changing something first.

Examples

{
  "success": false,
  "status": 400,
  "error": "Missing required parameter: url"
}
StatusWhat happenedCharged
400A parameter is missing or invalid, or the endpoint isn't supported. On the Website API, also a target page that answered 400 (fault: "user").Only target 400s
401The API key is missing or invalid.No
402Your balance is empty, or too low for this request.No
404Website API: the target page doesn't exist (fault: "user"). Data APIs: the profile, post or product wasn't found. Also an unknown /api route or job ID.Only target 404s
405The MCP endpoint was called with something other than POST.No
408The request timed out.No
413The request body is larger than 1 MB.No
429Your plan's concurrency limit is reached.No
500, 502, 503…The target site failed, a data source was temporarily unavailable, or something went wrong on our side.No

Stuck on something?

Try requests in the dashboard playgrounds, check every call in your usage log, or write to [email protected]. A real developer answers.