> ## Documentation Index
> Fetch the complete documentation index at: https://www.dropfans.io/developers/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /api/external/vault — List your vault items — the source of media for posts

> Returns APPROVED items by default. Use an item's `id` as `vaultItemId` when creating a MEDIA post. Pass includePending=true to also see items still in moderation (their media is stripped).

- Source: https://www.dropfans.io/developers/reference/list-vault
- Section: API reference
- OpenAPI: https://www.dropfans.io/developers/openapi.json

Returns APPROVED items by default. Use an item's `id` as `vaultItemId` when creating a MEDIA post. Pass includePending=true to also see items still in moderation (their media is stripped).

Every drop and every MEDIA post is built from vault item ids, so this list is the starting point of almost every flow. Items are newest first. The response also carries all of the creator's folders — but note the folders' `itemCount` here respects the moderation filter, while [GET /api/external/vault/folders](https://www.dropfans.io/developers/reference/list-folders.md) counts every non-hidden item, so the two can disagree for the same folder.

## Authentication

`Authorization: Bearer dpfn_...` — an API key generated in the creator's dashboard (Vault → API Connect). One key = one creator. Missing or invalid keys return 401 `{"error":"Unauthorized","code":"unauthorized"}`.

## Query parameters

| Field | Type | Required | Description |
|---|---|---|---|
| `page` | integer |  | Page number, 1-based. |
| `limit` | integer |  | Items per page. Hard cap 50 — larger values are clamped, not rejected. |
| `folderId` | string |  | `"all"` (default), `"unfiled"`, or a folder id from the folders list. |
| `includePending` | boolean |  | Pass the literal string `true` to also get PENDING and FLAGGED items (REJECTED never appears). Those items come back with `filePath: ""` and `downloadUrl: null` — thumbnail only — and each item gains a `moderationStatus` field. |

## Responses

### 200

Vault items, folders and pagination.

| Field | Type | Required | Description |
|---|---|---|---|
| `items` | VaultItem[] | yes | The requested page, newest first. |
| `folders` | Folder[] | yes | All of the creator’s folders (unpaginated), with counts under the current moderation filter. |
| `hasMore` | boolean | yes | True when more pages exist for the current filter. |
| `total` | integer | yes | Total items matching the filter. |
| `page` | integer | yes | Echoed page number (1-based). |
| `limit` | integer | yes | Echoed page size. |

```json
{
  "items": [
    {
      "id": "clxv1a2b30001item",
      "fileName": "beach-set-01.jpg",
      "filePath": "https://cdn.dropfans.io/valeria/vault/1721990000-ab12cd.jpg",
      "thumbnailPath": "https://cdn.dropfans.io/valeria/thumbnails/1721990000-ab12cd.jpg",
      "fileType": "image",
      "fileSize": 482113,
      "durationSeconds": null,
      "bunnyStreamId": null,
      "createdAt": "2026-08-01T10:15:00.000Z",
      "folderId": "clxf0ld3r0001abcd",
      "contentTags": [
        "beach",
        "bikini"
      ],
      "downloadUrl": null
    }
  ],
  "folders": [
    {
      "id": "clxf0ld3r0001abcd",
      "name": "Beach set",
      "itemCount": 12
    }
  ],
  "hasMore": true,
  "total": 128,
  "page": 1,
  "limit": 50
}
```

## Errors

| Status | Body | When |
|---|---|---|
| 401 | `{"error":"Unauthorized","code":"unauthorized"}` | Missing or invalid API key. |
| 500 | `{"error":"Failed to fetch vault items"}` | Query failed — retry later. |

## Notes

Signed URLs in the response (`downloadUrl`, audio `filePath`) are valid ~12 hours — re-list rather than caching them longer. `downloadUrl` is the only fetchable video source (the Stream `filePath` is DRM-locked); it is null for videos uploaded before dual-store existed. `filePath` is the empty string `""` for any non-APPROVED item.

## Rate limiting

Per-key fixed windows by tier (Personal 60/min · 5,000/day; approved apps 300/min · 50,000/day; Dropfans-operated integrations exempt). Read the live values from X-RateLimit-Tier, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and their -Day variants; a 429 carries Retry-After and `{"error":"Rate limit exceeded","code":"rate_limited"}`. See [Rate limits](https://www.dropfans.io/developers/concepts/rate-limits.md).

## Code samples

### curl

```bash
curl "https://www.dropfans.io/api/external/vault?page=1&folderId=all" \
  -H "Authorization: Bearer $DROPFANS_API_KEY"
```

### Node

```javascript
const res = await fetch(`https://www.dropfans.io/api/external/vault?page=1&folderId=all`, {
  headers: {
    Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
  },
});
console.log(await res.json());
```

### Python

```python
import os
import requests

