# The http tier

The cheapest way to read a page: what Kaabist can do without a browser, what makes a job need one, and how conditions behave on a document.

The http tier reads a page with one request and no browser: no JavaScript runs and nothing is laid out, and Kaabist extracts from the document that came back. It is the default for any program that only navigates and extracts, and it costs one compute credit per request where a browser costs five.

## When to use it

Most pages are documents: they arrive complete and say what they say in their markup, embedded JSON or `ld+json`. Any `goto` + `extract` program, including every `url` + `extract` job, starts here. You do not need to ask for it; you need to avoid steps that force a browser when a document would do, and to know what changes about conditions at this tier.

## What it fetches with

Not a plain HTTP client. A request from this tier carries a real browser's TLS and HTTP/2 fingerprint and a real browser's headers in a real browser's order, and goes out through the same proxy pool a browser job would use. A site that serves documents to browsers and blocks scrapers usually cannot tell the difference, and when it can, the job moves up a tier by itself.

Cookies persist across navigations inside one job, so a program that follows a redirect, collects a consent cookie and asks for the page again behaves like one browsing session. A bound [session](https://docs.kaab.ist/concepts/sessions) seeds those cookies. `options.headers` adds request headers to every request this tier makes.

## What it can read

Everything in the document: CSS, XPath, text and role locators, `regex` over text or HTML, and `json` locators over the JSON the page embeds (`<script id="__NEXT_DATA__">`, `window.__INITIAL_STATE__`, `application/ld+json`). A `network` locator works here too: the endpoints a program's `network` locators name are fetched alongside the document, so a program that reads a site's JSON API directly can run without a browser.

## What needs a browser

The http tier has a document, not a page. There is nothing to click, nothing has been laid out and no script has run, so a program containing any of these steps starts at the browser tier instead:

`click` · `fill` · `press` · `hover` · `select` · `check` · `uncheck` · `upload` · `scroll` · `wait_for` · `evaluate` · `screenshot` · `download` · `paginate` · `await_input` · `set_session` · `capture_response` · `loop_until`

A target inside an `iframe` also needs a browser: the http tier fetched one document and no frame in it. If a job reaches such a step at this tier, it moves up and records why:

```json
{ "meta": { "tier": "browser", "escalations": [{ "from": "http", "to": "browser", "reason": "tier_insufficient:frame" }] } }
```

A page that looks like a document but is an empty shell, a `<div id="root">` and a script, moves up for a different reason: the expected content is missing, and the page is classified `empty_shell`. See [Tiers and escalation](https://docs.kaab.ist/concepts/tiers-and-escalation).

## Conditions at this tier

`exists`, `absent`, `count`, `text`, `url`, `title`, `var` and `landmark` behave exactly as they do at the browser tier. Two do not:

- **`visible` means `exists` here, and `hidden` means `absent`.** Nothing was painted, and "cannot tell" is not "no": a price behind a tab is still the price.
- **`network` is always false.** One request was made, by Kaabist, and nothing ran to make another. A program that waits for an API call belongs at the browser tier.

`options.block_urls` and `options.assets` are ignored, because this tier loads no subresources.

## The shared cache

A plain `goto` + `extract` job with no session and no inputs can be served from the shared cache when you set `cache.max_age`:

```json
{ "url": "https://shop.example.com/p/1", "extract": { "title": "h1" }, "cache": { "max_age": 300 } }
```

A hit pays its compute credit and no proxy traffic, and says so with `meta.cache: "hit"` and a `cache_hit` warning. The eligibility rules are on [Tiers and escalation](https://docs.kaab.ist/concepts/tiers-and-escalation#the-shared-cache).

## Worked example

Run a shop's product page, server-rendered with an `ld+json` block and `__NEXT_DATA__`:

```json
{
  "url": "https://shop.example.com/p/1",
  "extract": {
    "title": "h1",
    "price": { "selector": ".price", "type": "money", "locale": "tr", "currency": "TRY" },
    "stock": { "json": "$.props.pageProps.product.stock", "type": "integer" }
  }
}
```

**The request.** One `GET` to `/p/1` with a browser's fingerprint, through a datacenter proxy. `meta.tier` is `http`, `meta.escalations` is empty, and `meta.steps` lists the `goto` and the `extract`.

**The snapshot.** `GET /v1/jobs/{id}/snapshot` redirects to the stored snapshot: the URL and final URL, the status and headers, `tier: "http"`, timings, and a reference to the stored HTML. Its network log is empty, because nothing else was fetched. `stock` came from the `__NEXT_DATA__` JSON inside that HTML, without a browser.

**The cost.** `meta.credits.breakdown` shows `compute: 1` and the proxy traffic of one document. The same job forced to the browser tier with `options.prefer_tier: "browser"` returns the same `data` and costs `compute: 5`, plus the traffic of the scripts and stylesheets a browser loads (images, fonts and trackers are blocked).

## Pitfalls

- **A precautionary `wait_for`.** It forces the browser tier for the whole job. Use an `expect` on the `goto` instead, which works at this tier.
- **Reading prices that JavaScript fills in.** A server-rendered skeleton (`-`, `Loading`) is classified `placeholder_content` and the job moves to the browser once; give such a field `null_markers` so a genuinely missing value is read as empty.
- **Waiting for a network response.** A `network` condition never holds here. Read the endpoint with a `network` locator, or let the job run at the browser tier.
- **Expecting `visible` to filter hidden elements.** It cannot at this tier; select the element you want with a more specific locator.

## Reference

- [Program options](https://docs.kaab.ist/reference/dsl/options) (`prefer_tier`, `max_tier`, `headers`, `cache`)
- [Conditions](https://docs.kaab.ist/reference/dsl/conditions/visible), [`network`](https://docs.kaab.ist/reference/dsl/conditions/network)
- [Field locators: `json`](https://docs.kaab.ist/reference/dsl/fields/json) and [`network`](https://docs.kaab.ist/reference/dsl/fields/network)
- [Get a job's snapshot](https://docs.kaab.ist/reference/api/jobs/get-job-snapshot)
- [Snapshots](https://docs.kaab.ist/concepts/snapshots)
