The Descript API lets you programmatically create projects, import media, and edit your projects — all without opening the app.
To sign up for access and learn more, visit descript.com/enterprise/api-early-access.
You can create a new project, import media, and place the media into a composition all in one API request using the import endpoint. This step also transcribes and processes the media so that it's ready for you or the agent to edit.
To import files, pass in public or pre-signed URIs. Currently, the API does not support uploading a file directly. To test the API with an example file, use the demo video included in the sample request below.
Importing and processing is an asynchronous job, so the response payload will contain a job_id for you to query the status of the job and information about the newly created project. Note that project_id and project_url are returned immediately alongside job_id, but opening the project in Descript will not always show the API's processing state in real time. To prevent unintended changes, we recommend that you do not make any changes to the project until the job has stopped.
Request
curl -X POST https://descriptapi.com/v1/jobs/import/project_media \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project_name": "My First Video",
"add_media": {
"demo.mp4": {
"url": "https://test-files.descriptapi.com/demo_video.mp4"
}
},
"add_compositions": [
{
"name": "Demo Video",
"clips": [
{ "media": "demo.mp4" }
]
}
]
}'
Response
{
"job_id": "project-media-import-9d635d5b",
"drive_id": "c9c5c47e",
"project_id": "e2f89ce6",
"project_url": "https://web.descript.com/e2f89ce6"
}
Poll the job status endpoint using the job_id from the last step to check whether the import job is finished processing. When it is, you'll see job_state: "stopped". You can then check the results object to see its full results.
You can also pass in a callback_url as a part of the first import request, and we'll ping you when the job has stopped with the same response payload.
Request
curl https://descriptapi.com/v1/jobs/project-media-import-9d635d5b \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response
{
"job_id": "project-media-import-9d635d5b",
"job_type": "import/project_media",
"job_state": "stopped",
"project_id": "e2f89ce6",
"project_url": "https://web.descript.com/e2f89ce6",
"result": {
"status": "success",
"media_status": {
"main.mp4": {
"status": "success",
"duration_seconds": 69.477006
}
},
"created_compositions": [
{ "id": "f8e5088a-4d53-4aab-9d4f-c6624b7d7622", "name": "Demo Video" }
]
}
}
Once your media is imported, you can use the agent edit endpoint to prompt Underlord for edits, just as you would in the app. Because it's an API, conversation and follow up questions aren't practical. So we recommend framing your edits as a one-shot prompt with all the information the agent needs.
Editing can take some time, so the response also returns a job_id that you can use to check the status of the job. You can also pass in a callback_url as a part of an agent request, and we'll ping you when the job has stopped.
Request
curl -X POST https://descriptapi.com/v1/jobs/agent \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project_id": "e2f89ce6",
"prompt": "Add studio sound and captions"
}'
Response
{
"job_id": "project-agent-edit-e2f89ce6",
"drive_id": "c9c5c47e",
"project_id": "e2f89ce6",
"project_url": "https://web.descript.com/e2f89ce6"
}
Poll the job status endpoint using the job_id. When the agent job completes successfully, the response includes a summary of what it accomplished (see the result.agent_response field). Use the project URL to open the project in Descript and review Underlord's changes.
Request
curl https://descriptapi.com/v1/jobs/project-agent-edit-e2f89ce6 \
-H "Authorization: Bearer YOUR_API_TOKEN"
Once the agent job is successfully complete, you’ll see a response from the agent with a brief summary of what it accomplished. You can then use the project url to review its changes directly in Descript.
Response
{
"job_id":"project-agent-edit-e2f89ce6",
"job_type":"agent",
"project_id":"YOUR_PROJECT_ID",
"project_url":"https://web.descript.com/e2f89ce6",
"job_state":"stopped",
"created_at":"2026-02-09T05:42:27.554Z",
"stopped_at":"2026-02-09T05:43:15.296Z",
"drive_id":"1df135a5-dc4a-4dc3-8f7d-681cfbe961e4",
"result":{
"status":"success",
"agent_response":"Done! I've applied Studio Sound to enhance your audio quality and added classic karaoke-style captions to your video.",
"project_changed":true,
"media_seconds_used":0,
"ai_credits_used":32
}
}
The CLI wraps the API into a simple to use command line tool with interactive flows for setting up authentication, importing, and prompting the agent. It also has built-in polling for job completion.
Before installing the CLI, you'll need Node.js installed on your computer. Node.js is a JavaScript runtime that allows you to run JavaScript programs from the command line.
Required version: Node.js 24 or higher
Open your terminal (or Command Prompt on Windows) and run:
node --version
If you see a version number like v24.x.x or higher, you're all set and can skip to the next section. If you see an error or a lower version number, follow the installation steps below.
Choose the installation method for your operating system:
Option 1: Using the installer (recommended for beginners)
Option 2: Using Homebrew
brew install node
Ubuntu/Debian:
# Install Node.js from NodeSource
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
Fedora/RHEL/CentOS:
# Install Node.js from NodeSource
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
sudo dnf install -y nodejs
After installation, verify it worked by running node --version again.
First, install the latest version of the CLI using npm.
npm install -g @descript/platform-cli@latest
Next, configure the CLI with your API key.
descript-api config set api-key

