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

# Pricing & Billing

> How Reactor charges for sessions, when billing starts and stops, and how to track usage

export const PricingTable = () => {
  const [data, setData] = useState(null);
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const ctrl = new AbortController();
    fetch("https://api.reactor.inc/pricing", {
      signal: ctrl.signal
    }).then(r => {
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      return r.json();
    }).then(json => {
      setData(json);
      setLoading(false);
    }).catch(err => {
      if (err.name === "AbortError") return;
      setError(true);
      setLoading(false);
    });
    return () => ctrl.abort();
  }, []);
  const MODELS = [{
    name: "helios",
    display: "Helios",
    bestFor: "Interactive long-form real-time video generation"
  }, {
    name: "lingbot",
    display: "LingBot",
    bestFor: "High-fidelity explorable environments"
  }, {
    name: "longlive-v2",
    display: "LongLive-2.0",
    bestFor: "Multi-shot storyboard video generation"
  }];
  if (error) {
    return <div className="not-prose my-6 p-4 border border-zinc-950/10 dark:border-white/10 rounded-xl text-sm text-zinc-950/70 dark:text-white/70">
        Couldn't load current pricing. See your{" "}
        <a href="https://reactor.inc/dashboard" className="underline text-zinc-950 dark:text-white">
          Dashboard
        </a>{" "}
        for live rates.
      </div>;
  }
  const creditsPerDollar = data?.settings?.credits_per_dollar;
  const formatPerSec = creditsPerSec => `$${(creditsPerSec / creditsPerDollar).toFixed(4)}`;
  const formatPerHour = creditsPerSec => `$${Math.round(creditsPerSec / creditsPerDollar * 3600)}`;
  const Skeleton = () => <span aria-label="loading" className="inline-block h-4 w-14 rounded bg-zinc-950/10 dark:bg-white/10 animate-pulse align-middle" />;
  const Dash = () => <span aria-label="not available" className="text-zinc-950/40 dark:text-white/40">
      —
    </span>;
  return <div className="not-prose my-6 overflow-x-auto">
      <table className="w-full text-sm border-collapse">
      <thead>
        <tr className="border-b border-zinc-950/10 dark:border-white/10">
          <th className="text-left font-semibold py-2 pl-4 pr-4 text-zinc-950 dark:text-white">
            Model
          </th>
          <th className="text-left font-semibold py-2 pr-4 text-zinc-950 dark:text-white">
            ID
          </th>
          <th className="text-left font-semibold py-2 pr-4 text-zinc-950 dark:text-white">
            Per second
          </th>
          <th className="text-left font-semibold py-2 pr-4 text-zinc-950 dark:text-white">
            Per hour
          </th>
          <th className="text-left font-semibold py-2 text-zinc-950 dark:text-white">
            Best for
          </th>
        </tr>
      </thead>
      <tbody>
        {MODELS.map(({name, display, bestFor}) => {
    const apiRow = data?.models?.find(m => m.name === name);
    const amountPerSec = apiRow?.rate?.amount_per_sec;
    const canRender = !loading && creditsPerDollar && typeof amountPerSec === "number";
    return <tr key={name} className="border-b border-zinc-950/5 dark:border-white/5">
              <td className="py-2 pl-4 pr-4 font-semibold text-zinc-950 dark:text-white">
                {display}
              </td>
              <td className="py-2 pr-4">
                <code>{name}</code>
              </td>
              <td className="py-2 pr-4">
                {loading ? <Skeleton /> : canRender ? <code>{formatPerSec(amountPerSec)}</code> : <Dash />}
              </td>
              <td className="py-2 pr-4">
                {loading ? <Skeleton /> : canRender ? <code>{formatPerHour(amountPerSec)}</code> : <Dash />}
              </td>
              <td className="py-2 text-zinc-950/80 dark:text-white/80">
                {bestFor}
              </td>
            </tr>;
  })}
      </tbody>
      </table>
    </div>;
};

Reactor uses **pay-as-you-go pricing**. You only pay for the time your session is actively holding a
GPU. There is no subscription, minimum spend, or seat fee.

