> Solana Developer Platform docs, served as Markdown. The [docs index](https://platform.solana.com/docs.md) lists every page and [llms.txt](https://platform.solana.com/docs/ai/llms.txt) the key ones; [SKILL.md](https://platform.solana.com/SKILL.md) has the rules for building on SDP.

# Estimate transfer batch

Validates an exact-wallet transfer batch request and estimates transaction chunking and fees.

`POST /v1/payments/transfer-batches/estimate`

`warnings` lists what would make the batch fail: a native SOL amount below the rent-exempt minimum sent to an address with no account, or a source wallet left below the minimum; create refuses those with 409 `AMOUNT_BELOW_RENT_MINIMUM`.

Requires an [API key](/docs/developing-with-sdp/authentication) with the `transfer_batches.read` action.

## Headers

- **`x-project-id`** · `string` · min length 1 · example `prj_example`

  Required for dashboard sessions. API keys use their assigned project.

## Request body

`application/json` · required

Estimate transaction chunking and fees for a transfer batch.

- **`projectId`** · `string` · min length 1 · example `prj_example`

  Project identifier for the transfer batch context.

- **`sourceCustodyWalletId`** · `string` · required · min length 1 · example `cwlt_example`

  Exact SDP Wallet ID (`id` from `GET /v1/wallets`).

- **`token`** · `string` · required · example `SOL`

  Token to transfer. Pass `SOL` for the native token, a well-known token symbol (e.g. `USDC`) resolved to the configured cluster's mint, or a base58 SPL mint address. Custom tokens must be specified by their on-chain mint.

- **`recipients`** · `object[]` · required · min items 1 · max items 500

  Counterparty-account recipients to pay in this batch.

  - **`externalId`** · `string` · min length 1 · max length 256

  - **`counterpartyId`** · `string` · min length 1

  - **`counterpartyAccountId`** · `string` · required · min length 1

  - **`amount`** · `string` · required

- **`options`** · `object`

  Execution options for transaction chunking, priority fees, and preflight validation.

  - **`maxRecipientsPerTransaction`** · `integer` · min 1 · max 64

  - **`priorityFee`** · `string` · one of `none`, `low`, `medium`, `high`, `auto`

  - **`preflight`** · `boolean`

- **`externalId`** · `string` · min length 1 · max length 256 · example `payroll_2026_06_30`

  Optional: an estimate moves nothing, so it names no payout run. Pass the run's `externalId` to estimate the batch you will create with it.

## Responses

- **`200`** Transfer batch estimate

  - **`data`** · `object` · required

    Transfer batch estimate response payload.

    - **`estimate`** · `object` · required

      Transfer batch estimate.

      - **`recipientCount`** · `integer` · required

        Requested recipient count.

      - **`transactionCount`** · `integer` · required

        Estimated number of Solana transaction chunks.

      - **`estimatedFees`** · `object` · required

        Estimated fee components.

        - **`networkFeeLamports`** · `string` · required · example `5000`

          Estimated base network fee in lamports.

        - **`priorityFeeLamports`** · `string` · required · example `0`

          Estimated priority fee in lamports.

        - **`tokenAccountRentLamports`** · `string` · required · example `0`

          Estimated rent needed for newly-created recipient token accounts.

        - **`sponsored`** · `boolean` · required · example `true`

          Whether SDP sponsors transaction fees for this batch.

      - **`warnings`** · `object[]` · required

        What would make the batch fail. Empty when the batch can be sent as estimated.

        One of:

        - **code: "AMOUNT_BELOW_RENT_MINIMUM"**

          - **`code`** · `string` · required · always `AMOUNT_BELOW_RENT_MINIMUM`

            A native SOL recipient's address holds no account and its amount is below the rent-exempt minimum.

          - **`recipientIndex`** · `integer` · required

            Zero-based index of the recipient in the request.

          - **`minimumAmount`** · `string` · required

            The smallest SOL amount the address can receive while it has no account.

          - **`message`** · `string` · required

            Human-readable warning.

        - **code: "SOURCE_BELOW_RENT_MINIMUM"**

          - **`code`** · `string` · required · always `SOURCE_BELOW_RENT_MINIMUM`

            The source wallet would be left holding more than nothing but less than the rent-exempt minimum, at the end or after some order of the batch's concurrent transactions.

          - **`sourceRemainder`** · `string` · required

            The SOL the source wallet would be left with.

          - **`minimumAmount`** · `string` · required

            The rent-exempt minimum for a SOL wallet.

          - **`message`** · `string` · required

            Human-readable warning.

  - **`meta`** · `object` · required

    Response metadata.

    - **`requestId`** · `string` · required · min length 1 · example `req_example`

      Request identifier for tracing.

    - **`timestamp`** · `string` · required · format `date-time` · example `2025-01-01T00:00:00.000Z`

      ISO 8601 timestamp.

- **`400`** Bad Request

  - **`error`** · `object` · required

    Standard error payload.

    - **`code`** · `string` · required

      Machine-readable error code.

    - **`message`** · `string` · required

      Human-readable error message.

    - **`details`** · `object`

      Optional error details for debugging.

      - **`*`** · `any`

  - **`meta`** · `object`

    - **`requestId`** · `string` · min length 1 · example `req_example`

      Request identifier for tracing.

- **`401`** Unauthorized

  Same fields as `400`.

- **`403`** Forbidden. Requires the `transfer_batches.read` action: an API key must hold it (a module's `manage` or `execute` also covers its `read`), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS and `error.details.action` names the action. Any other 403 names its own reason.

  Same fields as `400`.

- **`404`** Not Found

  Same fields as `400`.

- **`500`** Internal Server Error

  Same fields as `400`.

## Example request

```bash tab="cURL"
curl -X POST 'https://api.solana.com/v1/payments/transfer-batches/estimate' \
  -H "Authorization: Bearer $SDP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "prj_example",
    "sourceCustodyWalletId": "cwlt_example",
    "token": "SOL",
    "recipients": [
      {
        "externalId": "payroll_row_001",
        "counterpartyId": "cpty_example",
        "counterpartyAccountId": "cpacct_example",
        "amount": "25.00"
      }
    ],
    "options": {
      "maxRecipientsPerTransaction": 20,
      "priorityFee": "auto",
      "preflight": true
    },
    "externalId": "payroll_2026_06_30"
  }'
```

```ts tab="TypeScript"
const res = await fetch("https://api.solana.com/v1/payments/transfer-batches/estimate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SDP_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "projectId": "prj_example",
    "sourceCustodyWalletId": "cwlt_example",
    "token": "SOL",
    "recipients": [
      {
        "externalId": "payroll_row_001",
        "counterpartyId": "cpty_example",
        "counterpartyAccountId": "cpacct_example",
        "amount": "25.00"
      }
    ],
    "options": {
      "maxRecipientsPerTransaction": 20,
      "priorityFee": "auto",
      "preflight": true
    },
    "externalId": "payroll_2026_06_30"
  })
});
```

```python tab="Python"
import os, requests

r = requests.post("https://api.solana.com/v1/payments/transfer-batches/estimate",
  headers={"Authorization": f"Bearer {os.environ['SDP_API_KEY']}"},
  json={
    "projectId": "prj_example",
    "sourceCustodyWalletId": "cwlt_example",
    "token": "SOL",
    "recipients": [
      {
        "externalId": "payroll_row_001",
        "counterpartyId": "cpty_example",
        "counterpartyAccountId": "cpacct_example",
        "amount": "25.00"
      }
    ],
    "options": {
      "maxRecipientsPerTransaction": 20,
      "priorityFee": "auto",
      "preflight": True
    },
    "externalId": "payroll_2026_06_30"
  })
```

## Example responses

```json tab="200"
{
  "data": {
    "estimate": {
      "recipientCount": 1,
      "transactionCount": 1,
      "estimatedFees": {
        "networkFeeLamports": "5000",
        "priorityFeeLamports": "0",
        "tokenAccountRentLamports": "0",
        "sponsored": true
      },
      "warnings": [
        {
          "code": "AMOUNT_BELOW_RENT_MINIMUM",
          "recipientIndex": 1,
          "minimumAmount": "example",
          "message": "example"
        }
      ]
    }
  },
  "meta": {
    "requestId": "req_example",
    "timestamp": "2025-01-01T00:00:00.000Z"
  }
}
```

```json tab="403"
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Missing action: transfer_batches.read",
    "details": {
      "action": "transfer_batches.read"
    }
  },
  "meta": {
    "requestId": "req_example"
  }
}
```

Error responses use the [standard error shape](/docs/reference/conventions/errors).
