---
title: "List the credit movements | GPT-Video API reference"
description: "Lists every movement of the spendable credit balance and of the API bonus, newest first: charges, refunds, top-ups and corrections."
url: "https://www.gpt-video.com/developers/api/list-ledger-entries"
operation: "list_ledger_entries"
method: "GET"
path: "/v1/credits/ledger"
scope: "clips:read"
---

Do not assume endpoints have parameters other than the ones explicitly specified.

# List the credit movements

Operation id `list_ledger_entries`. Request `GET /v1/credits/ledger`.

Lists every movement of the spendable credit balance and of the API bonus, newest first: charges, refunds, top-ups and corrections.

Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.

## About

Use this to explain the balance that get_credits (`GET /v1/credits`) shows: what was charged and for which project, what came back, what was bought. Movements come newest first; send nextCursor as cursor to read older ones. Filter with period to read one calendar month; top-ups and their reversals belong to no month, so a period filter leaves them out. A movement the API bonus paid has credits 0, and fromBonus says how much the bonus moved; receiving the bonus is not a movement. The history starts when the ledger was turned on, and opening_balance movements sum up what came before. The balance that counts is the one of get_credits.

## Example use

An agency that gives each client its own API key reads the credit movements at the end of each week. It adds up the charges and refunds of each keyId and puts the total in the weekly report of that client.

## Endpoint

```text
GET https://api.gpt-video.com/v1/credits/ledger
```

- Scope `clips:read`.
- Rate limit 120 requests a minute for each key and 240 for each account, shared by every `read` call. See [rate limits](/developers/rate-limits.md).
- No `Idempotency-Key`.

## Parameters

### Query

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | Optional | How many items to return, from 1 to 100. Defaults to 20. From 1 to 100. Default `20`. |
| `cursor` | string | Optional | The nextCursor value from the previous page. Leave it out to get the first page. From 1 to 512 characters. |
| `period` | string | Optional | Only the movements that count on this calendar month, as YYYY-MM. Movements of the top-up alone (a top-up, its reversal, its opening balance) never match, because they belong to no month. Matches `^\d{4}-(0[1-9]\|1[0-2])$`. |

## Request

List the latest credit movements.

```bash title="curl"
curl "https://api.gpt-video.com/v1/credits/ledger?limit=10" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY"
```

```ts title="TypeScript"
const res = await fetch("https://api.gpt-video.com/v1/credits/ledger?limit=10", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
  },
});
const data = await res.json();
console.log(data);
```

```python title="Python"
import os

import requests

r = requests.get(
    "https://api.gpt-video.com/v1/credits/ledger?limit=10",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
print(data)
```

```json title="MCP (Full)"
{
  "method": "tools/call",
  "params": {
    "name": "list_ledger_entries",
    "arguments": {
      "limit": 10
    }
  }
}
```

## Response

A successful call answers HTTP 200.

### Fields to check

| Name | Type | Description |
| --- | --- | --- |
| `data[].id` | string | The id of the movement. It starts with lgr_. |
| `hasMore` | boolean | true when there are more items after this page. |
| `nextCursor` | string or null | Send it as cursor to get the next page. It is null on the last page. |

### Response example