<Info>
  New accounts receive free credits so you can try Reactor before adding a payment method. See the
  **[Dashboard](https://reactor.inc/dashboard)** for your current balance.
</Info>

***

## How pricing works

Reactor is a real-time streaming platform, so billing is **per session-second**. While your session
is connected to a GPU, you are billed for every second of wall-clock time, regardless of whether you
are actively sending commands or receiving media. Prices below are in US dollars; your
[Dashboard](https://reactor.inc/dashboard) balance is shown in credits.

<Warning>
  **You pay for the GPU, not the generation.** Once a session reaches `ready`, the meter runs for
  every second the GPU is held for you, even if you are idle and not sending commands or receiving
  media. The same applies to [recoverable disconnects](/concepts/sessions#disconnecting): the GPU
  stays reserved on your behalf, so billing continues until the session is terminated.
</Warning>

<Note>
  Billable metrics are subject to change.
</Note>

***

## Models

<PricingTable />

***

## Buying credits

Your balance is held in **credits**, which you buy up front and draw down as your sessions run.

| Detail                  | Value                                      |
| ----------------------- | ------------------------------------------ |
| Exchange rate           | 10,000 credits per US dollar               |
| Minimum purchase        | \$1                                        |
| Maximum per transaction | \$1,000                                    |
| Maximum balance         | Up to \$10,000 of credits held at one time |

Credits expire based on how they were added to your account:

* **Promotional credits** (including the free signup credits) expire **90 days** after they are issued.
* **Purchased credits** expire **365 days** after they are issued.

***

## When billing starts and stops

Sessions move through a [four-state lifecycle](/concepts/sessions#connection-lifecycle). Billing
follows the lifecycle precisely:

| State          | Billed? | Why                                                               |
| -------------- | :-----: | ----------------------------------------------------------------- |
| `disconnected` |    No   | No session exists                                                 |
| `connecting`   |    No   | Negotiating with Reactor; no GPU has been assigned yet            |
| `waiting`      |    No   | Reactor is allocating a GPU on your behalf                        |
| `ready`        |   Yes   | A GPU is dedicated to your session and is producing media for you |

The meter starts the moment your session reaches `ready` and runs until the session is
**terminated**, not until your network connection drops.

In a [shared session](/concepts/sessions#multiple-connections-per-session), only the session
creator's termination stops the meter. A client that adopted the session and then disconnects does
not stop billing; the session keeps running for its owner.

<Frame>
  <img src="https://mintcdn.com/reactortechnologiesinc/oCeFlbp0H3XO2l0l/diagrams/billing-lifecycle.svg?fit=max&auto=format&n=oCeFlbp0H3XO2l0l&q=85&s=5e637892f4bc05c82d8c9156bbbbb275" alt="Billing lifecycle: connecting and waiting are free; the meter starts at ready and keeps running through a recoverable disconnect while the GPU stays reserved, stopping only when the session terminates" width="664" height="144" data-path="diagrams/billing-lifecycle.svg" />
</Frame>

### Recoverable disconnects

If you disconnect with `recoverable=true`, the GPU keeps your session alive on the server so you can
reconnect later. Because the GPU is held for you, **a recoverable session continues to bill while it
waits for you to reconnect**.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  // Releases the GPU immediately. Billing stops.
  await reactor.disconnect();

  // Holds the GPU for reconnection. Billing continues.
  await reactor.disconnect(true);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  # Releases the GPU immediately. Billing stops.
  await reactor.disconnect()

  # Holds the GPU for reconnection. Billing continues.
  await reactor.disconnect(recoverable=True)
  ```
</CodeGroup>

<Tip>
  Use `recoverable=true` only when you actually expect the user to come back within seconds. For
  everyday "the user closed the tab" cases, prefer the default disconnect so you stop paying
  immediately.
</Tip>

***

## Minimizing cost

A few patterns that materially reduce spend:

* **Disconnect when idle.** A connected session with no commands flowing still bills. Tear it down
  as soon as the user navigates away or finishes their task.
* **Default to non-recoverable disconnects.** Recoverable sessions are useful, but they keep a GPU
  reserved (and metered) until you reconnect or the session times out.
* **Don't pre-warm sessions speculatively.** Time spent in `connecting` and `waiting` is free, so
  there is no benefit to opening sessions before the user is ready to interact.
* **Cap session length on the client.** For interactive demos, set a hard timeout (e.g. five
  minutes) so a user who walks away from their browser does not run up the bill.

***

## Usage tracking

<Info>
  **Coming soon.** Programmatic usage and billing endpoints are in development. For now, check the
  **[Dashboard](https://reactor.inc/dashboard)** for your current balance and spend.
</Info>

***

## Fetch pricing programmatically

Reactor exposes a public, read-only endpoint that returns the current pricing economics and, where
available, the catalog of publicly-priced models. Use it to render up-to-date pricing on a marketing
page, drive an in-app billing flow, or replace hard-coded numbers in client code.

Send a `GET` request to `https://api.reactor.inc/pricing`. No authentication is required.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  curl -i https://api.reactor.inc/pricing
  ```

  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const res = await fetch("https://api.reactor.inc/pricing");
  const pricing = await res.json();

  const creditsPerDollar = pricing.settings.credits_per_dollar;
  const helios = pricing.models?.find((m) => m.name === "helios");
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import httpx

  pricing = httpx.get("https://api.reactor.inc/pricing").json()

  credits_per_dollar = pricing["settings"]["credits_per_dollar"]
  helios = next((m for m in pricing.get("models", []) if m["name"] == "helios"), None)
  ```
</CodeGroup>

### Response

`200 OK`

```json theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
{
  "settings": {
    "credits_per_dollar": 10000,
    "purchase": {
      "min_dollars": 1,
      "max_dollars": 1000
    },
    "auto_topup": {
      "min_dollars": 5
    },
    "max_account_credits": 0
  },
  "models": [
    {
      "id": "f7d3a8b2-1c4e-4d2a-9b6f-0d8e3c1b5a47",
      "name": "helios",
      "rate": {
        "amount_per_sec": 17,
        "unit": "credits",
        "denomination": "second"
      }
    }
  ]
}
```

| Field                                       | Type          | Always present | Description                                                                                                                                                 |
| ------------------------------------------- | ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `settings.credits_per_dollar`               | integer       | yes            | Credits awarded per US dollar in self-serve purchases.                                                                                                      |
| `settings.purchase.min_dollars`             | integer       | yes            | Minimum self-serve top-up amount, in whole dollars.                                                                                                         |
| `settings.purchase.max_dollars`             | integer       | yes            | Maximum self-serve top-up amount, in whole dollars.                                                                                                         |
| `settings.auto_topup.min_dollars`           | integer       | yes            | Minimum auto top-up amount, in whole dollars.                                                                                                               |
| `settings.auto_topup.max_threshold_credits` | integer       | no             | Highest balance threshold (in credits) an auto top-up can be set at. Omitted when `max_account_credits` is `0` (no cap).                                    |
| `settings.max_account_credits`              | integer       | yes            | On-ledger credit-balance ceiling for an account, in credits. `0` means no cap is enforced.                                                                  |
| `models`                                    | array         | no             | Public-model catalog. May be absent during initial rollout (see note below). When present, an empty array means no publicly-priced models are listed today. |
| `models[].id`                               | string (UUID) | when present   | Model UUID.                                                                                                                                                 |
| `models[].name`                             | string        | when present   | Model name (e.g. `"helios"`, `"lingbot"`).                                                                                                                  |
| `models[].rate.amount_per_sec`              | integer       | when present   | Cost rate, expressed in `unit` per `denomination`. Today, credits per second.                                                                               |
| `models[].rate.unit`                        | string        | when present   | Currently `"credits"`.                                                                                                                                      |
| `models[].rate.denomination`                | string        | when present   | Currently `"second"`.                                                                                                                                       |

<Note>
  The `models` field may be omitted from responses while the public catalog is rolling out. Treat an
  absent field and an empty array as semantically equivalent in your application logic. Once the
  rollout is complete the field will always be present.
</Note>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Sessions" icon="activity" href="/concepts/sessions">
    Understand the connection lifecycle that billing is anchored to.
  </Card>

  <Card title="Dashboard" icon="gauge" href="https://reactor.inc/dashboard">
    Check your usage, free credits, and current spend.
  </Card>

  <Card title="Rate Limits" icon="shield" href="/resources/rate-limits">
    Concurrent session limits, token TTL, and error handling.
  </Card>
</CardGroup>
