Skip to main content

List Videos

GET /v1/videos

List all videos in your workspace with pagination and optional status filtering.

Query parameters

ParameterTypeRequiredDefaultDescription
pageintegerNo1Page number (minimum 1)
limitintegerNo20Results per page (1–100)
statusstringNoFilter by status: draft, in_progress, or completed
projectIdstringNoReturn only videos in the given project folder. 404 if the folder doesn't belong to the workspace.
uncategorizedbooleanNoIf true, return only videos with no project folder. Overrides projectId.
backupstringNoFilter by package backup state: none returns videos that have never had a package built, taken returns those that have. See Package backups.
approvalstringNoFilter by the latest approval decision: pending, approved, rejected or withdrawn.
kbstringNoFilter by Knowledge Base state: published, not_published (no article, or only drafts) or none (no article was ever created). See Knowledge Base state.
languageGroupstringNoReturn every language of one instruction. See Language families.

Example

curl "https://api.vidocu.ai/v1/videos?page=1&limit=10&status=completed" \
-H "Authorization: Bearer vdo_live_your_key_here"

Response 200 OK

{
"videos": [
{
"id": "vid_1708300000000_abc1234",
"projectId": "0f7c1d8a-2c4b-4a9c-9f2a-1b8a3a5e0c11",
"name": "Product Demo Q1",
"status": "completed",
"videoUrl": "https://storage.vidocu.ai/...",
"thumbnailUrl": "https://storage.vidocu.ai/...",
"videoDuration": 120.5,
"language": "en",
"exportedVideoUrl": "https://storage.vidocu.ai/...",
"exportedSrtUrl": "https://storage.vidocu.ai/...",
"exportedVoiceoverUrl": "https://storage.vidocu.ai/...",
"exportedAt": "2024-02-19T10:30:00.000Z",
"createdAt": "2024-02-18T14:00:00.000Z",
"updatedAt": "2024-02-19T10:30:00.000Z"
}
],
"pagination": {
"total": 42,
"page": 1,
"limit": 10,
"totalPages": 5
}
}

Video object fields

FieldTypeDescription
idstringUnique video ID
projectIdstring | nullParent project folder ID (null when uncategorized)
namestringDisplay name
statusstringdraft, in_progress, or completed
videoUrlstringSource video URL
thumbnailUrlstring | nullGenerated thumbnail URL
videoDurationnumber | nullDuration in seconds
languagestring | nullDetected language code
exportedVideoUrlstring | nullExported video URL (after export)
exportedSrtUrlstring | nullExported SRT file URL (after export)
exportedVoiceoverUrlstring | nullCombined voiceover MP3 URL (after export, if voiceover was generated)
exportedAtstring | nullExport timestamp
lockedbooleanWhether the video's content is frozen (see Lock state)
lockobject | nullWho locked it and when, when locked is true
packageDownloadobject | nullThe last package backup, or null if none was ever built. See Package backups.
languageGroupstring | nullThe language family this package belongs to, or null if it was never translated. See Language families.
translatedFromstring | nullThe package this one was translated from, or null.
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Results are sorted by updatedAt descending (most recent first).

Errors

StatusCodeCause
400validation_errorInvalid query parameter values
401authentication_errorInvalid or missing API key

Cursor pagination

page works, but it drifts on a collection that changes while you walk it: a video created between page 1 and page 2 shifts everything down, so you see an item twice or miss it entirely. Anything polling on a schedule hits that.

Every list response includes pagination.nextCursor. Pass it back as cursor to resume exactly after the last row you saw, regardless of what was inserted meanwhile:

# first page
curl "https://api.vidocu.ai/v1/videos?limit=50" -H "Authorization: Bearer ..."

# next page — cursor replaces page
curl "https://api.vidocu.ai/v1/videos?limit=50&cursor=eyJ0IjoiMjAyNi0wOC0wOVQxMDowMDowMC4wMDBaIiwiaWQiOiJ2aWRfMiJ9" \
-H "Authorization: Bearer ..."

nextCursor is null on the last page. Treat the value as opaque - it's a token to hand back, not a structure to build. The same applies to GET /v1/jobs.

Package backups

A package is the offline evidence bundle for one video: the exported MP4, the article PDF, the training material and a manifest recording lock status, publisher, approvers and content hashes. packageDownload records the last time one was produced.

FieldTypeDescription
downloadedAtstringWhen the package finished building and its link was sent
downloadedBystringUser ID of the person who asked for it
downloadedByNamestring | nullTheir display name at that moment

null means no package has ever been built for this video.

The stamp is written when the ZIP finishes building, not when someone clicks the emailed link. The build is the observable event, and it is the one that answers "has a backup been taken" — an email nobody opened does not un-take it.

The filter exists for one question in particular: which published content has no offline backup yet.

curl "https://api.vidocu.ai/v1/videos?status=completed&backup=none" \
-H "Authorization: Bearer vdo_live_your_key_here"

Knowledge Base state

kb answers a question the Knowledge Base's own listings structurally cannot: which content never got there. A video with no article does not appear on any article listing, so its absence is invisible exactly where somebody would look for it.

ValueReturns
publishedVideos with at least one live Knowledge Base article
not_publishedVideos that never went live there — no article at all, or only drafts
noneThe narrower case: no article was ever created from the video

State is resolved from the articles themselves on every request rather than cached on the video, so it cannot go stale after an article is archived or deleted.

# Approved, signed off, and never published to the Knowledge Base
curl "https://api.vidocu.ai/v1/videos?approval=approved&kb=not_published" \
-H "Authorization: Bearer vdo_live_your_key_here"

Language families

Translating a package creates a second package: its own id, its own approval, its own lock, its own Knowledge Base article. That is deliberate — a work instruction and its translation are two documents a quality system signs off separately, and freezing one should not freeze the other.

What connects them is languageGroup: the id of the first package in the family, carried by every member including the source. Ask for it and you get the whole set.

# Every language of one instruction, the source included
curl "https://api.vidocu.ai/v1/videos?languageGroup=vid_abc123" \
-H "Authorization: Bearer vdo_live_your_key_here"

The source's own id works as the group id, so you can ask with the id you already have rather than fetching the video first to read its languageGroup.

Each member carries its own language, so pairing a family with an ERP record is a match on languageGroup and a filter on language.

Two things are worth knowing before you build against this:

  • The training is shared, not copied. Every package in a family points at the same training module, which carries its own per-language translations. Somebody who passed the assessment in German has passed it — the Training Planner has one row per person per training, not one per language.
  • languageGroup is null on packages translated before this shipped. The field is written when a package is translated, and no migration invented families for older translations. A package with null is a family of one as far as the API is concerned; translate it again and it starts a real family.