openapi: 3.2.0 info: title: Savee Public Saves API version: 1.0.0 contact: name: Savee url: https://docs.savee.com email: hey@savee.com termsOfService: https://savee.com/terms/ license: name: Proprietary — Savee Terms of Service url: https://savee.com/terms/ description: 'Read-only REST API exposing a Savee user’s own saves, boards, and home feed, plus search over Savee’s public library. Authenticate with either a personal access token (`sv_live_…`) generated in your Savee settings, or an OAuth 2.1 access token (`sv_at_…`) obtained on one of your users’ behalf. OAuth tokens are limited to the scopes the user approved; personal tokens carry all of them. **Image format** — `media.thumbnail` and `media.original` for image saves are AVIF by default. Clients that cannot decode AVIF should send the request header `Avif-Fallback: 1` to receive JPG URLs instead. Video originals are always MP4.' servers: - url: https://api.savee.com tags: - name: Saves paths: /v1/saves: get: summary: List the authenticated user's saves description: 'Newest first. Includes the caller''s private saves, with one exception: if the caller has enabled *Hide private board saves from profile feed* in their Savee settings, saves that belong to a private board are left out here too. The `user` field on each save is omitted because the caller is implicitly the owner.' tags: - Saves security: - BearerAuth: [] - OAuth2: - saves:read parameters: - schema: type: integer minimum: 1 maximum: 100 description: Page size (default 30, max 100). example: 30 required: false name: limit in: query - schema: type: string description: Opaque cursor returned in `next_cursor` from the previous page. Pass it through verbatim — the encoding is an implementation detail and may change. example: eyJjIjoiMjAyNi0wNS0wOFQxMjowMDowMFoiLCJpIjoiNjdhYWRhMjAifQ required: false name: cursor in: query responses: '200': description: A page of saves. content: application/json: schema: $ref: '#/components/schemas/SavesPage' '400': description: Invalid input. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Missing or invalid Bearer token. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '402': description: Authenticated but the user has no active subscription. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: The Public API is not available on this account. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '429': description: Rate limit exceeded. Retry after the number of seconds in `Retry-After`. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Unexpected server error. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /v1/feed: get: summary: List the authenticated user’s home feed description: Saves from users this user follows (and platform-curated content), newest first. Each save includes a `user` object describing the saver. tags: - Saves security: - BearerAuth: [] - OAuth2: - saves:read parameters: - schema: type: integer minimum: 1 maximum: 100 description: Page size (default 30, max 100). example: 30 required: false name: limit in: query - schema: type: string description: Opaque cursor returned in `next_cursor` from the previous page. Pass it through verbatim — the encoding is an implementation detail and may change. example: eyJjIjoiMjAyNi0wNS0wOFQxMjowMDowMFoiLCJpIjoiNjdhYWRhMjAifQ required: false name: cursor in: query responses: '200': description: A page of feed saves. content: application/json: schema: $ref: '#/components/schemas/SavesPage' '400': description: Invalid input. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Missing or invalid Bearer token. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '402': description: Authenticated but the user has no active subscription. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: The Public API is not available on this account. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '429': description: Rate limit exceeded. Retry after the number of seconds in `Retry-After`. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Unexpected server error. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /v1/boards/{boardID}/saves: get: summary: List saves on a specific board description: '`boardID` is the board `id` returned by `/v1/boards`. Accessible by any user with a role on the board (owner, admin, editor, viewer) and by team members for team-owned boards. Returns 404 when the board does not exist or the caller has no role on it — the API does not confirm whether someone else’s board exists.' tags: - Saves security: - BearerAuth: [] - OAuth2: - boards:read - saves:read parameters: - schema: type: string description: The board id (the `id` returned by `/v1/boards`). required: true description: The board id (the `id` returned by `/v1/boards`). name: boardID in: path - schema: type: integer minimum: 1 maximum: 100 description: Page size (default 30, max 100). example: 30 required: false name: limit in: query - schema: type: string description: Opaque cursor returned in `next_cursor` from the previous page. Pass it through verbatim — the encoding is an implementation detail and may change. example: eyJjIjoiMjAyNi0wNS0wOFQxMjowMDowMFoiLCJpIjoiNjdhYWRhMjAifQ required: false name: cursor in: query responses: '200': description: A page of saves on the board. content: application/json: schema: $ref: '#/components/schemas/SavesPage' '400': description: Invalid input. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Missing or invalid Bearer token. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '402': description: Authenticated but the user has no active subscription. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: The Public API is not available on this account. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '404': description: Board not found or not accessible to the caller. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '429': description: Rate limit exceeded. Retry after the number of seconds in `Retry-After`. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Unexpected server error. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: schemas: User: type: object properties: id: type: string description: User identifier. Stable across renames; safe to store as a foreign key on the caller’s side. example: 63e1a4c2d242ec00094007f1 username: type: string description: Current username. May change if the user renames. example: aliceb name: type: string example: Alice Bauer url: type: string format: uri example: https://savee.com/aliceb/ avatar_url: type: string format: uri example: https://dm.savee.com/user-avatar/original/8kQ2mZp.jpg required: - id - username - name - url - avatar_url description: The user who created this save. Omitted on /v1/saves (the caller is implicitly the author); present on /v1/feed and /v1/boards/{id}/saves where the author may differ from the caller. SavesPage: type: object properties: data: type: array items: $ref: '#/components/schemas/Save' next_cursor: type: - string - 'null' description: Pass as `cursor` to fetch the next page. `null` on the last page. example: eyJjIjoiMjAyNi0wNS0wOFQxMjowMDowMFoiLCJpIjoiNjdhYWRhMjAifQ has_more: type: boolean description: Whether another page is available. Keep paging while this is `true`. example: true required: - data - next_cursor - has_more ErrorResponse: type: object properties: error: type: object properties: code: type: string message: type: string required: - code - message required: - error Save: type: object properties: id: type: string description: Save identifier. Stable across renames; safe to store as a foreign key on the caller’s side. example: 67aada20d242ec0009400825 url: type: string format: uri description: Canonical page on savee.com for this save. example: https://savee.com/i/UXb_bvc/ name: type: string example: Brutalist poster source_url: type: - string - 'null' description: Where the save was originally taken from. `null` if unknown. example: https://example.com/poster created_at: type: string format: date-time example: '2026-05-08T12:00:00.000Z' is_private: type: boolean description: Whether the save itself is marked private. This is a property of the save, independent of whether it also sits in a private board. example: false total_saves: type: integer minimum: 0 description: How many users across Savee have saved this same asset. example: 42 media: $ref: '#/components/schemas/SaveMedia' colors: type: array items: $ref: '#/components/schemas/SaveColor' description: Dominant colors of the media, most prominent first. Empty when colors have not been extracted for this save (e.g. shortly after saving). user: $ref: '#/components/schemas/User' required: - id - url - name - source_url - created_at - is_private - total_saves - media - colors SaveMedia: type: object properties: type: type: string enum: - image - video example: image width: type: integer minimum: 0 example: 1600 height: type: integer minimum: 0 example: 2400 thumbnail: type: string format: uri description: 'Grid-sized preview (~420px wide). AVIF by default; clients that cannot decode AVIF should send `Avif-Fallback: 1` to receive JPG. Applies to both image and video assets (videos return a JPG/AVIF poster frame).' example: https://dm.savee.com/asset_image/w420/6r4nDqE.avif original: type: string format: uri description: 'Full-quality asset. Image: AVIF by default (JPG with `Avif-Fallback: 1`). Video: MP4.' example: https://dm.savee.com/asset_image/original/6r4nDqE.avif required: - type - width - height - thumbnail - original SaveColor: type: object properties: color: type: string pattern: ^#[0-9A-F]{6}$ description: Hex color code, e.g. `#1A2B3C`. example: '#1A1A1A' amount: type: number minimum: 0 maximum: 1 description: Fraction of the media covered by this color (0–1). example: 0.62 required: - color - amount securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: sv_live_… description: '**Personal access token** (`sv_live_…`) — represents you and carries every scope, so no scope is required for this call. Best for your own scripts and internal tools. Generate one at https://savee.com/developers/.' OAuth2: type: oauth2 description: '**OAuth access token** (`sv_at_…`) — obtained on one of your users’ behalf and limited to the scopes they approved. Use this when you’re building a product other people sign into with Savee. See https://docs.savee.com/api/oauth. Missing the scope below returns `403` with a `WWW-Authenticate: Bearer error="insufficient_scope"` header naming it.' flows: authorizationCode: authorizationUrl: https://savee.com/oauth/authorize/ tokenUrl: https://savee.com/api/oauth/token/ refreshUrl: https://savee.com/api/oauth/token/ scopes: profile:read: Read the user’s username, name, and avatar saves:read: Read the user’s saves and home feed boards:read: Read the user’s boards and the saves on them search:read: Search Savee’s public library on the user’s behalf