res = requests.get(
    "https://www.dropfans.io/api/external/vault",
    headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
    params={"page": 1, "folderId": "all"},
)
print(res.json())
```

## OpenAPI

```json
{
  "method": "GET",
  "path": "/api/external/vault",
  "operationId": "listVault",
  "tags": [
    "vault"
  ],
  "summary": "List your vault items — the source of media for posts",
  "description": "Returns APPROVED items by default. Use an item's `id` as `vaultItemId` when creating a MEDIA post. Pass includePending=true to also see items still in moderation (their media is stripped).\n\nEvery drop and every MEDIA post is built from vault item ids, so this list is the starting point of almost every flow. Items are newest first. The response also carries all of the creator's folders — but note the folders' `itemCount` here respects the moderation filter, while [GET /api/external/vault/folders](https://www.dropfans.io/developers/reference/list-folders.md) counts every non-hidden item, so the two can disagree for the same folder.",
  "parameters": [
    {
      "name": "page",
      "in": "query",
      "required": false,
      "description": "Page number, 1-based.",
      "schema": {
        "type": "integer",
        "default": 1
      },
      "example": 1
    },
    {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Items per page. Hard cap 50 — larger values are clamped, not rejected.",
      "schema": {
        "type": "integer",
        "default": 50,
        "maximum": 50
      }
    },
    {
      "name": "folderId",
      "in": "query",
      "required": false,
      "description": "`\"all\"` (default), `\"unfiled\"`, or a folder id from the folders list.",
      "schema": {
        "type": "string"
      },
      "example": "all"
    },
    {
      "name": "includePending",
      "in": "query",
      "required": false,
      "description": "Pass the literal string `true` to also get PENDING and FLAGGED items (REJECTED never appears). Those items come back with `filePath: \"\"` and `downloadUrl: null` — thumbnail only — and each item gains a `moderationStatus` field.",
      "schema": {
        "type": "boolean",
        "default": false
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Vault items, folders and pagination.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Vault item id — use as `vaultItemId` when creating drops and MEDIA posts.",
                      "example": "clxv1a2b30001item"
                    },
                    "fileName": {
                      "type": "string",
                      "description": "Original file name as uploaded.",
                      "example": "beach-set-01.jpg"
                    },
                    "filePath": {
                      "type": "string",
                      "description": "Display asset URL. Images: the CDN display copy. Audio: a token-signed URL valid ~12 hours, directly fetchable. Videos: the Bunny Stream playback URL (DRM-locked — use `downloadUrl` to fetch bytes). **Empty string `\"\"` for items that are not APPROVED** (visible only with includePending=true): unapproved content may never be forwarded."
                    },
                    "thumbnailPath": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Thumbnail URL. For videos this is always rebuilt as an absolute Bunny Stream thumbnail from the current CDN host. Null for audio (voice messages have no visual)."
                    },
                    "fileType": {
                      "type": "string",
                      "description": "Media kind.",
                      "enum": [
                        "image",
                        "video",
                        "audio"
                      ]
                    },
                    "fileSize": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Size in bytes of the stored display asset.",
                      "example": 482113
                    },
                    "durationSeconds": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Audio (voice message) length in seconds. Null for images and videos.",
                      "example": 42
                    },
                    "bunnyStreamId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Bunny Stream GUID for videos — the id you poll with POST /api/external/vault/video-status. Null for images and audio."
                    },
                    "createdAt": {
                      "type": "string",
                      "description": "Upload time.",
                      "format": "date-time"
                    },
                    "folderId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Containing folder id, or null for unfiled (\"All\")."
                    },
                    "contentTags": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "A content tag (≤64 chars)."
                      },
                      "description": "The item’s content tags (set via PATCH /api/external/vault/{id}/tags)."
                    },
                    "downloadUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Token-signed, directly fetchable original — valid ~12 hours. Videos: the DRM-free Bunny Storage original (null for videos uploaded before dual-store existed — those cannot be fetched). Audio: the same signed voice file as `filePath`. **Always null for images** (fetch `filePath` instead) and for non-APPROVED items."
                    },
                    "moderationStatus": {
                      "type": "string",
                      "description": "**Only present when the request had includePending=true.** Default responses contain APPROVED items only and omit this field entirely, keeping the shape byte-identical for older consumers.",
                      "enum": [
                        "PENDING",
                        "APPROVED",
                        "REJECTED",
                        "FLAGGED"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "fileName",
                    "filePath",
                    "fileType",
                    "createdAt",
                    "contentTags"
                  ]
                },
                "description": "The requested page, newest first."
              },
              "folders": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Folder id.",
                      "example": "clxf0ld3r0001abcd"
                    },
                    "name": {
                      "type": "string",
                      "description": "Folder name — unique per creator.",
                      "example": "Beach set"
                    },
                    "itemCount": {
                      "type": "integer",
                      "description": "Items in the folder. NOTE: GET /api/external/vault counts only items visible at that call’s moderation filter, while GET /api/external/vault/folders counts every non-hidden item regardless of moderation status — the same folder can report two different counts.",
                      "example": 12
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "itemCount"
                  ]
                },
                "description": "All of the creator’s folders (unpaginated), with counts under the current moderation filter."
              },
              "hasMore": {
                "type": "boolean",
                "description": "True when more pages exist for the current filter."
              },
              "total": {
                "type": "integer",
                "description": "Total items matching the filter.",
                "example": 128
              },
              "page": {
                "type": "integer",
                "description": "Echoed page number (1-based).",
                "example": 1
              },
              "limit": {
                "type": "integer",
                "description": "Echoed page size.",
                "example": 50
              }
            },
            "required": [
              "items",
              "folders",
              "hasMore",
              "total",
              "page",
              "limit"
            ]
          },
          "example": {
            "items": [
              {
                "id": "clxv1a2b30001item",
                "fileName": "beach-set-01.jpg",
                "filePath": "https://cdn.dropfans.io/valeria/vault/1721990000-ab12cd.jpg",
                "thumbnailPath": "https://cdn.dropfans.io/valeria/thumbnails/1721990000-ab12cd.jpg",
                "fileType": "image",
                "fileSize": 482113,
                "durationSeconds": null,
                "bunnyStreamId": null,
                "createdAt": "2026-08-01T10:15:00.000Z",
                "folderId": "clxf0ld3r0001abcd",
                "contentTags": [
                  "beach",
                  "bikini"
                ],
                "downloadUrl": null
              }
            ],
            "folders": [
              {
                "id": "clxf0ld3r0001abcd",
                "name": "Beach set",
                "itemCount": 12
              }
            ],
            "hasMore": true,
            "total": 128,
            "page": 1,
            "limit": 50
          }
        }
      }
    },
    "401": {
      "description": "Missing or invalid API key.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message.",
                "example": "Rate limit exceeded"
              },
              "code": {
                "type": "string",
                "description": "Machine-readable code: unauthorized, app_suspended, first_party_only, rate_limited, username_required.",
                "example": "rate_limited"
              }
            },
            "required": [
              "error",
              "code"
            ]
          },
          "example": {
            "error": "Unauthorized",
            "code": "unauthorized"
          }
        }
      }
    },
    "403": {
      "description": "The app this key belongs to has been suspended by Dropfans. Every request fails with this until the app is reinstated — surface it to the creator and contact Dropfans.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message.",
                "example": "Rate limit exceeded"
              },
              "code": {
                "type": "string",
                "description": "Machine-readable code: unauthorized, app_suspended, first_party_only, rate_limited, username_required.",
                "example": "rate_limited"
              }
            },
            "required": [
              "error",
              "code"
            ]
          },
          "example": {
            "error": "This integration has been suspended by Dropfans. Contact the app developer.",
            "code": "app_suspended"
          }
        }
      }
    },
    "429": {
      "description": "Rate limit exceeded for the current minute or day window. Wait Retry-After seconds and retry.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "Retry-After": {
          "description": "Seconds to wait before retrying (sent with 429s).",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message.",
                "example": "Rate limit exceeded"
              },
              "code": {
                "type": "string",
                "description": "Machine-readable code: unauthorized, app_suspended, first_party_only, rate_limited, username_required.",
                "example": "rate_limited"
              }
            },
            "required": [
              "error",
              "code"
            ]
          },
          "example": {
            "error": "Rate limit exceeded",
            "code": "rate_limited"
          }
        }
      }
    },
    "500": {
      "description": "Query failed — retry later.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message describing what went wrong.",
                "example": "Vault item not found"
              }
            },
            "required": [
              "error"
            ]
          },
          "example": {
            "error": "Failed to fetch vault items"
          }
        }
      }
    }
  },
  "x-codeSamples": [
    {
      "lang": "cURL",
      "label": "curl",
      "source": "curl \"https://www.dropfans.io/api/external/vault?page=1&folderId=all\" \\\n  -H \"Authorization: Bearer $DROPFANS_API_KEY\""
    },
    {
      "lang": "JavaScript",
      "label": "Node",
      "source": "const res = await fetch(`https://www.dropfans.io/api/external/vault?page=1&folderId=all`, {\n  headers: {\n    Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,\n  },\n});\nconsole.log(await res.json());"
    },
    {
      "lang": "Python",
      "source": "import os\nimport requests\n\nres = requests.get(\n    \"https://www.dropfans.io/api/external/vault\",\n    headers={\"Authorization\": f\"Bearer {os.environ['DROPFANS_API_KEY']}\"},\n    params={\"page\": 1, \"folderId\": \"all\"},\n)\nprint(res.json())"
    }
  ],
  "x-dropfans-docs": "https://www.dropfans.io/developers/reference/list-vault"
}
```

---

Previous: [The creator’s payout balance](https://www.dropfans.io/developers/reference/get-balance.md) · Next: [Upload an image, small video or voice message](https://www.dropfans.io/developers/reference/upload-vault-item.md) · All pages: [llms.txt](https://www.dropfans.io/developers/llms.txt)