Use the import command to create a project by passing a project name and the link to any media you want to upload, or run descript-api import for interactive mode. The CLI shows live progress and outputs the project ID when done.
descript-api import \
--name "My First Project" \
--media "https://test-files.descriptapi.com/demo-video.mp4"

Use the agent command to edit a project by passing in the project id and an Underlord prompt.
descript-api agent \
--project-id YOUR_PROJECT_ID \
--prompt "Remove filler words and add Studio Sound to all clips"
You can also ask Underlord create a new project from a prompt alone by writing the script for you!
descript-api edit --new \
--prompt "Write a script about how to make great coffee"
Import media files into a new or existing project and create compositions.
This endpoint can:
project_id is not providedImports run in the background and return a job_id. Monitor progress via the GET /jobs/{job_id} endpoint.
If callback_url is provided, Descript will POST the job status to that URL when the job finishes (successfully or not).
The payload will match the format returned by GET /jobs/{job_id}.
Media import and project creation request
| project_id | string <uuid> [Work in progress] Importing into an existing project is not yet supported. Currently, a new project is always created. Existing project ID to import media into. If not provided, a new project will be created. When importing into an existing project, media filenames must not conflict with existing files. |
| project_name | string Name for the new project. Only used when project_id is not provided. |
| team_access | string Enum: "edit" "comment" "none" [Work in progress] This property is not yet supported and will be ignored if provided. Access level for the project's drive. Only applicable when creating a new project (when project_id is not provided).
|
object Map of media reference IDs (display names with optional folder paths) to media import items. Keys are the display names that will appear in the project (e.g., "Misc/intro.mp4" or "demo.mp4"). Values define how to import each media item (URL import or multitrack sequence). | |
Array of objects Optional list of compositions to create in the project | |
| callback_url | string <uri> Optional webhook URL to call when the job completes or fails. Descript will POST the job status (same format as GET /jobs/{job_id}) to this URL. |
Most common use case for seeding a project with a simple composition
{- "project_name": "Marketing Video",
- "add_media": {
}, - "add_compositions": [
- {
- "name": "Rough Cut",
- "clips": [
- {
- "media": "Misc/intro.mp4"
}, - {
- "media": "demo.mp4"
}, - {
- "media": "Misc/outro.mp4"
}
]
}
]
}{- "job_id": "6dc3f30a-58c2-4174-96a6-dc18cf3c7776",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
}Use a background agent to create and edit projects using a natural language prompt.
project_id to edit an existing projectproject_name instead of project_id to create a new projectAgent edits run in the background and return a job_id. Monitor progress via the GET /jobs/{job_id} endpoint.
If callback_url is provided, Descript will POST the job status to that URL when the job completes or fails.
The payload will match the format returned by GET /jobs/{job_id}.
AI agent request
| project_id | string <uuid> The ID of an existing project to edit. Mutually exclusive with |
| project_name | string Name for creating a new project. Mutually exclusive with |
| composition_id | string <uuid> [Work in progress] This property is accepted but not yet functional. It will be ignored. Optional composition ID within the project to edit. |
| model | string AI model to use for editing. Defaults to the default model. |
| prompt required | string Natural language instruction for the agent to execute. Examples: "add studio sound to every clip", "remove all filler words", "create a 30-second highlight reel" |
| callback_url | string <uri> Optional webhook URL to call when the job completes or fails. Descript will POST the job status (same format as GET /jobs/{job_id}) to this URL. |
{- "project_name": "Cooking Tips Video",
- "prompt": "create a 30-second video about cooking tips with background music"
}{- "job_id": "6dc3f30a-58c2-4174-96a6-dc18cf3c7776",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
}Work in progress — This endpoint is not yet available and will return an error if called.
List recent jobs with optional filtering by project or job type.
Query parameters allow you to filter the results:
project_id to see all jobs for a projecttype to see specific job types (import/project_media, agent)| project_id | string <uuid> Filter by project ID |
| type | string Enum: "import/project_media" "agent" Filter by job type |
[- {
- "job_id": "6dc3f30a-58c2-4174-96a6-dc18cf3c7776",
- "job_type": "import/project_media",
- "job_state": "stopped",
- "created_at": "2025-11-18T10:30:00Z",
- "stopped_at": "2025-11-18T10:35:00Z",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
- "result": {
- "status": "success",
- "media_status": {
- "Misc/intro.mp4": {
- "status": "success",
- "duration_seconds": 10.5
}
}, - "media_seconds_used": 11
}
}, - {
- "job_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
- "job_type": "agent",
- "job_state": "running",
- "created_at": "2025-11-18T11:00:00Z",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
}, - {
- "job_id": "11223344-5566-7788-99aa-bbccddeeff00",
- "job_type": "import/project_media",
- "job_state": "stopped",
- "created_at": "2025-11-18T08:45:00Z",
- "stopped_at": "2025-11-18T08:46:00Z",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
- "result": {
- "status": "error",
- "error_message": "Failed to download media file from URL"
}
}, - {
- "job_id": "22334455-6677-8899-aabb-ccddeeff0011",
- "job_type": "agent",
- "job_state": "cancelled",
- "created_at": "2025-11-18T08:30:00Z",
- "stopped_at": "2025-11-18T08:31:00Z",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
}
]Retrieve the status of any job.
The response format varies based on job type and includes type-specific fields.
| job_id required | string <uuid> The job ID |
{- "job_id": "6dc3f30a-58c2-4174-96a6-dc18cf3c7776",
- "job_type": "import/project_media",
- "job_state": "stopped",
- "created_at": "2025-11-18T10:30:00Z",
- "stopped_at": "2025-11-18T10:35:00Z",
- "drive_id": "c9c5c47e-158a-49f7-846b-4f6ee2a229a2",
- "project_id": "9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb",
- "result": {
- "status": "success",
- "media_status": {
- "Misc/intro.mp4": {
- "status": "success",
- "duration_seconds": 10.5
}, - "demo.mp4": {
- "status": "success",
- "duration_seconds": 125
}
}, - "media_seconds_used": 136,
- "created_compositions": [
- {
- "id": "0171c",
- "name": "Rough Cut"
}
]
}
}Work in progress — This endpoint is not yet available and will return an error if called.
Cancel a running job.
| job_id required | string <uuid> The job ID |
{- "error": "unauthorized",
- "message": "Missing or invalid authentication token"
}Work in progress — This endpoint is not yet available and will return an error if called.
Check API availability and validate authentication token.
This endpoint can be used to:
Returns a success response if the token is valid, or a 401 error if the token is invalid or missing.
{- "status": "ok"
}Descript API uses a personal token to authenticate your request. You can get your personal token by contacting us.
The personal token should be used as a bearer token in the authorization header.
Example
curl -H "authorization: Bearer ${YOUR_PERSONAL_TOKEN}" -d @datafile.json https://descriptapi.com/v1/edit_in_descript/schema
The Descript API implements rate limiting to ensure fair usage and protect service availability.
When you exceed the rate limit, the API returns a 429 Too Many Requests response.
When a rate limit is exceeded, the response includes the following headers:
| Header | Description |
|---|---|
Retry-After |
Number of seconds to wait before retrying the request |
X-RateLimit-Remaining |
Number of requests remaining in the current window |
X-RateLimit-Consumed |
Number of requests consumed in the current window |