Scene Cut Detection
This is the API documentation to our tool "Scene Cut Detection". The tool automatically detects scene changes in videos and generates EDL and CSV files containing scene timecodes. These files can be used for further editing in Avid Media Composer, Adobe Premiere Pro, DaVinci Resolve, or Apple Final Cut Pro. Individual files can also be rendered for each detected clip. The tool offers content-aware and threshold-based detection algorithms.
Quick Start
This guide shows a quick way to start scene cut detection processes for files up to 100 MB using our API.
Endpoint
This endpoint is used to start a new scene cut detection.
POST https://api.editingtools.io/v2/cutdetection
Authentication
This API requires Basic Authentication. The "Authorization" header should be set with the Base64 encoded string of "apikey:YOUR_API_KEY".
Authorization: Basic <base64Encoded(apikey:YOUR_API_KEY)>
Headers
Content-Type: multipart/form-data
Specifies the format of the request body.
Authorization: Basic <base64EncodedString>
Authentication credential.
Request Body
The API supports file uploads using a multipart/form-data request. The request can contain the following parameters:
Parameter | Type | Required | Description |
---|---|---|---|
file |
File | Yes | The media file to process. Possible file formats:
|
algorithm |
String | No | This defines the algorithm to be used. Possible values:
|
subclips |
Boolean | No | Whether subclips should be rendered. Default is 0 . Set to 1 to enable.This option renders an H264 (.mp4) file for each detected clip. Note that the accuracy of the renders depends on the accuracy of the scene detection. |
notification |
Boolean | No | Whether to send an email notification upon completion. Default is false . Set to true to enable.
|
cURL Examples
The following examples show how to use the API with cURL commands. If you are using a different language, we recommend using https://curlconverter.com to convert the commands for other languages like: PHP, JavaScript, Python, Ruby, Rust, Ansible, C , C#, ColdFusion, Clojure, Dart, Elixir, Go, HAR, HTTP, HTTPie, Java, JSON, Kotlin, MATLAB, Node.js, Objective-C, OCaml, PowerShell, R, Swift and more.
cURL Request Example 1
This is an example of how to start a simple scene cut detection with a mp4
file.
curl -X POST https://api.editingtools.io/v2/cutdetection \
-u "apikey:YOUR_API_KEY" \
-F file=@/Users/Dave/Movies/Trailer.mp4
cURL Request Example 2
This is an example of how to start a scene cut detection process using the threshold
algorithm, generate subclips, and receive an email notification once the process is finished.
curl -X POST https://api.editingtools.io/v2/cutdetection \
-u "apikey:YOUR_API_KEY" \
-F file=@/Users/Dave/Movies/Trailer.mp4 \
-F algorithm=threshold \
-F subclips=1 \
-F notification=1
Responses
Success (HTTP 200 OK / 201 Created)
If the file was successfully uploaded and the process created, the API will return a 201 Created
status code with a JSON object containing the process ID and an estimate of the process time in seconds.
{
"code": 201,
"status": "The upload was successful, and the process was created. Cut detection of your file will begin shortly.",
"timestamp": "2025-05-01T10:00:00Z",
"data": {
"processId": "APIA4F6F4DF50",
"processDurationEstimate": 60
}
}
Field | Type | Description |
---|---|---|
code |
String | HTTP status code. |
status |
String | A descriptive message about the process. |
timestamp |
String | Server time of the request (ISO 8601). |
data |
Object | An object containing details about the process. |
data.processId |
String | A unique identifier for the process. |
data.processDurationEstimate |
Int | The estimated time until the process is finished given in seconds. The minimum value is 60 by default. |
Error Responses
Error responses typically include an appropriate HTTP status code and a JSON body containing an error message.
{
"code": "400",
"status": "The upload could not be completed."
}
Get Process Status
After the scene cut detection process is successfully started, this endpoint can be used to check its status. Use the processId
returned in the first response for the request. Only processes started with the own account can be checked.
Endpoint
GET https://api.editingtools.io/v2/process/{processId}
Replace {processId}
with the unique ID of the process.
Headers
Content-Type: multipart/form-data
Specifies the format of the request body.
Authorization: Basic <base64EncodedString>
Authentication credential.
Path Parameters
Parameter | Type | Description |
---|---|---|
processId |
String | The unique identifier of the process. |
cURL Request Example
curl -X GET https://api.editingtools.io/v2/process/{processId} \
-u "apikey:YOUR_API_KEY"
Responses
Field | Type | Description |
---|---|---|
code |
String | HTTP status code. |
status |
String | A descriptive message about the update. |
timestamp |
String | Server time of the request (ISO 8601). |
data |
Object | An object containing details about the process. |
data.processId |
String | Unique identifier of the process. |
data.created |
String | Timestamp of process creation (ISO 8601). |
data.lastUpdate |
String | Timestamp of last process update (ISO 8601). |
data.status |
String | Status string of process |
data.label |
String | Description of status. |
data.fileName |
String | Filename of generated file. (Only returned if a file was created) |
data.fileUrl |
String | Download path of file. (Only returned if a file was created) |
Success (HTTP 200 OK)
If the process is successfully loaded, the API will return a 200
status code with a JSON object.
Example Response 1 - Process not completed
If the process is still running, a response like this could be given:
{
"code": "202",
"status": "Accepted",
"timestamp": "2025-05-01T10:04:10Z",
"data": {
"created": "2025-05-01T10:00:00Z",
"lastUpdate": "2025-05-01T10:01:00Z",
"status": "in_progress",
"label": "The process is still running."
}
}
Example Response 2 - Process completed
The download path will be displayed once the process is complete.
{
"code": "200",
"status": "OK",
"timestamp": "2025-05-01T10:00:00Z",
"data": {
"created": "2025-05-01T10:00:00Z",
"lastUpdate": "2025-05-01T10:05:20Z",
"status": "finished",
"label": "The Process is finished."
"fileName": "Trailer.zip",
"fileUrl": "https://editingtools.io/download/demo/path/Trailer.zip"
}
}
The generated file can be downloaded using wget
or curl
, or it can be embedded directly into your app from fileUrl. Note that the generated file will only be hosted online for a limited time.
Error Responses
If the process failed, a response like this could be given:
{
"code": "422",
"status": "Unprocessable Entity",
"timestamp": "2025-05-01T10:04:10Z",
"data": {
"created": "2025-05-01T10:00:00Z",
"lastUpdate": "2025-05-01T10:05:20Z",
"status": "failed",
"label": "The Process failed."
}
}
Download File
After the process is successfully completed, the file can be downloaded using the values returned in the process response under fileUrl
and fileName
.
cURL Request Example (macOS or Linux)
Example to download the file to the Desktop. Uses -o
to specify the output file path.
curl -o ~/Desktop/{fileName} "{fileUrl}"
curl -o ~/Desktop/Trailer.zip "https://editingtools.io/download/demo/path/Trailer.zip"
cURL Request Example (Windows with PowerShell)
curl -o "$env:USERPROFILE\Desktop\{fileName}" "{fileUrl}"
curl -o $env:USERPROFILE\Desktop\Trailer.zip "https://editingtools.io/download/demo/path/Trailer.zip"
cURL Request Example (Windows Command Prompt with cURL installed)
curl -o "%USERPROFILE%\Desktop\{fileName}" "{fileUrl}"
curl -o %USERPROFILE%\Desktop\Trailer.zip "https://editingtools.io/download/demo/path/Trailer.zip"
Python3 Request Example
import os
import urllib.request
name = "Trailer.zip"
url = "https://editingtools.io/download/demo/path/Trailer.zip"
# Construct the path to the Desktop for example
local_path = os.path.join(os.path.expanduser("~"), "Desktop", name)
# Download the file
try:
urllib.request.urlretrieve(url, local_path)
print(f"File downloaded successfully and saved to: {local_path}")
except Exception as e:
print(f"Failed to download file: {e}")
wget Request Example (macOS or Linux)
Example to download the file to the Desktop. Uses -O
to specify the output file path.
wget -O ~/Desktop/{fileName} "{fileUrl}"
wget -O ~/Desktop/Trailer.zip "https://editingtools.io/download/demo/path/Trailer.zip"
wget Request Example (Windows with PowerShell)
wget -O "$env:USERPROFILE\Desktop\{fileName}" "{fileUrl}"
wget -O "$env:USERPROFILE\Desktop\Trailer.zip" "https://editingtools.io/download/demo/path/Trailer.zip"
wget Request Example (Windows Command Prompt with wget installed)
wget -O "%USERPROFILE%\Desktop\{fileName}" "{fileUrl}"
wget -O "%USERPROFILE%\Desktop\Trailer.zip" "https://editingtools.io/download/demo/path/Trailer.zip"
Workflow Example (Python)
This is an example Python script for files up to 100MB that uploads, processes, and downloads a selected video file. The script checks the status of the process every 60 seconds and stops once it is finished. The generated files will then be downloaded and unzipped in the same folder.
import os
import time
import base64
import requests
import zipfile
API_KEY = "YOUR_API_KEY"
API_URL = "https://api.editingtools.io/v2/cutdetection"
STATUS_URL_TEMPLATE = "https://api.editingtools.io/v2/process/{processId}"
def encode_auth(api_key):
return base64.b64encode(f"apikey:{api_key}".encode()).decode()
def upload_file(filepath):
print("Uploading file...")
with open(filepath, 'rb') as f:
files = {
'file': (os.path.basename(filepath), f)
}
data = {
'algorithm': 'content-aware', #or use 'threshold'
'subclips': '0', #0=no subclip rendering
'notification': '0' #0=no email notification
}
headers = {
"Authorization": f"Basic {encode_auth(API_KEY)}"
}
response = requests.post(API_URL, headers=headers, files=files, data=data)
response.raise_for_status()
data = response.json()
print("Upload successful.")
return data['data']['processId'], int(data['data']['processDurationEstimate'])
def check_status(process_id):
url = STATUS_URL_TEMPLATE.format(processId=process_id)
headers = {
"Authorization": f"Basic {encode_auth(API_KEY)}"
}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
def download_file(url, out_path):
print(f"Downloading {out_path}...")
with requests.get(url, stream=True) as r:
r.raise_for_status()
total_size = int(r.headers.get('content-length', 0))
downloaded = 0
with open(out_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
if total_size:
percent = (downloaded / total_size) * 100
print(f"\rDownloaded {downloaded} of {total_size} bytes ({percent:.2f}%)", end='', flush=True)
print("\nDownload completed.")
def unzip_file(zip_path, extract_dir):
print(f"Unzipping {zip_path}...")
zip_name = os.path.splitext(os.path.basename(zip_path))[0]
subfolder_path = os.path.join(extract_dir, zip_name)
os.makedirs(subfolder_path, exist_ok=True)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(subfolder_path)
print(f"Unzipped to {subfolder_path}")
def main():
filepath = input("Enter the path of your video file: ").strip()
if not os.path.exists(filepath):
print("File not found.")
return
try:
process_id, duration = upload_file(filepath)
except Exception as e:
print(f"Upload failed: {e}")
return
print(f"Waiting for estimated processing duration: {duration} seconds...")
time.sleep(duration)
while True:
try:
status_data = check_status(process_id)
status = status_data['data']['status']
label = status_data['data']['label']
print(f"Status: {status} - {label}")
except Exception as e:
print(f"Status check failed: {e}")
return
if status == "finished":
fileUrl = status_data['data']['fileUrl']
file_name = status_data['data']['fileName']
output_path = os.path.join(os.path.dirname(filepath), file_name)
try:
download_file(fileUrl, output_path)
if output_path.endswith(".zip"):
unzip_file(output_path, os.path.dirname(filepath))
except Exception as e:
print(f"Download or unzip failed: {e}")
break
elif status == "failed":
print("Processing failed.")
break
else:
print("Waiting 60 seconds before checking again...")
time.sleep(60)
if __name__ == "__main__":
main()
Endpoint URL
Endpoint URL to start a Scene Cut Detection process.
https://api.editingtools.io/v2/cutdetection
Endpoint URL to check a process status.
https://api.editingtools.io/v2/process/{processId}
Authentication
This API requires Basic Authentication. The "Authorization" header should be set with the Base64 encoded string of "apikey:YOUR_API_KEY".
Authorization: Basic <base64Encoded(apikey:YOUR_API_KEY)>
Data Handling
Request parameters must be UTF-8 encoded. Results are returned as UTF-8-encoded JSON. By default, datasets will be inside the data tag.
Error Handling
This API service uses standard HTTP response codes to indicate whether a method was completed successfully. HTTP response codes in the 2XX range indicate success. Responses in the 4XX range indicate some sort of failure, while responses in the 5XX range indicate an internal system error that cannot be resolved by the user. The following error codes are used by the API:
Code | Description |
---|---|
200 | OK. The request was successful. |
201 | Created. The entity was created. |
202 | Accepted. The request was accepted. |
400 | Bad request. Please check error message. |
401 | Unauthorized: Username or Api Key is not valid. |
402 | Upgrade Required: This feature requires an active Pro subscription. |
403 | Forbidden: The request is understood, but it has been refused or access is not allowed. |
404 | Not found: The URI requested is invalid or the resource does not exist. |
422 | Unprocessable Entity. A process failed. |
429 | Too Many Requests. Try again in some seconds. |
500 | Internal Server Error. Something is broken. |
502 | Bad Gateway. API is down. |
503 | Service Unavailable. API is up but overloaded with requests. |
504 | Gateway Timeout: API is up but requests reached timout. |
Rate Limits
To prevent abuse and spam, the API has limits at various levels. If you receive error code 429 (Too Many Requests)
, it means you have reached a rate limit.
If you receive a rate limit error, you should stop making requests temporarily. If the retry-after
response header is present, you should not retry your request until after that many seconds has elapsed. To prevent rate-errors, we recommend to wait 300 ms
to 800 ms
between requests to the same endpoint.
Also there is a general limit to the api and all requests made:
Limit | Requests |
---|---|
General limit per minute | 50 |
General limit per hour | 1000 |
Recommended wait time between requests | > 200 ms |
Recommended wait time between requests to one endpoint | > 600 ms |
The general limits per minute and per hour can be changed upon request.
Upload Limits
The maximum upload size for any single request — including file uploads — is 100 MB
. This is a hard limit and applies regardless of your API plan or usage level.
If your request exceeds this size, it will be rejected before reaching our servers. To ensure successful uploads:
- Make sure uploaded files are under 100 MB.
- For larger media, consider splitting files before upload.
- Requests close to the limit may still fail due to encoding or header overhead.
Timezone
This API endpoint returns the time as an ISO 8601 timestamp
in the UTC time zone. These timestamps look like 2025-01-10T15:05:06Z
.
Localization and Languages
This API supports multiple languages. For instance, it can display the text of a status message in a different language.
Accept-Language: LANGUAGE
Example
To set the preferred response language to Spanish, add this header:
Accept-Language: es
cURL Example
To apply the language in a cURL request, add the following header:
-H "Accept-Language: es"
Available Languages
The following list contains all the currently available languages. Note that setting a language header does not guarantee a translated response, as not all texts are translated. If no language is set or a translation is unavailable, the default response will be in English.
Code | Language |
---|---|
en | English |
de | German |
fr | French |
es | Spanish |
ru | Russian |
it | Italian |
el | Greek |
pl | Polish |
pt | Portuguese |
lt | Lithuanian |
ko | Korean |
ja | Japanese |
zh | Chinese |
id | Indonesian |
tr | Turkish |
nl | Dutch |
ro | Romanian |
fi | Finnish |
cs | Czech |
hu | Hungarian |
ar | Arabic |
nb | Norwegian Bokmål |
sk | Slovak |
sl | Slovenian |
sv | Swedish |
lv | Latvian |
et | Estonian |
bg | Bulgarian |
uk | Ukrainian |
da | Danish |