> ## Documentation Index
> Fetch the complete documentation index at: https://kernel.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Pricing & Limits

export const PricingCalculator = () => {
  const defaults = {
    plan: 'free',
    browserType: 'headful',
    avgSessionLength: 30,
    numSessions: 100
  };
  const planPrices = {
    free: 0,
    hobbyist: 30,
    startup: 200
  };
  const usagePrices = 0.0000166667;
  const browserMultipliers = {
    headless: 1,
    headful: 8,
    gpu: 48
  };
  const [plan, setPlan] = useState(defaults.plan);
  const [browserType, setBrowserType] = useState(defaults.browserType);
  const [avgSessionLength, setAvgSessionLength] = useState(defaults.avgSessionLength);
  const [numSessions, setNumSessions] = useState(defaults.numSessions);
  const [flash, setFlash] = useState(false);
  const prevPriceRef = useRef(null);
  const hasInteracted = useRef(false);
  useEffect(() => {
    if (!hasInteracted.current) return;
    var url = new URL(window.location);
    url.searchParams.set('plan', plan);
    url.searchParams.set('browserType', browserType);
    url.searchParams.set('duration', avgSessionLength);
    url.searchParams.set('sessions', numSessions);
    url.hash = 'pricing-calculator';
    window.history.replaceState(null, '', url);
  }, [plan, browserType, avgSessionLength, numSessions]);
  const handleBrowserTypeChange = type => {
    hasInteracted.current = true;
    setBrowserType(type);
    if (type === 'gpu' && plan !== 'startup') {
      setPlan('startup');
    }
  };
  const handlePlanChange = newPlan => {
    hasInteracted.current = true;
    if (browserType === 'gpu' && newPlan !== 'startup') {
      return;
    }
    setPlan(newPlan);
  };
  var price = planPrices[plan];
  var multiplier = browserMultipliers[browserType];
  var usageCost = usagePrices * multiplier * numSessions * avgSessionLength;
  var includedUsageCredits = 5;
  if (plan === 'hobbyist') {
    includedUsageCredits = 10;
  } else if (plan === 'startup') {
    includedUsageCredits = 50;
  }
  if (usageCost > includedUsageCredits) {
    price += Math.max(0, usageCost - includedUsageCredits);
  }
  useEffect(() => {
    var prev = prevPriceRef.current;
    if (prev !== null && (prev.usageCost !== usageCost || prev.includedUsageCredits !== includedUsageCredits || prev.price !== price)) {
      setFlash(true);
      var t = setTimeout(() => setFlash(false), 300);
      return () => clearTimeout(t);
    }
    prevPriceRef.current = {
      usageCost,
      includedUsageCredits,
      price
    };
  }, [usageCost, includedUsageCredits, price]);
  const labelStyle = {
    fontWeight: 600,
    fontSize: '0.875rem',
    minWidth: '10rem',
    flexShrink: 0,
    maxWidth: '10rem'
  };
  const rowStyle = {
    display: 'flex',
    alignItems: 'center',
    gap: '0.5rem',
    minHeight: '2.25rem'
  };
  const inputStyle = {
    minWidth: 0,
    flex: 1,
    maxWidth: '100%',
    boxSizing: 'border-box',
    background: 'transparent'
  };
  const selectStyle = {
    ...inputStyle,
    appearance: 'none',
    backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23374151'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E")`,
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'right 0.5rem center',
    backgroundSize: '0.75rem',
    paddingRight: '1.5rem'
  };
  const btnStyle = active => ({
    padding: '0.25rem 0.5rem',
    borderRadius: '0.375rem',
    border: '1px solid var(--btn-border)',
    fontSize: '0.875rem',
    background: active ? 'var(--btn-selected-bg)' : undefined
  });
  return <Columns cols={2}>
            <Card title="Controls" icon="calculator">
                <div style={rowStyle}>
                    <label style={labelStyle}>Plan</label>
                    <select style={selectStyle} value={plan} onChange={e => handlePlanChange(e.target.value)}>
                        <option value="free">Free</option>
                        <option value="hobbyist">Hobbyist</option>
                        <option value="startup">Startup</option>
                    </select>
                </div>
                <div style={rowStyle}>
                    <label style={labelStyle}>Session length (seconds)</label>
                    <input type="number" style={{
    ...inputStyle
  }} value={avgSessionLength} onChange={e => {
    hasInteracted.current = true;
    setAvgSessionLength(parseInt(e.target.value));
  }} />
                </div>
                <div style={rowStyle}>
                    <label style={labelStyle}>Number of sessions</label>
                    <input type="number" style={{
    ...inputStyle
  }} value={numSessions} onChange={e => {
    hasInteracted.current = true;
    setNumSessions(parseInt(e.target.value));
  }} />
                </div>
                <div style={rowStyle}>
                    <button class="btn btn-primary dark:text-white" style={btnStyle(browserType === 'headful')} onClick={() => handleBrowserTypeChange('headful')}>Headful</button>
                    <button class="btn btn-primary dark:text-white" style={btnStyle(browserType === 'headless')} onClick={() => handleBrowserTypeChange('headless')}>Headless</button>
                    <button class="btn btn-primary dark:text-white" style={btnStyle(browserType === 'gpu')} onClick={() => handleBrowserTypeChange('gpu')}>Headful + GPU</button>
                </div>
                <div style={rowStyle}>
                    <span style={{
    width: '100%',
    fontSize: '0.8rem',
    fontStyle: 'italic'
  }}>
                        ${(usagePrices * multiplier).toFixed(8)}/second
                        {browserType === 'gpu' && <span style={{
    marginLeft: '0.5rem'
  }}>(Startup tier required)</span>}
                    </span>
                </div>
            </Card>
            <Card title="Price" icon="circle-dollar">
                <div style={rowStyle}><span style={labelStyle}>Base plan:</span> <span style={{
    background: flash ? '#CAB168' : 'transparent',
    transition: 'background 0.5s ease',
    marginLeft: 'auto'
  }}>${planPrices[plan].toFixed(2)}</span></div>
                <div style={rowStyle}><span style={labelStyle}>Usage:</span> <span style={{
    background: flash ? '#CAB168' : 'transparent',
    transition: 'background 0.5s ease',
    marginLeft: 'auto'
  }}>+${usageCost.toFixed(2)}</span></div>
                <div style={rowStyle}><span style={labelStyle}>Free credits:</span> <span style={{
    background: flash ? '#CAB168' : 'transparent',
    transition: 'background 0.5s ease',
    marginLeft: 'auto'
  }}>-${includedUsageCredits.toFixed(2)}</span></div>
                <div style={rowStyle}><span style={labelStyle}>Total cost:</span> <span style={{
    background: flash ? '#CAB168' : 'transparent',
    transition: 'background 0.5s ease',
    marginLeft: 'auto'
  }}>${price.toFixed(2)}</span></div>
            </Card>
        </Columns>;
};

