> ## 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.

# Snapshot Subscriptions

> Subscribe addresses to daily portfolio snapshots, renew them, and list the addresses your key can read

Subscribe addresses to daily portfolio snapshots, then read any day a snapshot was taken with [Historical Portfolio](https://docs.octav.fi/api/endpoints/historical-portfolio.md). A subscription lasts one year.

> [!NOTE]
>
> **Cost:** Listing is free. Subscribing and renewing cost 1,200 credits per address per year (\$30).

> [!NOTE]
>
> **Interactive Playground:** Test these endpoints in the [API Playground](https://docs.octav.fi/api-reference/snapshot-subscriptions/list-snapshot-subscriptions.md). Get your API key at [data.octav.fi](https://data.octav.fi/)

> [!TIP]
>
> Snapshots cannot be backfilled: history starts on the subscription date, never before it. Subscribe before you need the history.

***

## The subscription object

**type** `string`

`subscription` for an API snapshot subscription, `license` for an address licensed in [Octav Pro](https://pro.octav.fi). Licenses are renewed in the app, not through this API.

**address** `string`

Wallet address.

**description** `string`

Your description of the address. May be empty.

**subscriptionStartDate** `string`

When the subscription started.

**subscriptionEndDate** `string`

When the subscription ends.

**autoRenew** `boolean`

When `true`, the subscription renews itself for 1,200 credits on its end date. If your balance falls short, it is not renewed and `autoRenew` turns `false`. Always `false` for licenses.

**status** `string`

`active`, `expiring` (ends within 2 months: a subscription can be [renewed](#renew-subscriptions) now, a license renews in Octav Pro) or `expired`.

**lastSnapshotDate** `string`

When the most recent daily snapshot was taken. Omitted until the first one.

```json theme={null}
{
  "type": "subscription",
  "address": "0x6426af179aabebe47666f345d69fd9079673f6cd",
  "description": "Main Treasury",
  "subscriptionStartDate": "2025-12-01T09:30:00.000Z",
  "subscriptionEndDate": "2026-12-01T09:30:00.000Z",
  "autoRenew": true,
  "status": "expiring",
  "lastSnapshotDate": "2026-11-11T00:14:52.000Z"
}
```

***

## List Subscriptions

Every address whose snapshots your key can read: API subscriptions and licensed addresses. An address covered by both appears once for each.

> [!NOTE]
>
> **Cost:** Free

### Endpoint

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

### Parameters

**addresses** `string`

Comma-separated addresses to return only those. Addresses without a subscription or license are left out.

### Example

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

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

const { data } = await response.json();
const renewable = data.filter((s) => s.type === 'subscription' && s.status !== 'active');
```

```python Python theme={null}
import requests

response = requests.get(
    'https://api.octav.fi/v1/snapshot-subscriptions',
    headers={'Authorization': f'Bearer {api_key}'}
)

subscriptions = response.json()['data']
```

### Example Response

```json theme={null}
{
  "data": [
    {
      "type": "subscription",
      "address": "0x6426af179aabebe47666f345d69fd9079673f6cd",
      "description": "Main Treasury",
      "subscriptionStartDate": "2025-12-01T09:30:00.000Z",
      "subscriptionEndDate": "2026-12-01T09:30:00.000Z",
      "autoRenew": true,
      "status": "expiring",
      "lastSnapshotDate": "2026-11-11T00:14:52.000Z"
    },
    {
      "type": "subscription",
      "address": "0xe760ad8df0a54aafd95e3bc271b65d224abeaa57",
      "description": "Operations",
      "subscriptionStartDate": "2026-03-02T14:05:11.000Z",
      "subscriptionEndDate": "2027-03-02T14:05:11.000Z",
      "autoRenew": true,
      "status": "active",
      "lastSnapshotDate": "2026-11-11T00:15:03.000Z"
    },
    {
      "type": "license",
      "address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
      "description": "Solana Treasury",
      "subscriptionStartDate": "2026-05-14T08:00:00.000Z",
      "subscriptionEndDate": "2027-05-14T08:00:00.000Z",
      "autoRenew": false,
      "status": "active",
      "lastSnapshotDate": "2026-11-11T00:16:20.000Z"
    }
  ]
}
```

***

## Subscribe Addresses

Subscribe addresses for one year. Only new addresses are charged: an address that already has a subscription is returned as it is, never charged or changed.

> [!NOTE]
>
> **Cost:** 1,200 credits per new address. If your balance does not cover every new address, the request fails with `402` and nothing is created.

### Endpoint

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

### Parameters

**addresses** `array` *required*

1–500 entries per request.

**addresses\[].address** `string` *required*

EVM or Solana address. Must be unique within the request.

**addresses\[].description** `string`

Your description of the address.

### Example

```bash cURL theme={null}
curl -X POST "https://api.octav.fi/v1/snapshot-subscriptions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "addresses": [
      { "address": "0x28c6c06298d514db089934071355e5743bf21d60", "description": "New Fund" },
      { "address": "0xe760ad8df0a54aafd95e3bc271b65d224abeaa57", "description": "Operations" },
      { "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" }
    ]
  }'
```

```javascript JavaScript theme={null}
const response = await fetch('https://api.octav.fi/v1/snapshot-subscriptions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    addresses: [
      { address: '0x28c6c06298d514db089934071355e5743bf21d60', description: 'New Fund' },
      { address: '0xe760ad8df0a54aafd95e3bc271b65d224abeaa57', description: 'Operations' },
      { address: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' }
    ]
  })
});

