# Quickstart

Make your first ByteKit API call in under 5 minutes — no SDK required.

Nothing to install. You only need `curl` and an API key.

## Get an API key

Sign up at [app.bytekit.com](https://app.bytekit.com) and create an API key from the
dashboard. Keys are prefixed `sk_live_`.

See [Authentication](https://bytekit.com/docs/guides/authentication) for details on key formats and management.

## What you can try without paying

A new pay-as-you-go account receives a one-time starter grant of **100 MB of bandwidth and
100 credits**. It is issued once per account, is not a purchase, and does not expire, so you can
run the requests below without buying anything first. Real requests draw it down. See
[Balance and exhausted quota](https://bytekit.com/docs/billing/balance#starter-grant) for how the grant is spent and what
happens when a balance reaches zero.

## Make your first scrape

Set your API key and run this — no file to save, no `chmod`:

```bash
curl -X POST https://api.bytekit.com/v1/scrape \
  -H "Authorization: Bearer $BYTEKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'
```

A successful response looks like this — every envelope field is `snake_case`:

```json
{
  "schema_version": 1,
  "status": "success",
  "id": "sc_84c14b1058184045b97ae360ad74888b",
  "url": "https://example.com",
  "final_url": "https://example.com",
  "content_type": "text/html",
  "content_length": 318,
  "status_code": 200,
  "retrieved_at": "2026-08-27T12:16:01.872Z",
  "formats": {
    "raw": "<!doctype html><html lang=\"en\"><head><title>Example Domain</title>…</html>\n"
  },
  "metadata": {
    "title": "Example Domain",
    "description": null,
    "language": "en",
    "canonicalUrl": null,
    "ogTitle": null,
    "ogDescription": null,
    "ogImage": null,
    "ogUrl": null,
    "ogSiteName": null,
    "robots": null,
    "byline": null,
    "publishedAt": null
  },
  "cache": "miss",
  "billing": {
    "raw_bytes": 318,
    "factors": [{ "name": "endpoint", "value": 1, "reason": "scrape" }],
    "multiplier": 1,
    "billed_bytes": 318
  }
}
```

`content_length` is the compressed wire size in bytes (used for bandwidth billing), and
`billing` breaks down what the request actually cost. `cache` is `"miss"` or `"hit"`, and a
hit adds a `cache_age_s` field. The `id` field uses the `sc_` prefix — you can poll
`GET /v1/scrape/{id}` if the scrape was queued asynchronously. A page that only a hosted
browser could retrieve bills that browser's full measured transfer instead of one response.

The keys inside `metadata` stay `camelCase` (`canonicalUrl`, `ogTitle`, …): they are the
page's own metadata, not envelope fields.

## Ask for markdown

The default format is `raw` — that is why the response above carries no
`formats.markdown`. Request markdown explicitly:

```bash
curl -X POST https://api.bytekit.com/v1/scrape \
  -H "Authorization: Bearer $BYTEKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","formats":["markdown"]}'
```

Markdown output is billed at **1.5×** the request's upstream wire bytes — the `endpoint`
factor in the `billing` node below is the rate your balance is charged, not a display
weighting. See [Bandwidth billing](https://bytekit.com/docs/billing/bandwidth) for how the factors compose.

The response's `formats` object is keyed by what you asked for:

```json
{
  "schema_version": 1,
  "status": "success",
  "id": "sc_f659fa67dfb244159767132c2aa50917",
  "url": "https://example.com",
  "final_url": "https://example.com",
  "content_type": "text/html",
  "content_length": 318,
  "status_code": 200,
  "retrieved_at": "2026-08-27T12:16:11.337Z",
  "formats": {
    "markdown": "---\ntitle: \"Example Domain\"\nurl: \"https://example.com\"\ncaptured_at: \"2026-08-27T12:16:11.416Z\"\n---\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)"
  },
  "metadata": {
    "title": "Example Domain",
    "description": null,
    "language": "en",
    "canonicalUrl": null,
    "ogTitle": null,
    "ogDescription": null,
    "ogImage": null,
    "ogUrl": null,
    "ogSiteName": null,
    "robots": null,
    "byline": null,
    "publishedAt": null
  },
  "cache": "hit",
  "cache_age_s": 8,
  "billing": {
    "raw_bytes": 318,
    "factors": [
      { "name": "endpoint", "value": 1.5, "reason": "scrape_md" },
      { "name": "cache_hit", "value": 0.5, "reason": "hit, age 8s" }
    ],
    "multiplier": 0.75,
    "billed_bytes": 239
  }
}
```

See [Scraping](https://bytekit.com/docs/guides/scraping) for the other formats (`markdown`, `links`, `images`).

## Prefer an SDK?

Skip the raw HTTP and use an official client:

- [TypeScript SDK](https://bytekit.com/docs/libraries/typescript) — `npm install @hunt-labs/bytekit-sdk`
- [Python SDK](https://bytekit.com/docs/libraries/python) — `pip install bytekit-sdk`

Install `@hunt-labs/bytekit-sdk` (npm) / `bytekit-sdk` (PyPI). The npm package named `bytekit` is not ours.

## Next steps

- [Authentication](https://bytekit.com/docs/guides/authentication) — key formats, Bearer header, self-serve key management
- [Scraping](https://bytekit.com/docs/guides/scraping) — formats, options, latency, and async polling
- [Errors](https://bytekit.com/docs/guides/errors) — error shape, common codes, retry guidance
- [Rate Limits](https://bytekit.com/docs/guides/rate-limits) — usage counting, concurrency slots, rate limit headers
- [Monitors](https://bytekit.com/docs/guides/monitors) — page-change detection (screenshot or scrape) with webhook notifications
- [API Reference](https://bytekit.com/docs/api) — full endpoint documentation