---
title: "List the user's source videos (detected uploads + API/dashboard uploads), newest first, 50 per page"
method: GET
path: "/api/v1/videos"
---

# List the user's source videos (detected uploads + API/dashboard uploads), newest first, 50 per page

`GET /api/v1/videos`

## Request

- Base URL: `https://everpop.app`
- URL: `https://everpop.app/api/v1/videos`
- Auth: HTTP bearer

Each video says whether it can be clipped (clippable) and, if not, why (notClippableReason — the code POST /api/v1/clip would return). Pages with an opaque cursor: pass the envelope's nextCursor back as ?cursor= until it is null.

## Query parameters

- `limit` integer
- `cursor` string

## Response `200`

One page of the library.

- VideoListEnvelope
  - `ok` unknown, required
  - `data` VideoItem[], required
    - `id` string, required
    - `title` string, required
    - `status` string, required
    - `detectedAt` string, date-time, required
    - `detectionMethod` string, required
    - `clippable` boolean, required: Whether POST /api/v1/clip accepts this video — the same check the clip call runs.
    - `notClippableReason` 'upload_required' | 'generated_source' | 'demo_source' | 'null', nullable, required: When clippable is false: the code POST /api/v1/clip would 422 with. upload_required = upload the file (POST /api/v1/videos/upload, then /complete with linkSourceVideoId = this id).
    - `uploadUrl` string, uri, nullable, required: When notClippableReason is upload_required: the sign-in-gated Everpop page where the user uploads this video's file (pre-attached to this video). Give it to the user when you can't send file bytes yourself. null otherwise.
    - `latestJob` LatestJob, required
      - `jobId` string, required
      - `status` 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED' | 'CANCELED', required
      - `error` string, nullable, required: Customer-safe failure detail when the job did not complete.
      - `createdAt` string, date-time, required
  - `nextCursor` string, nullable, required: Pass back as ?cursor= for the next (older) page; null on the last page.
  - `browserUploadUrl` string, uri, required: The sign-in-gated Everpop page where the user uploads a new file themselves. Give it to the user when you can't send file bytes; unlike POST /api/v1/videos/upload it opens no upload slot.

## Other responses

- `401`: The API key did not authenticate. code=key_missing: no Authorization: Bearer epk_... header. code=key_invalid: the key is unknown or revoked. code=key_expired: the key passed its expiry; the user creates a new one in Dashboard → API.
- `402`: REST and MCP need the Pro or Scale plan. reason=subscription: the account has no active subscription (subscribe, or redeem an invite code). reason=plan: the account's plan doesn't include this (API and agent access need Pro or Scale).
- `403`: code=scope (also reason=scope): the API key is read-only and this is a write. code=suspended: the account is suspended. Endpoint-specific refusals document their own reason.
- `422`: Invalid limit, or code=invalid_cursor (pass nextCursor back unchanged).
- `429`: Too many requests. code=rate_limited: the per-minute limit; wait Retry-After (60) seconds. code=quota_exhausted (with used, limit, resetsAt, retryable: false): the monthly allowance is used up; Retry-After is the seconds until resetsAt — tell the user rather than wait. code=queue_capacity (retryable: true): the render queue is full; retry after Retry-After (180).

---

[API](https://skmtc.dev/everpop/apis/everpop-api.md) · [All operations](https://skmtc.dev/everpop/apis/everpop-api/llms.txt) · [OpenAPI document](https://skmtc.dev/everpop/apis/everpop-api/revisions/1a9fb20867f5?raw)
