# Credits & limits

What each request costs, what is free, the limits, and every error code with what to do.

## Credits

New accounts get 100 free credits, no card. After that, buy a pack in the [dashboard](https://cleanscript.ai/dashboard/billing); credits are valid for 12 months and add to your balance.

| Pack | Credits | Price | Per credit |
| --- | --- | --- | --- |
| Starter | 1,000 | $10 | 1¢ |
| Builder | 5,000 | $40 | 0.8¢ |
| Pro | 25,000 | $150 | 0.6¢ |

## What a request costs

- **Transcript:** 1 credit per started 10 minutes of video: an 11-minute video is 2 credits.
- **Fields** (`/v1/extract`): 3 credits per started 10 minutes, the transcript included.
- **Posts without captions:** transcribed for 1 credit per started minute instead: a 45-second Reel is 1 credit.
- **A translation the platform doesn't provide** (into `language`): 1 credit more per started 10 minutes.
- **On-screen text** alongside speech: 1 credit more. For a post without speech it is the transcript.
- **Creator posts** (`/v1/posts`): 1 credit per call.
- **Account** (`/v1/account`): free.

| Post | Transcript | Transcript and fields |
| --- | --- | --- |
| A 45-second TikTok or Reel, with or without captions | 1 credit | 3 credits |
| A 10-minute YouTube video | 1 credit | 3 credits |
| A 15-minute video without captions | 15 credits | 19 credits |
| A 1-hour podcast | 6 credits | 18 credits |
| A creator's post list (`/v1/posts`) | 1 credit | |

Free, always: any request that fails. Asking again for the same thing within 6 hours is free. If you got this post's transcript in that time, asking for its fields costs only the fields. Asking again after a result with an `incomplete` or `uncorrected` warning runs the request again in full and is charged again. Every response says what it used in `X-Credits-Used` and what is left in `X-Credits-Remaining`. With no credits left, every request except `/v1/account` is `insufficient_credits`, even a free repeat.

## Limits

- **Requests a minute:** set per account, 10 for new accounts; yours is `rate_limit_per_minute` in `GET /v1/account`. Over it you get `rate_limited` with `Retry-After`. Need more? Write to [mohamed@messaad.dev](mailto:mohamed@messaad.dev).
- **Unavailable posts and profiles:** 30 an hour. Requests that end in `video_unavailable`, `author_not_found` or `source_unavailable` are free, but past 30 in an hour you get `rate_limited` until the hour is up. Asking again within 30 minutes for a post or profile that doesn't exist returns the same 404 and doesn't count.
- **Post length:** up to 2 hours. Posts without captions are transcribed up to 15 minutes.
- **Time:** a request answers within 120 seconds, or returns `timeout`.
- **Size:** request body up to 128 KB, schema up to 32 KB, response up to 8 MB.
- **Creator posts:** up to 30 per call on YouTube (videos, not Shorts), 35 on TikTok, 12 on Instagram.

## Errors

Every error has the same shape. `code` is stable; `message` says what happened in words you can show; `param` names the field at fault; `details` carries data you can act on. Quote `request_id` when you contact us.

```json Error 402
{
  "error": {
    "code": "insufficient_credits",
    "message": "This request needs 1 credit and the account has 0 left. Buy more at https://cleanscript.ai/pricing.",
    "details": {
      "credits_remaining": 0,
      "credits_required": 1,
      "top_up_url": "https://cleanscript.ai/pricing"
    }
  },
  "request_id": "5f0c7d1e-0b7a-4a39-9c1e-3c1f2b8f6a10"
}
```

| Code | Status | What to do |
| --- | --- | --- |
| `invalid_request` | 400 | Fix the field named in `param`; the message says how. Also 404 or 405 for an unknown route or method, and 413 for a body over 128 KB or a result over 8 MB. |
| `invalid_url` | 400 | Send a link to one post: a YouTube watch, Shorts or `youtu.be` link, a TikTok video link, an Instagram `/p/` or `/reel/` link. |
| `unsupported_platform` | 400 | Only YouTube, TikTok and Instagram posts work. |
| `invalid_schema` | 400 | Fix the JSON Schema; the message names the problem. |
| `unauthorized` | 401 | Send `Authorization: Bearer <key>` with a key from the [dashboard](https://cleanscript.ai/dashboard/api-keys) that isn't revoked. |
| `insufficient_credits` | 402 | Top up at `details.top_up_url`. `details` also has `credits_required` and `credits_remaining`. |
| `video_unavailable` | 404 | `details.reason` is `private`, `removed`, `age_restricted` or `live` on YouTube, and `unavailable` on TikTok and Instagram. A live stream works once it has ended. |
| `author_not_found` | 404 | Check the profile link or handle; `details.reason` is `not_found` or `private`. |
| `idempotency_conflict` | 409 | The `Idempotency-Key` was used for a different request (use a new key), or the first request is still running (retry after `Retry-After`). |
| `no_captions` | 422 | The post has no captions and can't be transcribed: it is over 15 minutes, or its audio isn't available. |
| `language_unavailable` | 422 | Choose a language from `details.available`, or leave `language` out. |
| `video_too_long` | 422 | The post is over 2 hours. |
| `rate_limited` | 429 | Wait for `Retry-After` seconds, then retry. |
| `source_unavailable` | 503 | The platform didn't answer. Retry after `Retry-After`. |
| `processing_failed` | 503 | Something failed on our side. Retry after `Retry-After`. |
| `timeout` | 504 | Retry; the result is often ready by then. |
