---
title: Credits
description: Balance, ledger history, and pre-flight cost estimation. Gate paid operations to avoid mid-run PaymentRequiredError.
section: API Reference
order: 7
---
Pictograph uses a **credit ledger** (signed integers, per organization)
for paid operations. Free actions (uploads, exports, search) cost 0.

```python
from pictograph import Client
client = Client()
```

## balance

Current balance + monthly allowance + last 20 ledger entries.

```python
balance = client.credits.balance()
print(balance.credits_remaining, "/", balance.credits_monthly_allowance)
print("Resets:", balance.credits_reset_at)
for entry in balance.recent_history:
    print(entry.created_at, entry.operation, entry.amount)
```

Returns `CreditBalance`.

## history

Page through the credit ledger (newest first).

```python
entries = client.credits.history(limit=50, offset=0)
for e in entries:
    direction = "debit" if e.amount < 0 else "credit"
    print(e.created_at, direction, abs(e.amount), e.operation)
```

Sign convention: `amount < 0` = debit (operation consumed credits),
`amount > 0` = credit / refund (top-up, training overcharge refund).

## iter

Auto-paging iterator over the entire ledger.

```python
for entry in client.credits.iter(page_size=100):
    print(entry.balance_after, entry.operation)
```

## estimate

Pre-flight cost check **before** invoking a paid operation.

```python
estimate = client.credits.estimate("training_a10g_per_minute", quantity=30)
print(estimate.total_credits, "credits;", "sufficient:", estimate.sufficient)
```

`sufficient=True` is **not** a guarantee — another caller may drain credits
between the estimate and the actual call. The authoritative answer is
the operation's own `PaymentRequiredError`.

## Cost cheatsheet

| Operation slug | Approx cost |
|---|---|
| `sam3_per_minute` | 3 cr session minimum + ~1/prompt |
| `training_a10g_per_minute` | 10 cr/min |
| `training_a100_per_minute` | 60 cr/min |
| `training_h100_per_minute` | 120 cr/min |
| `image_generate_imagen_fast` | 5 cr/image |
| `image_edit_gemini_flash` | 3 cr/image |
| `image_inference` | 1 cr/image |

The full table lives server-side in `utils.tier_limits.CREDIT_COSTS`.

## Gating in workflows

`full_pipeline` already gates on credit balance before kicking off
paid phases:

```python
from pictograph.workflows import full_pipeline

report = full_pipeline(
    client,
    dataset_name="…", folder="…", classes=…, pipeline="yolox",
    min_credits=1,             # skip annotate + train if balance < 1
)
if report.credit_skip_reason:
    print(report.credit_skip_reason)
```

`min_credits=None` disables the check.

## PaymentRequiredError details

```python
from pictograph.exceptions import PaymentRequiredError

try:
    client.training.create(dataset_name, export_name, pipeline_type="yolox")
except PaymentRequiredError as e:
    print(f"Need {e.required}, have {e.remaining}")
    print(f"Top up at: {e.upgrade_url}")
```

## Refunds

The training pipeline auto-refunds unused GPU minutes when:

- A run is **cancelled** mid-training.
- A run **failed** before consuming the full `timeout` budget.

Refunds appear as positive ledger entries with operation
`training_refund_<gpu>`. No SDK call required.

## Common errors

| Status | Exception | Cause |
|---|---|---|
| 422 | `ValidationError` | `operation` slug not in the cost table |
| 402 | `PaymentRequiredError` | (raised by the *operation* being estimated, not by `estimate` itself) |