# Credits (/docs/credits)

What a call costs, how metered calls are held and settled, what is free, and where your balance comes from.



Every call is priced per endpoint in credits. A fixed endpoint always costs the same; a metered endpoint quotes a range and charges what the call actually used. Calls that fail or find nothing cost nothing. Your balance is the same one you use for InsightSocial exports.

## What does a call cost? [#what-does-a-call-cost]

Each endpoint has its own price, listed on its reference page and in the full table at [Endpoint pricing](/docs/endpoint-pricing). There are two kinds.

**Fixed.** One number, charged on every successful call.

| Endpoint                         | Credits |
| -------------------------------- | ------- |
| `GET /v1/instagram/profile`      | 10      |
| `GET /v1/linkedin/profile`       | 50      |
| `GET /v1/tiktok/post/transcript` | 100     |
| `GET /v1/youtube/channel/about`  | 250     |

**Metered.** A range. What you pay depends on how much the call reads, for example how many pages of comments it walked.

| Endpoint                          | Credits  |
| --------------------------------- | -------- |
| `GET /v1/instagram/profile/posts` | 10–130   |
| `GET /v1/tiktok/search`           | 10–370   |
| `GET /v1/linkedin/search/people`  | 100–500  |
| `GET /v1/reddit/profile/comments` | 20–2,000 |

`GET /v1/endpoints` returns the same prices as JSON: `credits` is a number for a fixed endpoint and `{ "min", "max" }` for a metered one. It needs no key and costs nothing.

### Reserve the ceiling, settle the actual [#reserve-the-ceiling-settle-the-actual]

Before a call runs, we hold the most it can cost against your balance: the fixed price, or, on a metered endpoint, the most that call can use with the parameters you sent. A call without `limit` or `include` holds one plain page, not the top of the range; asking for more rows or a join holds more. When the call finishes, we charge what it really used and release the rest. On a metered endpoint `credits_used` is often below the hold.

You never pay more than the ceiling. If you send `max_pages` (see [Pagination](/docs/pagination)), the ceiling is the listed maximum multiplied by `max_pages`, and that product is the most the call can cost.

If your balance cannot cover the ceiling, the call is refused before it starts:

```json title="Response"
{
  "success": false,
  "error": {
    "type": "INSUFFICIENT_CREDITS",
    "message": "This call needs up to 130 credits and 120 remain. Add credits: https://www.insightsocial.app/api/top-up?need=10&u=6f1c2b7e-0d4a-4c8e-9b21-3a5f7d9e1c40&via=api_402",
    "top_up_url": "https://www.insightsocial.app/api/top-up?need=10&u=6f1c2b7e-0d4a-4c8e-9b21-3a5f7d9e1c40&via=api_402"
  },
  "request_id": "req_1a2b3c4d5e6f",
  "credits_used": 0,
  "credits_remaining": 120
}
```

The status is `402` and nothing is charged.

## Why a call cost what it did [#why-a-call-cost-what-it-did]

Every successful response carries `charge_reason`, so you can tell why `credits_used` has the value it has.

| `charge_reason` | What happened                                          | Credits                                      |
| --------------- | ------------------------------------------------------ | -------------------------------------------- |
| `miss`          | We fetched the data for you                            | The endpoint's price (metered: what it used) |
| `shared_cache`  | The answer came from our shared cache (`cached: true`) | 2                                            |
| `replay`        | A retry with the same `Idempotency-Key`                | 0                                            |
| `no_result`     | Nothing was found                                      | 0                                            |
| `dry_run`       | You asked for a price quote with `dry_run=1`           | 0                                            |

