---
name: grabber
description: Fetch any public web page as clean Markdown, HTML, plain text, links or metadata with one GET request, for $0.001 a page. Use when an agent needs to read a URL, feed pages to RAG, extract links or render a JavaScript page.
---

# Grabber

Grabber connects AI agents to the web: one API call turns any URL into clean Markdown, HTML, text or links. Pay per page, no subscription. This file is everything an agent needs to use it.

## When to use it

- You have a URL and need its readable content, to answer a question, summarize it or cite it.
- You are building a RAG index and want pages as Markdown, without menus, ads and scripts.
- You need every link on a page, or its title, description, Open Graph tags and JSON-LD.
- The content only appears after JavaScript runs: add `js_render=true`.

Grabber does not search the web, crawl a whole site or sign in to sites. Give it the exact URL of a public page.

## Setup

1. A person signs up at https://dashboard.grabber.sh/signup and verifies their email. The account starts with $1 of free credit, enough for 1,000 pages, and needs no card.
2. They create an API key at https://dashboard.grabber.sh/keys. It starts with `gsh_` and is shown once.
3. The agent reads the key from its environment, for example `GRABBER_API_KEY`. Keep it out of URLs, logs and public repositories.

Send the key as `Authorization: Bearer gsh_...` on every request; the header is the only place a key is read. More in https://grabber.sh/auth.md.

## Fetch a page

```bash
curl -G "https://api.grabber.sh/fetch" \
  -H "Authorization: Bearer $GRABBER_API_KEY" \
  --data-urlencode "url=https://example.com/" \
  -d "formats=markdown" \
  -d "unwrap=true"
```

With `unwrap=true` and one format, the body is the content itself, with its own content type. Without it, the answer is a JSON envelope `{ status, message, data }`: `data.fetch` says what was loaded (final URL, status code, content type) and `data.markdown`, `data.html`, `data.text`, `data.links` and `data.metadata` hold what you asked for. Several formats in one request cost the same as one.

## Parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `url` | string, up to 8,192 characters | Yes | — | The address to fetch. Percent-encode it. Give it exactly once. |
| `formats` | list of `markdown`, `html`, `text`, `links`, `raw` | No | — | What to return, in the order you want it: a comma-separated list (`formats=markdown,links`). Repeating the parameter (`formats=markdown&formats=links`) works too. An empty item is refused. |
| `unwrap` | boolean | No | `false` | `true` returns the first format as the response body, instead of the JSON envelope. Only `true` or `false`, given at most once. |
| `includeMetadata` | boolean | No | `false` | `true` adds `data.metadata` (title, description, language, Open Graph tags, JSON-LD, links, phones, emails). Ignored when `unwrap` is true. Only `true` or `false`, given at most once. |
| `js_render` | boolean | No | `false` | `true` runs the page in a headless browser first, so content that depends on JavaScript is included. It costs $0.005 instead of $0.001 and can take up to 120 seconds. Only `true` or `false`, given at most once. |

## Response headers

| Header | Meaning |
| --- | --- |
| `X-Grabber-Cost` | What this request cost, in US dollars: `0.001`, `0.005`, or `0` when nothing was charged. |
| `X-Grabber-Balance` | What is left of your credit after this request, in US dollars. |
| `Cache-Control` | Always `no-store`. Answers are never cached. |
| `WWW-Authenticate` | Always `Bearer`. |
| `Allow` | Always `GET`. |
| `Retry-After` | Seconds until the next minute begins. |

## Errors

Every error is JSON, `{ status, message, detail, hint }` (`hint` only on some), and costs nothing. Branch on `detail`, which never changes.

| Status | detail | Meaning |
| --- | --- | --- |
| `400` | `invalid_query` | The query is wrong, and `hint` says how. |
| `400` | `invalid_url` | The url is not `http` or `https`, has a username or password, or is too long. |
| `400` | `blocked_url` | The url points at a host that cannot be fetched. |
| `401` | `invalid_api_key` | The key is missing, malformed, unknown, or deleted. |
| `402` | `insufficient_credits` | The balance is below the price of this request. Nothing was charged and nothing was fetched. |
| `405` | `method_not_allowed` | The method is not `GET`. |
| `429` | `rate_limited` | More than 60 requests in this minute for this key. Wait `Retry-After` seconds. |
| `500` | `internal_error` | Something failed on Grabber's side. Nothing was charged. Try again; if it keeps happening, it is not caused by your request. |
| `502` | `fetch_failed` | The page could not be loaded. |
| `502` | `blocked_url` | The page redirected to a host that cannot be fetched. |
| `502` | `body_too_large` | The page is over 8 MiB. |
| `502` | `format_unavailable` | `unwrap=true`, and none of the requested formats can be produced for this content. |
| `502` | `upstream_unavailable` | Grabber could not get an answer from its fetching service. |

- On `429`, wait the seconds in `Retry-After`, then retry.
- On `500` or `502 upstream_unavailable`, retry with a growing delay: 1, 2, then 4 seconds.
- On `400`, fix the request; sending it again gives the same answer.
- On `402`, tell the person to add credit at https://dashboard.grabber.sh/billing.

## Costs and limits

- $0.001 per page, $0.005 with `js_render=true`. Errors cost nothing; a page whose own server answered 404 or 500 was loaded, so it is charged.
- Prepaid credit, added from $5, valid for 365 days. `X-Grabber-Cost` and `X-Grabber-Balance` report the spend on every answer.
- 60 requests per minute per key. `url` up to 8,192 characters, pages up to 8 MiB, rendered pages up to 120 seconds: set your client timeout to 130 seconds.

## MCP

The same API is a remote MCP server at https://mcp.grabber.sh (Streamable HTTP), with the same key in the `Authorization` header and the same 60-per-minute limit.

| Tool | What it does | Cost |
| --- | --- | --- |
| `fetch_page` | Loads one public web page and returns its content, as Markdown unless you ask for another format. | $0.001, or $0.005 with `js_render`. Errors are free. |
| `check_balance` | Returns the credit left on the account of the key, in US dollars. | Free |

`fetch_page` takes `url`, `format`, `js_render` and `max_chars` (at most 100,000). Cursor, in `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "grabber": {
      "url": "https://mcp.grabber.sh",
      "headers": {
        "Authorization": "Bearer gsh_your_api_key"
      }
    }
  }
}
```

Claude Code:

```bash
claude mcp add --transport http grabber https://mcp.grabber.sh \
  --header "Authorization: Bearer gsh_your_api_key"
```

Other clients: https://grabber.sh/mcp.md

## More

- Every page, as Markdown links: https://grabber.sh/llms.txt
- The docs: https://grabber.sh/docs.md
- The OpenAPI spec: https://grabber.sh/openapi.json
