> ## Documentation Index
> Fetch the complete documentation index at: https://docs.octav.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Bundles

> Group saved addresses into named bundles and manage their membership

Group addresses into named bundles — the same bundles you see in [Octav Pro](https://pro.octav.fi).

<Info>
  **Cost:** 1 credit per call
</Info>

<Warning>
  Every address in a bundle must already be in your [address book](/api/endpoints/addressbook).
</Warning>

<Note>
  **Interactive Playground:** Test these endpoints in the [API Playground](/api-reference/bundles). Get your API key at [data.octav.fi](https://data.octav.fi/)
</Note>

***

## The bundle object

<ResponseField name="id" type="string">
  Bundle identifier.
</ResponseField>

<ResponseField name="name" type="string">
  Bundle name.
</ResponseField>

<ResponseField name="addresses" type="array">
  Addresses in the bundle.
</ResponseField>

```json theme={null}
{
  "id": "7714a4a5-487b-4b36-99ba-537e9cca8e62",
  "name": "Client A",
  "addresses": [
    "0xe760ad8df0a54aafd95e3bc271b65d224abeaa57",
    "0x05d2e386525b0495e952f6945d0fa2480ed5b056"
  ]
}
```

<Note>
  Bundles created through the API are private. Sharing settings are managed in [Octav Pro](https://pro.octav.fi).
</Note>

***

## List Bundles

### Endpoint

```bash theme={null}
GET https://api.octav.fi/v1/bundles
```

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/bundles" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.octav.fi/v1/bundles', {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });

  const { data } = await response.json();
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "data": [
    {
      "id": "7714a4a5-487b-4b36-99ba-537e9cca8e62",
      "name": "Client A",
      "addresses": ["0xe760ad8df0a54aafd95e3bc271b65d224abeaa57"]
    }
  ]
}
```

***

## Create Bundle

### Endpoint

```bash theme={null}
POST https://api.octav.fi/v1/bundles
```

### Parameters

<ParamField body="name" type="string" required>
  1–255 characters. Must be unique.
</ParamField>

<ParamField body="addresses" type="array" required>
  1–100 addresses per request, each already in your address book. A bundle holds up to 10.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.octav.fi/v1/bundles" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Client A",
      "addresses": ["0xe760ad8df0a54aafd95e3bc271b65d224abeaa57"]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.octav.fi/v1/bundles', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Client A',
      addresses: ['0xe760ad8df0a54aafd95e3bc271b65d224abeaa57']
    })
  });

  const { data } = await response.json();
  ```
</CodeGroup>

### Response

`201` with the new bundle.

***

## Get Bundle

### Endpoint

```bash theme={null}
GET https://api.octav.fi/v1/bundles/{bundleId}
```

### Parameters

<ParamField path="bundleId" type="string" required>
  Bundle identifier.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/bundles/7714a4a5-487b-4b36-99ba-537e9cca8e62" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.octav.fi/v1/bundles/${bundleId}`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );

  const { data } = await response.json();
  ```
</CodeGroup>

***

## Rename Bundle

### Endpoint

```bash theme={null}
PATCH https://api.octav.fi/v1/bundles/{bundleId}
```

### Parameters

<ParamField path="bundleId" type="string" required>
  Bundle identifier.
</ParamField>

<ParamField body="name" type="string" required>
  1–255 characters. Must be unique.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH "https://api.octav.fi/v1/bundles/7714a4a5-487b-4b36-99ba-537e9cca8e62" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "name": "Client A — Q1" }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.octav.fi/v1/bundles/${bundleId}`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ name: 'Client A — Q1' })
    }
  );

  const { data } = await response.json();
  ```
</CodeGroup>

***

## Delete Bundle

### Endpoint

```bash theme={null}
DELETE https://api.octav.fi/v1/bundles/{bundleId}
```

### Parameters

<ParamField path="bundleId" type="string" required>
  Bundle identifier.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://api.octav.fi/v1/bundles/7714a4a5-487b-4b36-99ba-537e9cca8e62" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://api.octav.fi/v1/bundles/${bundleId}`, {
    method: 'DELETE',
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });
  ```
</CodeGroup>

### Response

`204` with no body. The addresses stay in your address book.

***

## Add Address to Bundle

### Endpoint

```bash theme={null}
POST https://api.octav.fi/v1/bundles/{bundleId}/addresses
```

### Parameters

<ParamField path="bundleId" type="string" required>
  Bundle identifier.
</ParamField>

<ParamField body="address" type="string" required>
  Wallet address, already in your address book.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.octav.fi/v1/bundles/7714a4a5-487b-4b36-99ba-537e9cca8e62/addresses" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "address": "0x05d2e386525b0495e952f6945d0fa2480ed5b056" }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.octav.fi/v1/bundles/${bundleId}/addresses`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ address: '0x05d2e386525b0495e952f6945d0fa2480ed5b056' })
    }
  );

  const { data } = await response.json();
  ```
</CodeGroup>

### Response

Returns the updated bundle.

***

## Remove Address from Bundle

### Endpoint

```bash theme={null}
DELETE https://api.octav.fi/v1/bundles/{bundleId}/addresses/{address}
```

### Parameters

<ParamField path="bundleId" type="string" required>
  Bundle identifier.
</ParamField>

<ParamField path="address" type="string" required>
  Wallet address.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://api.octav.fi/v1/bundles/7714a4a5-487b-4b36-99ba-537e9cca8e62/addresses/0x05d2e386525b0495e952f6945d0fa2480ed5b056" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.octav.fi/v1/bundles/${bundleId}/addresses/${address}`,
    { method: 'DELETE', headers: { 'Authorization': `Bearer ${apiKey}` } }
  );

  const { data } = await response.json();
  ```
</CodeGroup>

### Response

Returns the updated bundle with `200`. Removing the last member returns `422` — delete the bundle instead.

***

## Limits

| Limit | Default |
| - | - |
| Bundles | 5 |
| Addresses per bundle | 10 |

Contact us to raise these.

Bundle names must be unique and match case-sensitively. Two bundles cannot hold exactly the same addresses.

***

## Error Responses

| Code | Status | Meaning |
| - | - | - |
| `VALIDATION_ERROR` | 400 | Invalid or duplicate address |
| `BUNDLE_NOT_FOUND` | 404 | No such bundle |
| `BUNDLE_NAME_TAKEN` | 409 | Name already used |
| `BUNDLE_MEMBERS_ALREADY_BUNDLED` | 409 | Another bundle holds these addresses |
| `BUNDLE_QUOTA_REACHED` | 422 | Bundle limit reached |
| `BUNDLE_SIZE_LIMIT_EXCEEDED` | 422 | Address limit reached |
| `BUNDLE_EMPTY` | 422 | A bundle cannot be empty |
| `BUNDLE_ADDRESS_NOT_IN_ADDRESS_BOOK` | 422 | Address is not in your address book |
| `INTERNAL_ERROR` | 500 | Unexpected failure |

```json theme={null}
{
  "error": {
    "code": "BUNDLE_ADDRESS_NOT_IN_ADDRESS_BOOK",
    "message": "Not in the address book: 0x05d2e386...",
    "addresses": ["0x05d2e386525b0495e952f6945d0fa2480ed5b056"]
  }
}
```

<Note>
  **Rate limit:** 360 requests/min.
</Note>

***

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Address Book" icon="address-book" href="/api/endpoints/addressbook">
    Manage saved addresses
  </Card>

  <Card title="Portfolio" icon="wallet" href="/api/endpoints/portfolio">
    Holdings for an address
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.