With Kernel, you only pay for what you use and nothing more. You don't pay for idle time thanks to [Standby Mode](/docs/browsers/standby), idle browsers in a browser pool incur no usage charges, and you're never charged for proxies.

## Plan Pricing

| Plan       | Monthly cost  | Included Credits / mo |
| ---------- | ------------- | --------------------- |
| Developer  | Free + usage  | \$5                   |
| Hobbyist   | \$30 + usage  | \$10                  |
| Start-Up   | \$200 + usage | \$50                  |
| Enterprise | Custom        | Custom                |

## Usage Rates

| Browser type               | Price (\$/sec) |
| -------------------------- | -------------- |
| Headful                    | 0.0001333336   |
| Headless                   | 0.0000166667   |
| Headful + GPU acceleration | 0.0008000016   |

> Included monthly credits apply to usage costs only.

### Pricing calculator

<PricingCalculator />

## Managed infrastructure

| Feature                           | Developer | Hobbyist  | Start-Up  | Enterprise   |
| --------------------------------- | --------- | --------- | --------- | ------------ |
| Browser live view                 | ✅         | ✅         | ✅         | ✅            |
| Extended browser timeouts         | ✅         | ✅         | ✅         | ✅            |
| Browser live view                 | ✅         | ✅         | ✅         | ✅            |
| Configurable browser viewports    | ✅         | ✅         | ✅         | ✅            |
| Managed stealth mode              | ✅         | ✅         | ✅         | ✅            |
| Computer controls API             | ✅         | ✅         | ✅         | ✅            |
| Browser pools                     | ✅         | ✅         | ✅         | ✅            |
| SOC2 compliance                   | ✅         | ✅         | ✅         | ✅            |
| Browser replays                   | 1 day     | 7 days    | 30 days   | Custom       |
| Managed auth connections          | 3         | unlimited | unlimited | unlimited    |
| Custom browser extensions         | 1         | 1         | unlimited | unlimited    |
| Projects                          | 1         | 1         | unlimited | unlimited    |
| Support                           | Discord   | Email     | Email     | Shared Slack |
| Configurable & BYO proxies        | ❌         | ❌         | ✅         | ✅            |
| GPU acceleration                  | ❌         | ❌         | ✅         | ✅            |
| Audit log search & export         | ❌         | ❌         | ✅         | ✅            |
| Continuous audit log export to S3 | ❌         | ❌         | ❌         | ✅            |
| HIPAA compliance (BAA)            | ❌         | ❌         | ❌         | ✅            |