const { data } = await response.json();
const toRenew = data.filter((s) => s.status === 'expired').map((s) => s.address);
```

```python Python theme={null}
import requests

response = requests.post(
    'https://api.octav.fi/v1/snapshot-subscriptions',
    headers={'Authorization': f'Bearer {api_key}'},
    json={
        'addresses': [
            {'address': '0x28c6c06298d514db089934071355e5743bf21d60', 'description': 'New Fund'},
            {'address': '0xe760ad8df0a54aafd95e3bc271b65d224abeaa57', 'description': 'Operations'},
            {'address': '0xd8da6bf26964af9d7eed9e03e53415d37aa96045'}
        ]
    }
)

results = response.json()['data']
```

### Example Response

Every address sent comes back with a `status`, grouped by status rather than in request order:

| `status` | Meaning | Charged |
| - | - | - |
| `created` | New subscription, starting with `autoRenew: true` | Yes |
| `already_subscribed` | The subscription is still running | No |
| `expired` | The subscription has ended. [Renew it](#renew-subscriptions) to resume snapshots | No |

```json theme={null}
{
  "data": [
    {
      "address": "0x28c6c06298d514db089934071355e5743bf21d60",
      "description": "New Fund",
      "subscriptionStartDate": "2026-11-11T10:00:00.000Z",
      "subscriptionEndDate": "2027-11-11T10:00:00.000Z",
      "autoRenew": true,
      "status": "created"
    },
    {
      "address": "0xe760ad8df0a54aafd95e3bc271b65d224abeaa57",
      "description": "Operations",
      "subscriptionStartDate": "2026-03-02T14:05:11.000Z",
      "subscriptionEndDate": "2027-03-02T14:05:11.000Z",
      "autoRenew": true,
      "status": "already_subscribed"
    },
    {
      "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      "description": "Cold Wallet",
      "subscriptionStartDate": "2025-10-20T11:00:00.000Z",
      "subscriptionEndDate": "2026-10-20T11:00:00.000Z",
      "autoRenew": false,
      "status": "expired"
    }
  ]
}
```

***

## Renew Subscriptions

Renew subscriptions for another year. A subscription can be renewed once it has expired or within 2 months of its end date (`status` is `expired` or `expiring`), so years cannot be bought ahead.

Renewing adds a year to the current end date, or a year from today if the subscription has already expired. The start date and the `autoRenew` setting are unchanged.

> [!NOTE]
>
> **Cost:** 1,200 credits per address.

### Endpoint

```bash theme={null}
POST https://api.octav.fi/v1/snapshot-subscriptions/renew
```

### Parameters

**addresses** `string[]` *required*

1–500 subscribed addresses per request. Must be unique within the request.

### Example

```bash cURL theme={null}
curl -X POST "https://api.octav.fi/v1/snapshot-subscriptions/renew" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "addresses": [
      "0x6426af179aabebe47666f345d69fd9079673f6cd",
      "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
    ]
  }'
