Clone videos
Add permitted videos to your library, individually or in a job.
You will copy videos whose owners allow cloning into your own library. Cloning creates your own video id; your title, folder and availability are independent.
Clone one video
Create a video with the following source. The owner must allow cloning and the source must be available. A hidden or inaccessible source is reported as 404; an owner who disallows cloning produces clone_not_allowed.
{"source":{"type":"clone","video_id":"h87x"}}
Send this body to POST /videos. The response contains the new video and a null upload. A clone of a ready video can already be ready. A clone of a processing video becomes ready with its source, and emits its own video.ready event.
Clone a list
curl --fail-with-body --request POST 'https://api.todayfiles.com/v1/clone-jobs' \
--header "Authorization: Bearer $TODAYFILES_API_KEY" \
--header 'Idempotency-Key: replace-with-a-unique-request-key' \
--header 'Content-Type: application/json' \
--data '{"inputs":["h87x","https://todayfiles-play.site/q2m8"]}'const response = await fetch("https://api.todayfiles.com/v1/clone-jobs", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.TODAYFILES_API_KEY,
"Idempotency-Key": "replace-with-a-unique-request-key",
"Content-Type": "application/json",
},
body: JSON.stringify({"inputs":["h87x","https://todayfiles-play.site/q2m8"]}),
});
if (!response.ok) throw new Error(await response.text());
const result = response.status === 204 ? null : await response.json();
console.log(result);import json
import os
import requests
response = requests.request(
"POST", "https://api.todayfiles.com/v1/clone-jobs",
headers={
"Authorization": "Bearer " + os.environ["TODAYFILES_API_KEY"],
"Idempotency-Key": "replace-with-a-unique-request-key",
},
json=json.loads("{\"inputs\":[\"h87x\",\"https://todayfiles-play.site/q2m8\"]}"),
timeout=30,
)
response.raise_for_status()
print(response.json() if response.content else None)<?php
$client = curl_init("https://api.todayfiles.com/v1/clone-jobs");
curl_setopt_array($client, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TODAYFILES_API_KEY'),
'Idempotency-Key: replace-with-a-unique-request-key',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => "{\"inputs\":[\"h87x\",\"https://todayfiles-play.site/q2m8\"]}",
]);
$body = curl_exec($client);
if ($body === false) throw new RuntimeException(curl_error($client));
$status = curl_getinfo($client, CURLINFO_RESPONSE_CODE);
curl_close($client);
if ($status >= 400) throw new RuntimeException($body);
$result = $body === '' ? null : json_decode($body, true, 512, JSON_THROW_ON_ERROR);
print_r($result);package main
import (
"encoding/json"
"io"
"net/http"
"os"
"strings"
"time"
)
func main() {
request, err := http.NewRequest("POST", "https://api.todayfiles.com/v1/clone-jobs", strings.NewReader("{\"inputs\":[\"h87x\",\"https://todayfiles-play.site/q2m8\"]}"))
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + os.Getenv("TODAYFILES_API_KEY"))
request.Header.Set("Idempotency-Key", "replace-with-a-unique-request-key")
request.Header.Set("Content-Type", "application/json")
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1 << 20))
if err != nil { panic(err) }
if response.StatusCode >= 400 { panic(string(body)) }
var result any
if len(body) > 0 {
if err := json.Unmarshal(body, &result); err != nil { panic(err) }
}
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil { panic(err) }
}A job accepts up to 200 video ids or player URLs. You can have three jobs queued or running at once. Single clones and jobs share a limit of 200 clones per rolling hour.
Follow the outcome
curl --fail-with-body --request GET 'https://api.todayfiles.com/v1/clone-jobs/cj_1z' \
--header "Authorization: Bearer $TODAYFILES_API_KEY"const response = await fetch("https://api.todayfiles.com/v1/clone-jobs/cj_1z", {
method: "GET",
headers: {
Authorization: "Bearer " + process.env.TODAYFILES_API_KEY,
},
});
if (!response.ok) throw new Error(await response.text());
const result = response.status === 204 ? null : await response.json();
console.log(result);import json
import os
import requests
response = requests.request(
"GET", "https://api.todayfiles.com/v1/clone-jobs/cj_1z",
headers={
"Authorization": "Bearer " + os.environ["TODAYFILES_API_KEY"],
},
timeout=30,
)
response.raise_for_status()
print(response.json() if response.content else None)<?php
$client = curl_init("https://api.todayfiles.com/v1/clone-jobs/cj_1z");
curl_setopt_array($client, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TODAYFILES_API_KEY'),
],
]);
$body = curl_exec($client);
if ($body === false) throw new RuntimeException(curl_error($client));
$status = curl_getinfo($client, CURLINFO_RESPONSE_CODE);
curl_close($client);
if ($status >= 400) throw new RuntimeException($body);
$result = $body === '' ? null : json_decode($body, true, 512, JSON_THROW_ON_ERROR);
print_r($result);package main
import (
"encoding/json"
"io"
"net/http"
"os"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://api.todayfiles.com/v1/clone-jobs/cj_1z", nil)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer " + os.Getenv("TODAYFILES_API_KEY"))
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1 << 20))
if err != nil { panic(err) }
if response.StatusCode >= 400 { panic(string(body)) }
var result any
if len(body) > 0 {
if err := json.Unmarshal(body, &result); err != nil { panic(err) }
}
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil { panic(err) }
}A job moves through queued, running and completed. Counts report total, succeeded, failed and pending. Completed means the job has finished trying every input; check its failure count before treating the whole job as successful.
Each item reports its input, status, new video id when successful, and a stable error code when it fails. Errors include invalid_input, duplicate_input, video_not_found, clone_not_allowed, clone_limit_reached and unknown. Fix failed inputs and submit a new job when appropriate. GET /clone-jobs returns your twenty newest jobs without individual items.
What's next
Subscribe to clone_job.completed or organise the new videos.