## Concurrency limits

Kernel enforces a single concurrency limit covering all browsers you run at once—whether created on demand with `browsers.create()` or reserved in a [browser pool](/docs/browsers/pools/overview). Your full limit is available to either API in any mix.

| Feature                            | Developer       | Hobbyist       | Start-Up           | Enterprise |
| ---------------------------------- | --------------- | -------------- | ------------------ | ---------- |
| Concurrent browsers                | 5               | 10             | 150                | Custom     |
| App invocations                    | 5               | 10             | 50                 | Custom     |
| App invocations (per-app)          | 5               | 10             | 20                 | Custom     |
| Managed auth health check interval | 6 hours minimum | 1 hour minimum | 20 minutes minimum | Custom     |

#### Notes

* Reserved capacity in a [browser pool](/docs/browsers/pools/overview) counts toward your concurrency limit whether or not the browsers are currently acquired—a pool sized to 40 browsers uses 40 of your limit.
* Browsers in [Standby Mode](/docs/browsers/standby) count against your concurrency limit.
* Limits are org-wide by default unless stated otherwise.

## Rate limiting

Kernel enforces per-organization rate limits on API requests. When you exceed the rate limit, the API returns a `429 Too Many Requests` response with a `Retry-After` header indicating how many seconds to wait before retrying.

Rate-limited endpoints include these headers on every response:

| Header                  | Description                                               |
| ----------------------- | --------------------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed per minute                       |
| `X-RateLimit-Remaining` | Requests remaining in the current window                  |
| `Retry-After`           | Seconds to wait before retrying (only on `429` responses) |

All Kernel SDKs automatically retry `429` responses up to 2 times, respecting the `Retry-After` header for delay timing. If retries are exhausted, the SDK throws a typed `RateLimitError` with the response headers accessible for custom backoff logic.

If you need higher rate limits, [contact us](https://calendly.com/d/d3tn-5kp-5yt).

## FAQ

<Accordion title="how can i control my team's usage spending?">
  see this guide on [spending controls](/docs/info/spending-caps).
</Accordion>

<Accordion title="How does billing work across a browser's lifecycle? Am I charged for the full timeout_seconds, or only while it's actively in use?">
  Only for active runtime. `timeout_seconds` sets an idle auto-delete ceiling, not a billing window. Once a browser goes idle — 5 seconds after the last CDP or Live View activity — it enters Standby Mode and stops accruing usage cost, even if it stays alive until the timeout is reached. Deleting a browser early doesn't lower cost any further (idle time is already free), but it does free up your concurrency slot sooner.
</Accordion>

<Accordion title="How are browser pools charged?">
  you pay the standard usage-based price per GB-second while browsers are running. Idle browsers in a pool incur no disk charges—you only pay when a browser is actively in use.

  Note: A browser pool counts toward your concurrency limit whether or not its browsers are currently acquired — a browser pool sized to 40 browsers uses 40 of your limit. Browser pools are available on Start-Up and Enterprise plans.
</Accordion>

<Accordion title="How is Managed Auth charged?">
  Managed Auth is included on all plans with no per-connection fees. Under the hood, it uses browser sessions to log in and keep your sessions fresh—these count toward your browser usage and concurrency like any other browser session.

  Auth sessions are fast (typically 5-30 seconds each). Kernel monitors session health and re-authenticates automatically when sessions expire—most stay valid for days. For example, keeping 100 auth connections logged in typically costs less than \$5/month in browser usage.
</Accordion>
