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

# Get credit balance

> Read your current credit balance and plan. Does not consume a credit.

```http theme={null}
GET https://api.transcriptmagic.com/api/balance
```

A read-only check that returns your current credit balance and plan. **No credit is charged.** Use it to validate a freshly-issued key, surface remaining credits in your UI, or trigger a top-up flow before a batch job. Rate limit still applies — 120 requests per minute per key.

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.transcriptmagic.com/api/balance \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.transcriptmagic.com/api/balance",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
  )

  data = response.json()
  print(f"Credits remaining: {data['credits']} (plan: {data['plan']})")
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.transcriptmagic.com/api/balance", {
    headers: { "Authorization": "Bearer YOUR_API_KEY" },
  });

  const data = await response.json();
  console.log(`Credits remaining: ${data.credits} (plan: ${data.plan})`);
  ```

  ```go Go theme={null}
  package main

  import (
  	"encoding/json"
  	"fmt"
  	"net/http"
  )

  func main() {
  	req, _ := http.NewRequest("GET",
  		"https://api.transcriptmagic.com/api/balance", nil)
  	req.Header.Set("Authorization", "Bearer YOUR_API_KEY")

  	resp, _ := http.DefaultClient.Do(req)
  	defer resp.Body.Close()

  	var data struct {
  		Credits int    `json:"credits"`
  		Plan    string `json:"plan"`
  	}
  	json.NewDecoder(resp.Body).Decode(&data)
  	fmt.Printf("Credits remaining: %d (plan: %s)\n", data.Credits, data.Plan)
  }
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://api.transcriptmagic.com/api/balance');

  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer YOUR_API_KEY',
  ]);

  $response = curl_exec($ch);
  curl_close($ch);

  $data = json_decode($response, true);
  echo "Credits remaining: {$data['credits']} (plan: {$data['plan']})\n";
  ```
</CodeGroup>

## Response

```json theme={null}
{
  "credits": 487,
  "plan": "credits"
}
```

| Field     | Type    | Description                                                                                                                      |
| --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `credits` | integer | Remaining credits on the account. `0` means out of credits — transcript endpoints will return `403 no_credits` until you top up. |
| `plan`    | string  | One of `"free"`, `"credits"`, or a subscription plan slug (e.g. `"plus"`, `"pro"`).                                              |

### Response headers

Every response carries the standard rate-limit headers:

| Header                  | Description                                    |
| ----------------------- | ---------------------------------------------- |
| `X-RateLimit-Limit`     | Always `120`.                                  |
| `X-RateLimit-Remaining` | Requests left in the current 60-second window. |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets.         |

## Errors

| Status | Body                                                   | When                                                                   |
| ------ | ------------------------------------------------------ | ---------------------------------------------------------------------- |
| `401`  | `{ "error": "Missing API key" }`                       | No `Authorization` header, or it doesn't start with `Bearer sk_live_`. |
| `401`  | `{ "error": "Invalid API key" }`                       | The key was revoked or never existed.                                  |
| `429`  | `{ "error": "Rate limit exceeded. Try again later." }` | 120 requests/minute exceeded. Honor the `Retry-After` header.          |

Unlike the transcript endpoints, `/api/balance` does **not** return `403 no_credits` for empty accounts — it is the canonical way to check whether you should top up before issuing a transcript request.

## Common patterns

<AccordionGroup>
  <Accordion title="Pre-flight check before a batch job">
    Call `/api/balance` once at the start of a batch of 1,000 URLs. If `credits < 1000`, fail fast rather than burning through credits mid-run and getting partial results.
  </Accordion>

  <Accordion title="Validate a key in CI">
    Use `/api/balance` as your smoke test in deploy pipelines — a `200` confirms the key is valid and points at a real account. Cheaper and faster than POSTing a real video URL.
  </Accordion>

  <Accordion title="Surface remaining credits in your UI">
    Cache the response for \~30 seconds in your app shell. Refresh after every successful transcript call (which already returns `credits` in its response body) so users see the deduction immediately.
  </Accordion>
</AccordionGroup>
