curl "https://api.hooked.so/v1/project/list?type=script-to-video&status=completed&limit=20" \
-H "x-api-key: your_api_key_here"
const params = new URLSearchParams({ type: "script-to-video", status: "completed", limit: "20" });
const response = await fetch(`https://api.hooked.so/v1/project/list?${params}`, {
headers: { "x-api-key": process.env.HOOKED_API_KEY },
});
const { data } = await response.json();
console.log(`${data.projects.length} of ${data.total} projects`);
for (const project of data.projects) {
console.log(project.name, project.status, project.video?.url ?? "(no video yet)");
}
import os
import requests
response = requests.get(
"https://api.hooked.so/v1/project/list",
params={"type": "script-to-video", "status": "completed", "limit": 20},
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
)
data = response.json()["data"]
print(f"{len(data['projects'])} of {data['total']} projects")
{
"success": true,
"message": "Projects fetched successfully",
"data": {
"projects": [
{
"id": "cm4x9q1r30005ab12gh34ij78",
"name": "Evening routine tips",
"type": "script_to_video",
"status": "processing",
"source": "api",
"progress": 35,
"message": "Generating media...",
"usedCredits": 42,
"metadata": {},
"createdAt": "2026-10-01T11:02:00.000Z",
"updatedAt": "2026-10-01T11:03:10.000Z",
"video": null
},
{
"id": "cm4x9k2p10001ab12cd34ef56",
"name": "Morning routine tips",
"type": "script_to_video",
"status": "completed",
"source": "api",
"progress": 100,
"message": "Video ready",
"usedCredits": 42,
"metadata": { "campaignId": "spring-launch" },
"createdAt": "2026-10-01T09:12:03.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z",
"video": {
"id": "cm4x9n7q20003ab12xy98zt10",
"name": "Morning routine tips",
"projectId": "cm4x9k2p10001ab12cd34ef56",
"projectType": "script_to_video",
"status": "COMPLETED",
"progress": 100,
"message": "Video ready",
"url": "https://files.hooked.so/team/videos/cm4x9n7q20003ab12xy98zt10.mp4?X-Amz-Signature=...",
"thumbnail": "https://files.hooked.so/team/thumbnails/cm4x9n7q20003ab12xy98zt10.jpg?X-Amz-Signature=...",
"durationInFrames": 1125,
"durationInSeconds": 45,
"size": 18874368,
"createdAt": "2026-10-01T09:15:58.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z"
}
}
],
"total": 12,
"limit": 2,
"offset": 0
}
}
{
"success": false,
"message": "status: Must be one of draft, processing, completed, failed"
}
List Projects
List your team’s projects, newest first, with their latest video
curl "https://api.hooked.so/v1/project/list?type=script-to-video&status=completed&limit=20" \
-H "x-api-key: your_api_key_here"
const params = new URLSearchParams({ type: "script-to-video", status: "completed", limit: "20" });
const response = await fetch(`https://api.hooked.so/v1/project/list?${params}`, {
headers: { "x-api-key": process.env.HOOKED_API_KEY },
});
const { data } = await response.json();
console.log(`${data.projects.length} of ${data.total} projects`);
for (const project of data.projects) {
console.log(project.name, project.status, project.video?.url ?? "(no video yet)");
}
import os
import requests
response = requests.get(
"https://api.hooked.so/v1/project/list",
params={"type": "script-to-video", "status": "completed", "limit": 20},
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
)
data = response.json()["data"]
print(f"{len(data['projects'])} of {data['total']} projects")
{
"success": true,
"message": "Projects fetched successfully",
"data": {
"projects": [
{
"id": "cm4x9q1r30005ab12gh34ij78",
"name": "Evening routine tips",
"type": "script_to_video",
"status": "processing",
"source": "api",
"progress": 35,
"message": "Generating media...",
"usedCredits": 42,
"metadata": {},
"createdAt": "2026-10-01T11:02:00.000Z",
"updatedAt": "2026-10-01T11:03:10.000Z",
"video": null
},
{
"id": "cm4x9k2p10001ab12cd34ef56",
"name": "Morning routine tips",
"type": "script_to_video",
"status": "completed",
"source": "api",
"progress": 100,
"message": "Video ready",
"usedCredits": 42,
"metadata": { "campaignId": "spring-launch" },
"createdAt": "2026-10-01T09:12:03.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z",
"video": {
"id": "cm4x9n7q20003ab12xy98zt10",
"name": "Morning routine tips",
"projectId": "cm4x9k2p10001ab12cd34ef56",
"projectType": "script_to_video",
"status": "COMPLETED",
"progress": 100,
"message": "Video ready",
"url": "https://files.hooked.so/team/videos/cm4x9n7q20003ab12xy98zt10.mp4?X-Amz-Signature=...",
"thumbnail": "https://files.hooked.so/team/thumbnails/cm4x9n7q20003ab12xy98zt10.jpg?X-Amz-Signature=...",
"durationInFrames": 1125,
"durationInSeconds": 45,
"size": 18874368,
"createdAt": "2026-10-01T09:15:58.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z"
}
}
],
"total": 12,
"limit": 2,
"offset": 0
}
}
{
"success": false,
"message": "status: Must be one of draft, processing, completed, failed"
}
Overview
Returns your team’s projects, newest first, with the total count for paging. Projects made through the API and projects made in the dashboard are both listed; templates are not. Each item is the same object Get Project returns: itsstatus, progress, the metadata you sent, and its latest render under video (null until the render starts). Unlike List Videos, a project is listed from the moment it is created, so you can find one that is still generating or that failed before rendering.
curl "https://api.hooked.so/v1/project/list?type=script-to-video&status=completed&limit=20" \
-H "x-api-key: your_api_key_here"
const params = new URLSearchParams({ type: "script-to-video", status: "completed", limit: "20" });
const response = await fetch(`https://api.hooked.so/v1/project/list?${params}`, {
headers: { "x-api-key": process.env.HOOKED_API_KEY },
});
const { data } = await response.json();
console.log(`${data.projects.length} of ${data.total} projects`);
for (const project of data.projects) {
console.log(project.name, project.status, project.video?.url ?? "(no video yet)");
}
import os
import requests
response = requests.get(
"https://api.hooked.so/v1/project/list",
params={"type": "script-to-video", "status": "completed", "limit": 20},
headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
)
data = response.json()["data"]
print(f"{len(data['projects'])} of {data['total']} projects")
{
"success": true,
"message": "Projects fetched successfully",
"data": {
"projects": [
{
"id": "cm4x9q1r30005ab12gh34ij78",
"name": "Evening routine tips",
"type": "script_to_video",
"status": "processing",
"source": "api",
"progress": 35,
"message": "Generating media...",
"usedCredits": 42,
"metadata": {},
"createdAt": "2026-10-01T11:02:00.000Z",
"updatedAt": "2026-10-01T11:03:10.000Z",
"video": null
},
{
"id": "cm4x9k2p10001ab12cd34ef56",
"name": "Morning routine tips",
"type": "script_to_video",
"status": "completed",
"source": "api",
"progress": 100,
"message": "Video ready",
"usedCredits": 42,
"metadata": { "campaignId": "spring-launch" },
"createdAt": "2026-10-01T09:12:03.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z",
"video": {
"id": "cm4x9n7q20003ab12xy98zt10",
"name": "Morning routine tips",
"projectId": "cm4x9k2p10001ab12cd34ef56",
"projectType": "script_to_video",
"status": "COMPLETED",
"progress": 100,
"message": "Video ready",
"url": "https://files.hooked.so/team/videos/cm4x9n7q20003ab12xy98zt10.mp4?X-Amz-Signature=...",
"thumbnail": "https://files.hooked.so/team/thumbnails/cm4x9n7q20003ab12xy98zt10.jpg?X-Amz-Signature=...",
"durationInFrames": 1125,
"durationInSeconds": 45,
"size": 18874368,
"createdAt": "2026-10-01T09:15:58.000Z",
"updatedAt": "2026-10-01T09:16:41.000Z"
}
}
],
"total": 12,
"limit": 2,
"offset": 0
}
}
{
"success": false,
"message": "status: Must be one of draft, processing, completed, failed"
}
Filtering by format
type takes the name of the create endpoint that made the project. Each project answers with its stored type, and that value works as a filter too.
type filter | Stored type |
|---|---|
script-to-video | script_to_video |
prompt-to-video | prompt_to_video |
ugc-ads, ugc-studio | ugc_ads (every UGC Studio format) |
talking-avatar | ugc_video |
hook-demo | hook_demo |
podcast-interview | podcast_interview |
scenes | scenes |
tiktok-slideshow | tiktok_slideshow |
quiz-video | quiz_video |
reddit-story | reddit_story |
scrolling-video | scrolling_video |
article-to-video | article_to_video |
pdf-to-video | pdf_to_video |
pdf-to-brainrot | pdf_to_brainrot |
music-to-video | music_to_video |
clone-video | clone_video |
cinematic | cinematic |
add-captions | caption_video |
extend-video | extend_video |
remove-background | remove_background |
product_ads) are listed and can be filtered by their stored type.
source=api keeps only the projects made through the API; source=web only the ones made in the dashboard (its automations included).
Paging
Passlimit (1 to 100, default 50) and offset. You have every project when offset + limit >= total. video.url is a signed link that expires: read the project again for a fresh one instead of storing it.
Errors
| Status | When |
|---|---|
| 400 | Unknown status, type or source. message names the parameter |
| 401 | Missing or invalid API key (code: "not_authenticated") |
| 403 | The team does not have the Hooked app product (code: "entitlement_required") |
| 500 | Failed to fetch projects. Retry the request |
Authorizations
Query Parameters
Only projects in this state. Case-insensitive; any other value answers 400.
draft, processing, completed, failed Only projects of this format, named by its create endpoint: add-captions, article-to-video, cinematic, clone-video, extend-video, hook-demo, music-to-video, pdf-to-brainrot, pdf-to-video, podcast-interview, prompt-to-video, quiz-video, reddit-story, remove-background, scenes, script-to-video, scrolling-video, talking-avatar, tiktok-slideshow, ugc-ads, ugc-studio. ugc-ads and ugc-studio are the same type (ugc_ads), so either lists both. The stored type a project answers with (script_to_video, caption_video, …) works too. all or left out: every type. Any other value answers 400.
Only projects made through the API (api) or in the dashboard (web). Both when left out; any other value answers 400.
api, web Page size, 1 to 100. Larger values are capped at 100; missing or invalid values fall back to 50.
1 <= x <= 100Projects to skip. Missing or invalid values fall back to 0. You are done when offset + limit >= total.
x >= 0