```

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

const { data } = await response.json();
```

```python Python theme={null}
import requests

response = requests.post(
    'https://api.octav.fi/v1/snapshot-subscriptions/renew',
    headers={'Authorization': f'Bearer {api_key}'},
    json={
        'addresses': [
            '0x6426af179aabebe47666f345d69fd9079673f6cd',
            '0xd8da6bf26964af9d7eed9e03e53415d37aa96045'
        ]
    }
)

renewed = response.json()['data']
```

### Example Response

Returns the renewed subscriptions.

```json theme={null}
{
  "data": [
    {
      "address": "0x6426af179aabebe47666f345d69fd9079673f6cd",
      "description": "Main Treasury",
      "subscriptionStartDate": "2025-12-01T09:30:00.000Z",
      "subscriptionEndDate": "2027-12-01T09:30:00.000Z",
      "autoRenew": true
    },
    {
      "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      "description": "Cold Wallet",
      "subscriptionStartDate": "2025-10-20T11:00:00.000Z",
      "subscriptionEndDate": "2027-11-11T10:02:37.000Z",
      "autoRenew": false
    }
  ]
}
```

### Rejected Renewals

If any address cannot be renewed, the whole request is rejected and nothing is charged. `rejected` lists each such address with its reason:

| `reason` | Meaning |
| - | - |
| `not_subscribed` | The address has no snapshot subscription. [Subscribe it](#subscribe-addresses) instead |
| `license` | The address is covered by a license, renewed in [Octav Pro](https://pro.octav.fi) |
| `not_yet_renewable` | The subscription ends more than 2 months from now |

```json theme={null}
{
  "error": {
    "code": "SNAPSHOTS_NOT_RENEWABLE",
    "message": "Some addresses cannot be renewed: 0xe760ad8df0a54aafd95e3bc271b65d224abeaa57 (not_yet_renewable), 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM (license)",
    "rejected": [
      { "address": "0xe760ad8df0a54aafd95e3bc271b65d224abeaa57", "reason": "not_yet_renewable" },
      { "address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", "reason": "license" }
    ]
  }
}
```

***

## Address Matching

**EVM** addresses match case-insensitively. **Solana** addresses are case-sensitive.

***

## Error Responses

| Code | Status | Meaning |
| - | - | - |
| `VALIDATION_ERROR` | 400 | Invalid or duplicate address |
| `SNAPSHOTS_NOT_RENEWABLE` | 400 | Renew only: an address cannot be renewed, see [Rejected Renewals](#rejected-renewals) |
| `NOT_ENOUGH_CREDITS` | 402 | Your balance does not cover the request. Nothing is charged or changed |
| `INTERNAL_ERROR` | 500 | Unexpected failure |

```json theme={null}
{
  "error": {
    "code": "NOT_ENOUGH_CREDITS",
    "message": "Not enough credits to renew / create snapshots"
  }
}
```

A missing or invalid API key, or one without snapshot access, returns `401` with `{ "message": "Unauthorized" }`. A malformed body (missing or empty `addresses`, more than 500 addresses) returns `400` with `{ "error": "Validation Failed", "details": ... }`.

> [!NOTE]
>
> **Rate limit:** 360 requests/min.

***

## Migrating from `/v1/subscribe-snapshot`

[`POST /v1/subscribe-snapshot`](https://docs.octav.fi/api/endpoints/subscribe-snapshot) is deprecated. It still works, but returns only the subscriptions it created, so addresses that were already subscribed come back as `[]`. Send the same body to `POST /v1/snapshot-subscriptions` to get a `status` for every address, and renew with `POST /v1/snapshot-subscriptions/renew`.

***

## Related Endpoints

> [card] [Historical Portfolio](https://docs.octav.fi/api/endpoints/historical-portfolio.md)
>
> Read a subscribed address on a past date

> [card] [Credits](https://docs.octav.fi/api/endpoints/credits.md)
>
> Check your balance before subscribing


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