# Usage API (https://upapi.io/docs/usage-api)



`GET /api/usage` reports where your organization stands in the current minute,
the current month and — when one is running — the current trial. It is
authenticated with the same API key as the gateway, and reading it costs no
units.

<Tabs groupId="upapi-docs-lang" items="['cURL', 'Node', 'Python']">
  <Tab value="cURL">
    ```bash
    curl https://app.upapi.io/api/usage \
      -H 'x-api-key: upapi_YOUR_KEY'
    ```
  </Tab>

  <Tab value="Node">
    ```ts
    const usage = await fetch('https://app.upapi.io/api/usage', {
      headers: { 'x-api-key': process.env.UPAPI_API_KEY },
    }).then((res) => res.json());
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os, requests

    usage = requests.get(
        'https://app.upapi.io/api/usage',
        headers={'x-api-key': os.environ['UPAPI_API_KEY']},
    ).json()
    ```
  </Tab>
</Tabs>

The endpoint is also reachable through the gateway host, which is convenient if
your client is already configured for `api.upapi.io`:

```bash
curl https://api.upapi.io/usage \
  -H 'x-api-key: upapi_YOUR_KEY'
```

<Callout title="This one is a GET">
  Operations are always `POST`. `/api/usage` is a platform endpoint rather than an operation, and it
  is a `GET`. It accepts an API key only — a browser session will not authenticate it.
</Callout>

## Response [#response]

```json
{
  "plan": "Pro",
  "planSlug": "pro",
  "status": "ACTIVE",
  "rpmLimit": 300,
  "monthlyLimit": 500000,
  "usedThisMinute": 4,
  "usedThisMonth": 128340,
  "remainingMinute": 296,
  "remainingMonth": 371660,
  "units": { "used": 128340, "limit": 500000, "remaining": 371660 },
  "resetAt": "2026-08-06T12:01:00.000Z",
  "hostedFunctions": { "used": 2, "limit": 15 },
  "computeSeconds": { "used": 812, "limit": 50000 }
}
```

| Field                                             | Meaning                                               |
| ------------------------------------------------- | ----------------------------------------------------- |
| `plan`, `planSlug`                                | Display name and slug of the active plan              |
| `status`                                          | Subscription status — `FREE`, `TRIALING`, `ACTIVE`, … |
| `rpmLimit`, `usedThisMinute`, `remainingMinute`   | The per-minute burst window, counted in **requests**  |
| `monthlyLimit`, `usedThisMonth`, `remainingMonth` | The monthly budget, counted in **weighted units**     |
| `units`                                           | The same monthly figures grouped together             |
| `resetAt`                                         | When the current minute window rolls over             |
| `hostedFunctions`                                 | Deployed functions against your plan's allowance      |
| `computeSeconds`                                  | Hosted-function compute consumed this month           |

<Callout type="warn" title="Minutes count requests, months count units">
  `usedThisMinute` is a raw request count. `usedThisMonth` and every `units` figure are weighted. A
  single Instagram call moves the first by 1 and the second by 15 — see [Rate limits](https://upapi.io/docs/rate-limits).
</Callout>

## The trial block [#the-trial-block]

While a trial is live, one extra key appears:

```json
{
  "trial": {
    "unitCap": 50000,
    "unitsUsed": 12800,
    "unitsRemaining": 37200,
    "endsAt": "2026-08-20T09:14:00.000Z"
  }
}
```

When no trial is running, `trial` is **omitted entirely** rather than sent with
zeroes. That is deliberate, so clients can feature-detect:

```ts
const usage = await res.json();

if ('trial' in usage) {
  // On a trial: both the clock and the unit budget apply.
  showTrialBanner(usage.trial.unitsRemaining, usage.trial.endsAt);
}
```

Testing `usage.trial.unitsRemaining > 0` without the guard throws off-trial.