```json title="HTTP 200"
{
  "data": [
    {
      "id": "lgr_9b1e4c7a2d5f40e8a316",
      "kind": "charge",
      "operation": "analysis",
      "credits": -30,
      "fromPlan": -30,
      "fromTopUp": 0,
      "fromBonus": 0,
      "projectId": "prj_7c2e9a4f1b3d4e8096a5",
      "source": "api",
      "keyId": "mck_5f2c9e1a7b3d8046",
      "period": "2026-09",
      "createdAt": "2026-09-01T09:10:00.000Z"
    },
    {
      "id": "lgr_2c8f0a6e4b1d47c9e5a0",
      "kind": "top_up",
      "operation": null,
      "credits": 250,
      "fromPlan": 0,
      "fromTopUp": 250,
      "fromBonus": 0,
      "projectId": null,
      "source": "system",
      "keyId": null,
      "period": null,
      "createdAt": "2026-09-01T08:30:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**All response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | array of objects | Required | The items of this page. |
| `data[].id` | string | Required | The id of the movement. It starts with lgr_. |
| `data[].kind` | string | Required | What moved the balance: charge spent credits; refund gave credits back for work that was not done or an action you undid; top_up added credits you bought; top_up_reversal took back a top-up that was refunded or disputed; plan_change corrected the month after a change of plan; adjustment is any other correction; opening_balance is the balance the history starts from. One of `charge`, `refund`, `top_up`, `top_up_reversal`, `plan_change`, `adjustment`, `opening_balance`. |
| `data[].operation` | string or null | Required | What a charge paid for, and for a refund to the API bonus what the refunded charge had paid for: analysis finds the clips of a video, editing adds a clip to an edit, generation makes a video with a model, export renders a composition to a file, assistant is a turn of the Editor Assistant, tool is a voiceover, an audio separation or a brainstorm, fake_text writes a fake text story or makes its voices. null when the movement names no purchase. One of `analysis`, `editing`, `generation`, `export`, `assistant`, `tool`, `fake_text`. |
| `data[].credits` | integer | Required | How the spendable balance changed: negative when credits were spent, positive when they came in. credits is fromPlan plus fromTopUp; fromBonus moves only the API bonus, so a movement the bonus paid has credits 0. |
| `data[].fromPlan` | integer | Required | The part of credits that moved the plan credits of period. |
| `data[].fromTopUp` | integer | Required | The part of credits that moved the top-up. |
| `data[].fromBonus` | integer | Required | How the API bonus changed: negative when the bonus paid, positive when credits went back to it. 0 when the bonus was not involved. Receiving the bonus is not a movement: get_credits (`GET /v1/credits`) shows it. |
| `data[].projectId` | string or null | Required | The project the movement is about, when it is about one; always null for generation. |
| `data[].source` | string | Required | Where the movement came from: app is the GPT-Video app, api is a call with a key or from a connected assistant, system is everything else, such as a purchase or a plan change. One of `app`, `api`, `system`. |
| `data[].keyId` | string or null | Required | The id of the API key that made the call, when a key made it, also through an assistant set up with a key. It starts with mck_. null otherwise: the GPT-Video app, a purchase, or an assistant connected without a key. |
| `data[].period` | string or null | Required | The calendar month the movement counts on, as YYYY-MM; null for a movement of the top-up alone (a top-up, its reversal, its opening balance), which belongs to no month. |
| `data[].createdAt` | string | Required | An ISO 8601 time in UTC. |
| `hasMore` | boolean | Required | true when there are more items after this page. |
| `nextCursor` | string or null | Required | Send it as cursor to get the next page. It is null on the last page. |

**More examples**

#### List the movements of one month

`GET /v1/credits/ledger?period=2026-09`

```json title="HTTP 200"
{
  "data": [
    {
      "id": "lgr_9b1e4c7a2d5f40e8a316",
      "kind": "charge",
      "operation": "analysis",
      "credits": -30,
      "fromPlan": -30,
      "fromTopUp": 0,
      "fromBonus": 0,
      "projectId": "prj_7c2e9a4f1b3d4e8096a5",
      "source": "api",
      "keyId": "mck_5f2c9e1a7b3d8046",
      "period": "2026-09",
      "createdAt": "2026-09-01T09:10:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### List what the API bonus paid

`GET /v1/credits/ledger`

```json title="HTTP 200"
{
  "data": [
    {
      "id": "lgr_5d8a2f0c7e1b46a9d3c7",
      "kind": "charge",
      "operation": "export",
      "credits": 0,
      "fromPlan": 0,
      "fromTopUp": 0,
      "fromBonus": -1,
      "projectId": null,
      "source": "api",
      "keyId": "mck_5f2c9e1a7b3d8046",
      "period": "2026-09",
      "createdAt": "2026-09-01T09:42:00.000Z"
    },
    {
      "id": "lgr_0f6c3b9e2a7d41f8c5b1",
      "kind": "charge",
      "operation": "assistant",
      "credits": 0,
      "fromPlan": 0,
      "fromTopUp": 0,
      "fromBonus": -2,
      "projectId": null,
      "source": "api",
      "keyId": "mck_5f2c9e1a7b3d8046",
      "period": "2026-09",
      "createdAt": "2026-09-01T09:40:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### Send a cursor that was not issued

`GET /v1/credits/ledger?cursor=bm90LWEtY3Vyc29y`

```json title="HTTP 400"
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The cursor is not valid. Send the nextCursor of the previous page unchanged, or leave it out.",
    "hint": "Fix the request as the message says, then send it again.",
    "requestId": "req_7d2a9c4e1b6f3a0d8e5c2b71"
  }
}
```

## Errors

Every call can also answer these errors. [`VALIDATION_FAILED`](/developers/errors.md#VALIDATION_FAILED), [`UNKNOWN_PARAMETER`](/developers/errors.md#UNKNOWN_PARAMETER), [`UNAUTHORIZED`](/developers/errors.md#UNAUTHORIZED), [`SCOPE_MISSING`](/developers/errors.md#SCOPE_MISSING), [`RATE_LIMITED`](/developers/errors.md#RATE_LIMITED), [`INTERNAL`](/developers/errors.md#INTERNAL), [`UNAVAILABLE`](/developers/errors.md#UNAVAILABLE).

## Cost

No credits are used.

## Related operations

- [`get_credits`](/developers/api/get-credits.md) (`GET /v1/credits`) Get the credit balance

## Questions

### Why is a top-up missing when I filter by month?

Top-ups and their reversals belong to no month, so a period filter leaves them out. Read the list without period to see them.

### Does the sum of the movements give my balance?

Use get_credits (`GET /v1/credits`) for the balance. This list explains how it moved, and it is in beta while it runs next to the counters it explains.

### Why does a movement have no keyId?

keyId names the API key that made the call. Movements made in the GPT-Video app, by a purchase, or by an assistant connected without a key have none.

### Why is there no movement for the API bonus I received?

Receiving the bonus does not change the spendable balance. get_credits (`GET /v1/credits`) shows the bonus, and every charge it pays is listed here with credits 0 and the amount in fromBonus.