Errors are never charged either: every error response has `credits_used: 0`. That includes a request refused with `400 UNSUPPORTED_PARAMETER` because it sent an analysis parameter that schema 2 does not accept; see [Errors](/docs/errors#unsupported_parameter).

### Repeating a call [#repeating-a-call]

Repeating a call is charged like the first time, because a repeat can return newer data. Store responses you need again on your side. A retry with the same `Idempotency-Key` is the exception: it returns the recorded outcome and costs 0 (`replay`). See [Caching](/docs/caching).

### Price a call without running it [#price-a-call-without-running-it]

Add `dry_run=1` to any data endpoint to get our price quote instead of the data. Nothing is fetched and nothing is charged:

```bash title="cURL"
curl "https://api.insightsocial.app/v1/instagram/profile/posts?handle=nasa&dry_run=1" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"
```

```json title="Response"
{
  "success": true,
  "platform": "instagram",
  "endpoint": "/v1/instagram/profile/posts",
  "schema_version": "2",
  "data": {
    "dry_run": { "credits_min": 10, "credits_max": 130 }
  },
  "unavailable": [],
  "credits_used": 0,
  "credits_remaining": 9980,
  "request_id": "req_2ac2c2b769ef",
  "cached": false,
  "idempotent_replay": false,
  "charge_reason": "dry_run",
  "free_call": false
}
```

`credits_min` is the least the call can cost and `credits_max` is what we would hold for those exact parameters, including the `max_pages` you sent. A quote never uses a free call.

### Prices under schema 2 [#prices-under-schema-2]

Prices did not change with schema 2. A few metered endpoints hold less of your balance while they run, because the analysis they used to do, and could be charged for, is gone. The charge is still what the call actually used.

## Free calls [#free-calls]

Every account gets **10 free calls**, once, for its lifetime. A free call is any call that would have been charged (a `miss` or a `shared_cache`) whose ceiling is 100 credits or less. It comes back with `free_call: true` and `credits_used: 0`.

Calls above 100 credits are charged normally even while free calls remain. Empty results, errors and replays of a call we already answered never use one up. The one replay that can use a free call is a replay of a call we never answered successfully, because that replay is charged; see [Production checklist](/docs/production-checklist#make-retries-safe).

## Where your credits come from [#where-your-credits-come-from]

| Source                                               | Credits          | Expires                               |
| ---------------------------------------------------- | ---------------- | ------------------------------------- |
| Free plan                                            | 500 per month    | Resets on the 1st of each month (UTC) |
| Pro plan ($9.99/month, or $7.99/month billed yearly) | 10,000 per month | Resets monthly                        |
| Credit packs (one-time purchase)                     | Pack size        | Never                                 |

The monthly grant is spent first and pack credits second. Referral bonuses are added to your pack credits.

This is **one balance**, shared with InsightSocial exports. Credits you spend on the API reduce what you can export, and the other way round.

Buy a plan or a pack at [insightsocial.app/pricing](https://www.insightsocial.app/pricing).

<Callout type="warn" title="Non-refundable credits">
  API credits are non-refundable. Failed calls, empty results and `dry_run` calls are simply never charged in the first place.
</Callout>

## How do I check my balance? [#how-do-i-check-my-balance]

1. **On every response.** The body has `credits_remaining`, and the `X-Credits-Remaining` header carries the same number. The header is left out when the balance could not be read.
2. **With `GET /v1/credits`.** Needs your key, costs 0 credits.
3. **In the dashboard.** The API section of your portal shows balance, usage and an activity log.

```bash title="cURL"
curl "https://api.insightsocial.app/v1/credits" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"
```

```json title="Response"
{
  "success": true,
  "credits_used": 0,
  "credits_remaining": 9180,
  "plan": { "tier": "pro", "credits_per_month": 10000, "resets_at": "2026-10-01T00:00:00.000Z" },
  "usage": { "window_start": "2026-09-01T00:00:00.000Z", "used": 1320, "used_export": 400, "used_api": 920 },
  "pack_credits": 500,
  "free_calls": { "remaining": 0, "total": 10 },
  "key": { "id": "3f6c2a8e-7b41-4d0f-9a52-1c8e5d7b9f20", "name": "production" },
  "request_id": "req_1a2b3c4d5e6f"
}
```

## Retries with an Idempotency-Key are not charged twice [#retries-with-an-idempotency-key-are-not-charged-twice]

Send an `Idempotency-Key` header and retry with the same value. A replay of a call we already answered comes back with `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. See [Production checklist](/docs/production-checklist#make-retries-safe) for the key format and the edge cases.

## Next steps [#next-steps]

<Cards>
  <Card title="Endpoint pricing" href="/docs/endpoint-pricing">
    The price of every endpoint.
  </Card>

  <Card title="Caching" href="/docs/caching">
    Shared-cache hits and forcing a fresh fetch.
  </Card>

  <Card title="Pagination" href="/docs/pagination">
    What a full walk costs, and how 

    `max_pages`

     changes the ceiling.
  </Card>

  <Card title="Testing your integration" href="/docs/testing-your-integration">
    Checks that cost nothing.
  </Card>
</Cards>

## Endpoints billed per item [#endpoints-billed-per-item]

`/v1/linkedin/profile/all` and `/v1/pinterest/url-stats` take a list in `urls` and are billed per item: the listed price is for one URL, and a call with three URLs costs three times as much. The hold and the `dry_run` quote count the URLs the same way.

