{"openapi":"3.1.0","info":{"title":"Savee Public 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.\n\nAuthenticate 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.\n\n**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"}],"components":{"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.\n\nMissing 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"}}}}},"schemas":{"Me":{"type":"object","properties":{"id":{"type":"string","description":"User identifier. Stable across renames.","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"},"plan":{"type":"object","properties":{"active":{"type":"boolean","example":true},"tiers":{"type":"array","items":{"type":"string"},"example":["pro"]}},"required":["active","tiers"],"description":"The caller’s subscription state. `active` must be true to use the API."}},"required":["id","username","name","url","avatar_url","plan"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"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"]},"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."},"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"},"short_id":{"type":"string","description":"The save’s short identifier — the segment in its savee.com URL (`savee.com/i/{short_id}/`). Also stable across renames. Accepted anywhere `saveID` is, so a savee.com link can be turned into an API call without a lookup step.","example":"UXb_bvc"},"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"},"detected_aesthetics":{"type":["string","null"],"description":"AI-generated description of what the media depicts, followed by its aesthetic vocabulary (style, medium, mood, composition, era, subject, palette). `null` when the save has not been annotated yet. Useful for filtering or grouping saves by look and feel without downloading the media.","example":"Bold black-and-white typography overlays a textured forest silhouette. Minimalist, grunge, typography, dark, moody, energetic, asymmetric, layered, negative space."},"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","short_id","url","name","source_url","created_at","is_private","total_saves","media","detected_aesthetics","colors"]},"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"]},"SaveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Save"}},"required":["data"]},"SubBoard":{"type":"object","properties":{"id":{"type":"string","description":"Board identifier. Stable across renames and slug changes; safe to store as a foreign key on the caller’s side."},"short_id":{"type":"string","description":"The board’s short identifier. Also stable across renames and slug changes, and accepted anywhere `boardID` is."},"name":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string","format":"uri","description":"Canonical page on savee.com for this board (uses the current slug)."},"is_private":{"type":"boolean"},"saves_count":{"type":"integer","minimum":0},"created_at":{"type":"string","format":"date-time"},"og_image":{"type":["string","null"],"format":"uri"},"ownership_type":{"type":"string","enum":["team","user"],"description":"`user` if the board belongs to a person (the caller, or someone who's added the caller as a collaborator); `team` if it belongs to a Savee Teams workspace the caller is a member of."},"sub_boards":{"type":"array","description":"Always empty — sub-boards do not nest further (nesting is single-level).","items":{},"maxItems":0}},"required":["id","short_id","name","slug","url","is_private","saves_count","created_at","og_image","ownership_type","sub_boards"]},"Board":{"type":"object","properties":{"id":{"type":"string","description":"Board identifier. Stable across renames and slug changes; safe to store as a foreign key on the caller’s side.","example":"69fbaf96d242ec48009df59a"},"short_id":{"type":"string","description":"The board’s short identifier. Also stable across renames and slug changes, and accepted anywhere `boardID` is.","example":"Lp9wQ2s"},"name":{"type":"string","example":"Posters"},"slug":{"type":"string","example":"posters"},"url":{"type":"string","format":"uri","description":"Canonical page on savee.com for this board (uses the current slug).","example":"https://savee.com/aliceb/posters/"},"is_private":{"type":"boolean","example":false},"saves_count":{"type":"integer","minimum":0,"example":128},"created_at":{"type":"string","format":"date-time","example":"2026-04-01T09:00:00.000Z"},"og_image":{"type":["string","null"],"format":"uri","description":"Share image for the board. `null` if one has not been generated yet.","example":"https://dm.savee.com/board-og/original/Lp9wQ2s.jpg"},"ownership_type":{"type":"string","enum":["team","user"],"description":"`user` if the board belongs to a person (the caller, or someone who's added the caller as a collaborator); `team` if it belongs to a Savee Teams workspace the caller is a member of.","example":"user"},"sub_boards":{"type":"array","items":{"$ref":"#/components/schemas/SubBoard"},"description":"Child boards nested one level under this board, ordered by the same boards-sort preference as the top level."}},"required":["id","short_id","name","slug","url","is_private","saves_count","created_at","og_image","ownership_type","sub_boards"]},"BoardsList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Board"}}},"required":["data"]},"BoardResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Board"}},"required":["data"]}},"parameters":{}},"paths":{"/v1/ping":{"get":{"summary":"Health check","tags":["System"],"responses":{"200":{"description":"Server is up.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}},"required":["status"]}}}}}}},"/v1/openapi.json":{"get":{"summary":"Get the OpenAPI spec","description":"The machine-readable OpenAPI 3.1 description of this API. No auth required. Point code generators and agents at this URL rather than at a copied snapshot, so they stay accurate as the API evolves.","tags":["System"],"responses":{"200":{"description":"The OpenAPI 3.1 document describing the v1 surface.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/v1/me":{"get":{"summary":"Get the authenticated user","tags":["User"],"security":[{"BearerAuth":[]},{"OAuth2":["profile:read"]}],"responses":{"200":{"description":"Authenticated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"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/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/saves/{saveID}":{"get":{"summary":"Get a single save","description":"Look up one save by either of its identifiers.\n\n`saveID` accepts the `id` returned by any listing endpoint (`67aada20d242ec0009400825`) **or** the save's `short_id` — the segment in its savee.com URL. So a link like `https://savee.com/i/997d6DW/` becomes `GET /v1/saves/997d6DW` directly, with no lookup step in between. The two forms can't be confused: an `id` is 24 hex characters, a `short_id` is 7.\n\nVisible to the caller are their own saves (including private ones) and any public save on Savee. A save that exists but isn't visible returns 404 rather than 403 — the API does not confirm whether someone else's private save exists. Unlike `/v1/saves`, the response always includes the `user` who created the save, since it may not be the caller.","tags":["Saves"],"security":[{"BearerAuth":[]},{"OAuth2":["saves:read"]}],"parameters":[{"schema":{"type":"string","description":"The save `id`, or its `short_id` from a savee.com `/i/…/` URL.","example":"997d6DW"},"required":true,"description":"The save `id`, or its `short_id` from a savee.com `/i/…/` URL.","name":"saveID","in":"path"}],"responses":{"200":{"description":"The save.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveResponse"}}}},"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":"Save not found or not visible 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"}}}}}}},"/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":{"get":{"summary":"List the authenticated user's boards","description":"Returns every board the caller can see as a tree in a single response (not paginated): personal boards they own, boards they collaborate on, and boards from any Savee Teams workspace they're a member of. Only top-level boards appear at the root of `data`; a board's children are nested under its `sub_boards` array (single-level, so each sub-board is a leaf). Use the `ownership_type` field on each board to tell user-owned boards apart from team boards. The order at every level follows the caller's chosen boards-sort preference on savee.com.","tags":["Boards"],"security":[{"BearerAuth":[]},{"OAuth2":["boards:read"]}],"responses":{"200":{"description":"All boards visible to the caller, as a tree.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardsList"}}}},"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}":{"get":{"summary":"Get a single board","description":"`boardID` is either the board `id` or the `short_id` returned by `/v1/boards`; both are accepted and both are stable across renames. Returns the board with its child boards nested under `sub_boards` — the same shape as a node in the `/v1/boards` tree — so there is no separate sub-boards endpoint. 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":["Boards"],"security":[{"BearerAuth":[]},{"OAuth2":["boards:read"]}],"parameters":[{"schema":{"type":"string","description":"The board `id`, or its `short_id` — both are returned by `/v1/boards`."},"required":true,"description":"The board `id`, or its `short_id` — both are returned by `/v1/boards`.","name":"boardID","in":"path"}],"responses":{"200":{"description":"The board, with its sub-boards nested.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"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"}}}}}}},"/v1/boards/{boardID}/saves":{"get":{"summary":"List saves on a specific board","description":"`boardID` is either the board `id` or the `short_id` returned by `/v1/boards`; both are accepted. 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":["Boards","Saves"],"security":[{"BearerAuth":[]},{"OAuth2":["boards:read","saves:read"]}],"parameters":[{"schema":{"type":"string","description":"The board `id`, or its `short_id` — both are returned by `/v1/boards`."},"required":true,"description":"The board `id`, or its `short_id` — both are 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"}}}}}}},"/v1/search":{"get":{"summary":"Search Savee's public library","description":"Full-text search across Savee's public library — not the caller's own saves. Results include the user who saved each item, since they come from across the platform.\n\nSearch is metered separately from the rest of the API and far more tightly: 20 searches per 5 minutes and 200 per week, reported as the `search-burst` and `search-weekly` policies in the `RateLimit` header. Pace against the weekly figure. Requires the `search:read` scope.\n\nPages hold at most 30 results (a larger `limit` is reduced to 30), and a query can be paged through its first 90 results only: `next_cursor` is `null` after the 90th, and a cursor past it returns `400`. Every page counts as a search.","tags":["Search"],"security":[{"BearerAuth":[]},{"OAuth2":["search:read"]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":30,"description":"Page size (default 30, max 30).","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"},{"schema":{"type":"string","minLength":2,"description":"What to search for. Must be at least 2 characters.","example":"brutalist poster"},"required":true,"description":"What to search for. Must be at least 2 characters.","name":"query","in":"query"}],"responses":{"200":{"description":"A page of matching 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"}}}}}}}},"webhooks":{}}