{
  "openapi": "3.1.0",
  "info": {
    "title": "Viral Swipe File API",
    "version": "1.0.0",
    "summary": "Read-only access to Viral Swipe File, the viral TikTok & Instagram video swipe file.",
    "description": "The viral video library for marketers and their AI agents: viral Instagram Reels and TikToks ranked by viral factor, with each video's hook, views, niche and format, from Claude, ChatGPT, Grok or the web.\n\nThe JSON endpoints under /api/v1 are read-only and need a Viral Swipe File account, free or paid: an OAuth 2.1 access token (`Authorization: Bearer`; authorization code with PKCE, scope `mcp:read`; the token MCP clients use works here) or, from a browser, a signed-in session cookie. Without one: 401 with `WWW-Authenticate: Bearer resource_metadata=\"https://viralswipefile.com/.well-known/oauth-protected-resource\"`. Every account gets paging, search, sorting and every filter. Connecting an AI agent is free on every plan, hooks and captions included, and any account can use the JSON API. Free: 50 questions a day (MCP tool calls and API requests together), counted from midnight UTC. Starter and Growth: no daily cap on agent questions; JSON API requests (bulk pulls) are 10,000 requests a month on Starter and 50,000 requests on Growth, counted from the 1st of each month (UTC). 30 requests a minute per account, reported in `RateLimit` headers. Pages as markdown, llms.txt, skill.md and this document need no account.\n\nFrom an AI assistant (the same library, plus your team's saved videos) use the MCP server at https://viralswipefile.com/api/mcp: 6 typed read-only tools, OAuth 2.1 with dynamic client registration and no API key, on the same allowance.\n\nErrors under /api/ are RFC 9457 problem details with `code`, `detail` and `resolution`.",
    "contact": {
      "name": "Viral Swipe File",
      "email": "hello@viralswipefile.com",
      "url": "https://viralswipefile.com/contact"
    },
    "termsOfService": "https://viralswipefile.com/terms"
  },
  "servers": [
    {
      "url": "https://viralswipefile.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "API reference and when to use it",
    "url": "https://viralswipefile.com/docs/api"
  },
  "tags": [
    {
      "name": "Library",
      "description": "Videos, niches and formats as JSON."
    },
    {
      "name": "Pages",
      "description": "Every page as markdown."
    },
    {
      "name": "Agents",
      "description": "Documents written for AI agents."
    },
    {
      "name": "Meta",
      "description": "Health and this description."
    }
  ],
  "x-mcp": {
    "endpoint": "https://viralswipefile.com/api/mcp",
    "name": "viralswipefile",
    "version": "1.2.0",
    "manifest": "https://viralswipefile.com/.well-known/mcp.json",
    "documentation": "https://viralswipefile.com/mcp",
    "tools": [
      "search_library",
      "list_videos",
      "get_video",
      "list_niches",
      "list_formats",
      "list_saved_videos"
    ],
    "prompts": [
      "what-to-post",
      "viral-videos-in-my-niche",
      "swipe-a-creator",
      "hooks-for-my-product",
      "map-a-niche"
    ],
    "description": "Streamable HTTP, OAuth 2.1. Every tool is read-only. Any account, within its monthly allowance."
  },
  "x-onboarding": {
    "freeTier": true,
    "apiKeyRequired": false,
    "accountRequired": true,
    "selfServe": true,
    "sandbox": false,
    "notes": "No API key is issued. A client registers itself (https://viralswipefile.com/api/auth/oauth2/register, dynamic client registration), the person signs in by email link, code or Google and approves, and the client receives an OAuth access token for scope mcp:read. That token works for the JSON API and the MCP server, for any account, free included, within its monthly allowance. Markdown pages and the agent documents need no account."
  },
  "paths": {
    "/api/v1/videos": {
      "get": {
        "operationId": "listVideos",
        "summary": "Viral videos for a niche, format, platform or creator",
        "description": "Videos with each one's hook, caption, creator, views, viral factor, engagement rate, niche, formats and page URL, newest first unless sorted. Filter by a niche or format slug (from listNiches / listFormats), a platform, or a creator on a platform. Needs an account; every plan gets paging, search, sort and every filter (up to 48 a page, offset up to 2400). Each request counts against the team's monthly allowance; past it, a 402.",
        "tags": [
          "Library"
        ],
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "niche",
            "in": "query",
            "required": false,
            "description": "A niche slug from listNiches, e.g. fitness.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "A format slug from listFormats.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "Only this platform. Required with `creator`.",
            "schema": {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram"
              ]
            }
          },
          {
            "name": "creator",
            "in": "query",
            "required": false,
            "description": "A username (letters, digits, `.` and `_`); needs `platform`.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._]{1,30}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many videos, up to 48.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 48,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "The previous page's nextOffset. Past the cap the answer is an empty page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 2400,
              "default": 0
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free text over niche, formats, hook, caption, creator and platform.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "`viral` = highest viral factor first; `views`; `recent` (the default order).",
            "schema": {
              "type": "string",
              "enum": [
                "viral",
                "views",
                "recent"
              ]
            }
          },
          {
            "name": "time_period",
            "in": "query",
            "required": false,
            "description": "Posted in the last 7, 30, 90 or 365 days.",
            "schema": {
              "type": "string",
              "enum": [
                "7d",
                "30d",
                "90d",
                "365d"
              ]
            }
          },
          {
            "name": "viral_factor",
            "in": "query",
            "required": false,
            "description": "Minimum viral factor.",
            "schema": {
              "type": "string",
              "enum": [
                "4",
                "8",
                "32"
              ]
            }
          },
          {
            "name": "content_type",
            "in": "query",
            "required": false,
            "description": "Video or photo posts.",
            "schema": {
              "type": "string",
              "enum": [
                "video",
                "photo"
              ]
            }
          },
          {
            "name": "creator_gender",
            "in": "query",
            "required": false,
            "description": "The creator's gender.",
            "schema": {
              "type": "string",
              "enum": [
                "male",
                "female"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Viral videos for a niche, format, platform or creator",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "VideoList",
                  "properties": {
                    "videos": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Video"
                      },
                      "maxItems": 48
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as offset for the next page; null when there is none."
                    }
                  },
                  "required": [
                    "videos",
                    "nextOffset"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A parameter is malformed or not supported.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "No account: send an OAuth access token or a session cookie. WWW-Authenticate names the protected-resource metadata.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "The team's monthly call allowance is used up; `resolution` links the next plan.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks mcp:read.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such niche, format, creator, page or endpoint.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; wait Retry-After seconds.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The request failed on our side.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/niches": {
      "get": {
        "operationId": "listNiches",
        "summary": "Every niche, with its slug and video count",
        "description": "Start here to filter videos by topic: every niche that has videos, with the slug listVideos takes and the niche's page URL. Needs an account (any plan). Each request counts against the team's monthly allowance; past it, a 402.",
        "tags": [
          "Library"
        ],
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every niche, with its slug and video count",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "NicheList",
                  "properties": {
                    "niches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Niche"
                      }
                    }
                  },
                  "required": [
                    "niches"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No account: send an OAuth access token or a session cookie. WWW-Authenticate names the protected-resource metadata.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "The team's monthly call allowance is used up; `resolution` links the next plan.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks mcp:read.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; wait Retry-After seconds.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The request failed on our side.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/formats": {
      "get": {
        "operationId": "listFormats",
        "summary": "Every video format, with its slug and video count",
        "description": "Every format (selfie with background, story slideshow, street interviews, …) that has videos, with the slug listVideos takes and the format's page URL. Needs an account (any plan). Each request counts against the team's monthly allowance; past it, a 402.",
        "tags": [
          "Library"
        ],
        "security": [
          {
            "oauth2": [
              "mcp:read"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every video format, with its slug and video count",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "FormatList",
                  "properties": {
                    "formats": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Format"
                      }
                    }
                  },
                  "required": [
                    "formats"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No account: send an OAuth access token or a session cookie. WWW-Authenticate names the protected-resource metadata.",
            "headers": {
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "The team's monthly call allowance is used up; `resolution` links the next plan.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks mcp:read.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; wait Retry-After seconds.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "The request failed on our side.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness and the running build",
        "description": "Whether the site is up, the build it is running and when that container started. Touches neither the database nor auth.",
        "tags": [
          "Meta"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Liveness and the running build",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "Health",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "buildId": {
                      "type": "string"
                    },
                    "startedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "ok",
                    "buildId",
                    "startedAt"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/{path}": {
      "get": {
        "operationId": "getPageAsMarkdown",
        "summary": "Any page as markdown, at its canonical URL",
        "description": "Any public page (home, niches, formats, a niche, format, platform, creator or video, about, contact, docs, terms, privacy) as markdown instead of HTML, at the same URL. Listings show their first 24 videos as a table with hooks and stats (the rest of a list is over the JSON API and MCP), then the listing's top hooks, formats or niches, creators, numbers and FAQ. An old `?page=N` on a listing redirects (308) to the listing. Unknown paths answer 404 with a markdown body saying where to look; a client that accepts neither markdown nor HTML gets 406. Enumerate pages from /sitemap.xml.",
        "tags": [
          "Pages"
        ],
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "A page path without the leading slash, e.g. niches/fitness. Empty for the home page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": true,
            "description": "`text/markdown`. q-values are honoured (RFC 9110); an equal-weight tie resolves to HTML, so browsers are unaffected.",
            "schema": {
              "type": "string",
              "const": "text/markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Any page as markdown, at its canonical URL",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such niche, format, creator, page or endpoint.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "406": {
            "description": "Neither markdown nor HTML is acceptable.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/md/{path}": {
      "get": {
        "operationId": "getMarkdownRendition",
        "summary": "The same markdown at its own URL",
        "description": "The markdown rendition at a URL of its own, for clients that cannot set request headers. Prefer content negotiation on the canonical URL; this one is marked noindex.",
        "tags": [
          "Pages"
        ],
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "A page path without the leading slash.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The same markdown at its own URL",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such niche, format, creator, page or endpoint.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getAgentIndex",
        "summary": "What this site covers, and when to use it",
        "description": "Read this first to decide whether the library can help: what it covers, when not to use it, the URL grammar and every machine-readable surface (llmstxt.org format).",
        "tags": [
          "Agents"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "What this site covers, and when to use it",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "operationId": "getAgentIndexFull",
        "summary": "Everything for an agent, in one file",
        "description": "llms.txt, the skill, the JSON API reference and every MCP tool with its description, in one fetch.",
        "tags": [
          "Agents"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Everything for an agent, in one file",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/skill.md": {
      "get": {
        "operationId": "getSkillDocument",
        "summary": "The agent skill",
        "description": "When to use the library and how to read and cite it, with front matter for skill loaders.",
        "tags": [
          "Agents"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The agent skill",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp.json": {
      "get": {
        "operationId": "getMcpServerCard",
        "summary": "The MCP server card",
        "description": "The MCP server's endpoint, transport, authorization metadata and documentation links.",
        "tags": [
          "Agents"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The MCP server card",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "McpServerCard",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "websiteUrl": {
                      "type": "string",
                      "format": "uri"
                    },
                    "remotes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          }
                        },
                        "required": [
                          "type",
                          "url"
                        ]
                      }
                    }
                  },
                  "required": [
                    "name",
                    "title",
                    "description",
                    "version",
                    "remotes"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "summary": "This document",
        "description": "The machine-readable description of every public HTTP surface.",
        "tags": [
          "Meta"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "This document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "OpenApiDocument",
                  "properties": {
                    "openapi": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "openapi"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemap",
        "summary": "Sitemap index of the public pages",
        "description": "A sitemap index: /sitemaps/pages.xml lists the home, platform, niche, format and editorial pages; /sitemaps/creators-{tiktok,instagram}.xml list the creator pages; /sitemaps/videos-{n}.xml list a curated set of video pages: of the videos with a viral factor of 3 or more, at least 100,000 views, a real hook and a cover, the top 4,000 by viral factor, at most 5 per creator and 500 per niche. Every other video page still works but is marked noindex. Use it to enumerate pages before reading them as markdown.",
        "tags": [
          "Pages"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Sitemap index of the public pages",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Video": {
        "type": "object",
        "title": "Video",
        "description": "One video, as its public page shows it.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The video's page on this site. Link the reader here."
          },
          "originalUrl": {
            "type": "string",
            "format": "uri",
            "description": "The post on TikTok or Instagram."
          },
          "platform": {
            "type": "string",
            "enum": [
              "tiktok",
              "instagram"
            ]
          },
          "creator": {
            "type": "string",
            "description": "The account's username."
          },
          "creatorUrl": {
            "type": "string",
            "format": "uri",
            "description": "The creator's page on this site."
          },
          "hook": {
            "type": [
              "string",
              "null"
            ],
            "description": "The opening line that stops the scroll."
          },
          "caption": {
            "type": [
              "string",
              "null"
            ]
          },
          "views": {
            "type": "number",
            "minimum": 0
          },
          "viralFactor": {
            "type": "number",
            "description": "Views divided by the account's usual views: 20 means twenty times its normal reach."
          },
          "engagementRate": {
            "type": "number",
            "description": "Engagement as a share of views (0.05 = 5%)."
          },
          "niche": {
            "type": [
              "string",
              "null"
            ]
          },
          "formats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "postedAt": {
            "type": "string",
            "format": "date-time"
          },
          "cover": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Cover image."
          }
        },
        "required": [
          "id",
          "url",
          "originalUrl",
          "platform",
          "creator",
          "creatorUrl",
          "hook",
          "caption",
          "views",
          "viralFactor",
          "engagementRate",
          "niche",
          "formats",
          "postedAt",
          "cover"
        ]
      },
      "Niche": {
        "type": "object",
        "title": "Niche",
        "properties": {
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `niche` to listVideos."
          },
          "name": {
            "type": "string"
          },
          "videoCount": {
            "type": "integer",
            "minimum": 0
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The niche's page, /niches/{slug}."
          }
        },
        "required": [
          "slug",
          "name",
          "videoCount",
          "url"
        ]
      },
      "Format": {
        "type": "object",
        "title": "Format",
        "properties": {
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `format` to listVideos."
          },
          "name": {
            "type": "string"
          },
          "videoCount": {
            "type": "integer",
            "minimum": 0
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The format's page, /formats/{slug}."
          }
        },
        "required": [
          "slug",
          "name",
          "videoCount",
          "url"
        ]
      },
      "Problem": {
        "type": "object",
        "title": "Problem",
        "description": "RFC 9457 problem details. Every error under /api/ has this shape: branch on `code`, show `detail`, follow `resolution`.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "const": "https://viralswipefile.com/docs/api#errors"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this request."
          },
          "instance": {
            "type": "string",
            "description": "The path that was requested."
          },
          "code": {
            "type": "string",
            "enum": [
              "not_found",
              "bad_request",
              "method_not_allowed",
              "rate_limited",
              "quota_exceeded",
              "unauthorized",
              "forbidden",
              "not_acceptable",
              "internal_error"
            ],
            "description": "Stable machine token. Closed set."
          },
          "resolution": {
            "type": "string",
            "description": "What to do next."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "resolution"
        ]
      }
    },
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE (S256 only), from this site's authorization server (issuer https://viralswipefile.com/api/auth; metadata https://viralswipefile.com/.well-known/oauth-authorization-server). Register a client at https://viralswipefile.com/api/auth/oauth2/register (dynamic client registration, public clients allowed). Tokens carry the audience https://viralswipefile.com/api/mcp: the same tokens the MCP server accepts. Ask for offline_access to get a refresh token.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://viralswipefile.com/api/auth/oauth2/authorize",
            "tokenUrl": "https://viralswipefile.com/api/auth/oauth2/token",
            "refreshUrl": "https://viralswipefile.com/api/auth/oauth2/token",
            "scopes": {
              "mcp:read": "Search viral videos, niches, formats and creators, read any video, and list your team's saved videos. Nothing is changed in your account.",
              "offline_access": "Keep this app connected between conversations without signing in again. Disconnect it any time from the app's connector settings."
            }
          }
        },
        "x-pkce-required": true,
        "x-dynamic-client-registration": "https://viralswipefile.com/api/auth/oauth2/register",
        "x-resource-metadata": "https://viralswipefile.com/.well-known/oauth-protected-resource"
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Secure-better-auth.session_token",
        "description": "A Better Auth session, set when you sign in on the site. For first-party and browser use; agents should use oauth2."
      }
    },
    "headers": {
      "WWW-Authenticate": {
        "description": "`Bearer resource_metadata=\"https://viralswipefile.com/.well-known/oauth-protected-resource\", scope=\"mcp:read\"` (RFC 6750 / RFC 9728), plus `error=\"invalid_token\"` when a token was refused.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Policy": {
        "description": "The quota per account, IETF draft structured field: `\"per-account\";q=<requests>;w=<seconds>`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "Where you stand: `\"per-account\";r=<remaining>;t=<seconds to reset>`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed per window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in this window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    }
  }
}