Drive Watch Tonight from your own code
Everything the web app does goes through one public surface. Base URL:
https://api.skillsafe.ai/v1/app-api
Every request carries Authorization: Bearer <token> and Content-Type: application/json.
Every response is a JSON envelope: {"ok":true,"data":{…}} on success and
{"ok":false,"error":{"code":"…","message":"…"}} on failure. Read error.code, not only the HTTP status.
Picks come from the model's general knowledge. Nothing checks that a title is real or where it streams; the web app checks each pick against the person's own limits and lists, and code calling the API should do the same.
Errors
| Code | Meaning | What to do |
|---|---|---|
UNAUTHORIZED | Missing, expired or wrong-app token. | Mint a guest token or sign in again. |
INSUFFICIENT_CREDITS | Balance below min_credits. | Top up. /estimate is free and tells you the hold. |
VALIDATION_ERROR | The input object failed validation. | Check error.details. The body is the input object itself — never wrap it in {"input": …}. |
RATE_LIMITED | Too many requests. | Back off and retry. |
JOB_FAILED | The run started and did not complete. | Retry with the same Idempotency-Key. |
The task field comes first
One prompt serves two turns, chosen by task:
"pick"— a first set from the taste intake."refine"— a new set steered by marks on the last one. Addspreviousandfeedback.
Input
One flat object of strings. task is required; everything else may be empty. Lists and objects travel as JSON text.
| Field | Type | Meaning |
|---|---|---|
task | string, required | "pick" or "refine". |
loved, disliked | string | One title per line, optionally Title (year) - note (1,800 and 1,200 characters; long lists keep the first and last lines). |
moods | string | Comma-separated, e.g. Cozy, Funny. |
format | string | movies, shows or both. |
runtime | string | any, under_100, under_120, under_150, short_episodes (35 min or less), limited_series. |
era | string | any, classic (before 1980), 1980_1999, 2000_2014, recent (2015+). |
language, language_note | string | any, english, non_english or specific with the language in language_note. |
audience | string | solo, partner, friends, family_kids, family_teens. |
avoid | string | Comma-separated content to keep out, e.g. Gore, Jump scares. |
count | string | 8, 10 or 12. |
notes | string | Anything else (800). |
seen | string | Titles already watched, one per line; never picked. |
previous | string (JSON) | Refine only: [{"title","year","kind"}] of earlier picks. |
feedback | string (JSON) | Refine only: {"more_like":[],"less_like":[],"seen":[],"keep":[],"note":""}. |
prescan | string (JSON) | The web app's free check: counts, parsed limits, conflicts and exclude (titles never to pick). Send {} if you have none. |
retry_note | string | Sent only on the one automatic reformat retry. |
Output
The output (output.output) is one JSON object: task, status (ok · need_more · off_topic), message, title, taste_read, changes (refine only),
picks[] of {title, year, kind, runtime_min, seasons, language, country, genres[], fit, anchor, why, content_notes, confidence}
with kind in movie · series · limited and fit in safe · stretch · wildcard,
left_out[] of {title, reason} and followups[].
runtime_min is a movie's length or a series' typical episode length. status: "need_more" means there was too little to go on: show message and the 3 or 4 starter picks. The model may wrap the object in a code fence; strip it before parsing.
1. Get a token
A guest token is enough for /me and /estimate. Running is metered and needs a personal token from signing in. The tokens page shows yours and copies it. Replace YOUR_TOKEN below with it.
2. Check the session and the balance
/me returns subject_type, subject_id and credits. Signed in means subject_type === "user".
curl -s "https://api.skillsafe.ai/v1/app-api/me" \ -H "Authorization: Bearer YOUR_TOKEN"
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
r = requests.get(f"{BASE}/me", headers=H, timeout=30).json()
print(r["data"]["subject_type"], r["data"]["credits"])
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
const r = await (await fetch(`${BASE}/me`, { headers: H })).json();
console.log(r.data.subject_type, r.data.credits);
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
func main() {
req, _ := http.NewRequest("GET", base+"/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out["data"])
_ = bytes.NewReader
}
var token = "YOUR_TOKEN";
var base = "https://api.skillsafe.ai/v1/app-api";
var http = java.net.http.HttpClient.newHttpClient();
var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create(base + "/me"))
.header("Authorization", "Bearer " + token).GET().build();
System.out.println(http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body());
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI("#{BASE}/me")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init("$base/me");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
print_r(json_decode(curl_exec($ch), true)["data"]);
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Console.WriteLine(await http.GetStringAsync($"{baseUrl}/me"));
3. Price it before you run it
/estimate is free and starts no job. hold_credits is a reservation priced against the longest possible reply, not the price. A first set (task: "pick"):
curl -s -X POST "https://api.skillsafe.ai/v1/app-api/estimate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}'
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
body = {
"task": "pick",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
}
r = requests.post(f"{BASE}/estimate", headers={**H}, json=body, timeout=120).json()
print(r["data"]["hold_credits"], r["data"]["model_alias"])
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
const body = {
"task": "pick",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
};
const r = await (await fetch(`${BASE}/estimate`, { method: "POST", headers: { ...H }, body: JSON.stringify(body) })).json();
console.log(r.data.hold_credits, r.data.model_alias);
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
func main() {
payload := []byte(`{"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}`)
req, _ := http.NewRequest("POST", base+"/estimate", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out["data"])
}
var token = "YOUR_TOKEN";
var base = "https://api.skillsafe.ai/v1/app-api";
var http = java.net.http.HttpClient.newHttpClient();
var body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create(base + "/estimate"))
.header("Authorization", "Bearer " + token).header("Content-Type", "application/json")
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(body)).build();
System.out.println(http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body());
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI("#{BASE}/estimate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["Content-Type"] = "application/json"
req.body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";
$base = "https://api.skillsafe.ai/v1/app-api";
$body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
$ch = curl_init("$base/estimate");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $token", "Content-Type: application/json"],
]);
print_r(json_decode(curl_exec($ch), true)["data"]);
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var body = new StringContent("{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}", Encoding.UTF8, "application/json");
var res = await http.PostAsync($"{baseUrl}/estimate", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
A refine round (task: "refine") carries the last set and the marks:
curl -s -X POST "https://api.skillsafe.ai/v1/app-api/estimate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "refine", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "Source Code (2011)", "previous": "[{\"title\":\"Primer\",\"year\":2004,\"kind\":\"movie\"},{\"title\":\"Coherence\",\"year\":2013,\"kind\":\"movie\"},{\"title\":\"Timecrimes\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"The Man from Earth\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"Another Earth\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Source Code\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Predestination\",\"year\":2014,\"kind\":\"movie\"},{\"title\":\"Annihilation\",\"year\":2018,\"kind\":\"movie\"},{\"title\":\"The Endless\",\"year\":2017,\"kind\":\"movie\"},{\"title\":\"Pi\",\"year\":1998,\"kind\":\"movie\"}]", "feedback": "{\"more_like\":[\"Coherence (2013)\",\"The Man from Earth (2007)\"],\"less_like\":[\"Annihilation (2018)\"],\"seen\":[\"Source Code (2011)\"],\"keep\":[\"Primer (2004)\"],\"note\":\"a bit warmer, less bleak\"}", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\",\"Source Code\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}'
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
body = {
"task": "refine",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "Source Code (2011)",
"previous": "[{\"title\":\"Primer\",\"year\":2004,\"kind\":\"movie\"},{\"title\":\"Coherence\",\"year\":2013,\"kind\":\"movie\"},{\"title\":\"Timecrimes\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"The Man from Earth\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"Another Earth\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Source Code\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Predestination\",\"year\":2014,\"kind\":\"movie\"},{\"title\":\"Annihilation\",\"year\":2018,\"kind\":\"movie\"},{\"title\":\"The Endless\",\"year\":2017,\"kind\":\"movie\"},{\"title\":\"Pi\",\"year\":1998,\"kind\":\"movie\"}]",
"feedback": "{\"more_like\":[\"Coherence (2013)\",\"The Man from Earth (2007)\"],\"less_like\":[\"Annihilation (2018)\"],\"seen\":[\"Source Code (2011)\"],\"keep\":[\"Primer (2004)\"],\"note\":\"a bit warmer, less bleak\"}",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\",\"Source Code\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
}
r = requests.post(f"{BASE}/estimate", headers={**H}, json=body, timeout=120).json()
print(r["data"]["hold_credits"])
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
const body = {
"task": "refine",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "Source Code (2011)",
"previous": "[{\"title\":\"Primer\",\"year\":2004,\"kind\":\"movie\"},{\"title\":\"Coherence\",\"year\":2013,\"kind\":\"movie\"},{\"title\":\"Timecrimes\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"The Man from Earth\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"Another Earth\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Source Code\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Predestination\",\"year\":2014,\"kind\":\"movie\"},{\"title\":\"Annihilation\",\"year\":2018,\"kind\":\"movie\"},{\"title\":\"The Endless\",\"year\":2017,\"kind\":\"movie\"},{\"title\":\"Pi\",\"year\":1998,\"kind\":\"movie\"}]",
"feedback": "{\"more_like\":[\"Coherence (2013)\",\"The Man from Earth (2007)\"],\"less_like\":[\"Annihilation (2018)\"],\"seen\":[\"Source Code (2011)\"],\"keep\":[\"Primer (2004)\"],\"note\":\"a bit warmer, less bleak\"}",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\",\"Source Code\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
};
const r = await (await fetch(`${BASE}/estimate`, { method: "POST", headers: { ...H }, body: JSON.stringify(body) })).json();
console.log(r.data.hold_credits);
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
func main() {
payload := []byte(`{"task": "refine", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "Source Code (2011)", "previous": "[{\"title\":\"Primer\",\"year\":2004,\"kind\":\"movie\"},{\"title\":\"Coherence\",\"year\":2013,\"kind\":\"movie\"},{\"title\":\"Timecrimes\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"The Man from Earth\",\"year\":2007,\"kind\":\"movie\"},{\"title\":\"Another Earth\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Source Code\",\"year\":2011,\"kind\":\"movie\"},{\"title\":\"Predestination\",\"year\":2014,\"kind\":\"movie\"},{\"title\":\"Annihilation\",\"year\":2018,\"kind\":\"movie\"},{\"title\":\"The Endless\",\"year\":2017,\"kind\":\"movie\"},{\"title\":\"Pi\",\"year\":1998,\"kind\":\"movie\"}]", "feedback": "{\"more_like\":[\"Coherence (2013)\",\"The Man from Earth (2007)\"],\"less_like\":[\"Annihilation (2018)\"],\"seen\":[\"Source Code (2011)\"],\"keep\":[\"Primer (2004)\"],\"note\":\"a bit warmer, less bleak\"}", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\",\"Source Code\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}`)
req, _ := http.NewRequest("POST", base+"/estimate", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out["data"])
}
var token = "YOUR_TOKEN";
var base = "https://api.skillsafe.ai/v1/app-api";
var http = java.net.http.HttpClient.newHttpClient();
var body = "{\"task\": \"refine\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"Source Code (2011)\", \"previous\": \"[{\\\"title\\\":\\\"Primer\\\",\\\"year\\\":2004,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Coherence\\\",\\\"year\\\":2013,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Timecrimes\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Man from Earth\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Another Earth\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Source Code\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Predestination\\\",\\\"year\\\":2014,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Annihilation\\\",\\\"year\\\":2018,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Endless\\\",\\\"year\\\":2017,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Pi\\\",\\\"year\\\":1998,\\\"kind\\\":\\\"movie\\\"}]\", \"feedback\": \"{\\\"more_like\\\":[\\\"Coherence (2013)\\\",\\\"The Man from Earth (2007)\\\"],\\\"less_like\\\":[\\\"Annihilation (2018)\\\"],\\\"seen\\\":[\\\"Source Code (2011)\\\"],\\\"keep\\\":[\\\"Primer (2004)\\\"],\\\"note\\\":\\\"a bit warmer, less bleak\\\"}\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\",\\\"Source Code\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create(base + "/estimate"))
.header("Authorization", "Bearer " + token).header("Content-Type", "application/json")
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(body)).build();
System.out.println(http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body());
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI("#{BASE}/estimate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["Content-Type"] = "application/json"
req.body = "{\"task\": \"refine\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"Source Code (2011)\", \"previous\": \"[{\\\"title\\\":\\\"Primer\\\",\\\"year\\\":2004,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Coherence\\\",\\\"year\\\":2013,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Timecrimes\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Man from Earth\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Another Earth\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Source Code\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Predestination\\\",\\\"year\\\":2014,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Annihilation\\\",\\\"year\\\":2018,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Endless\\\",\\\"year\\\":2017,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Pi\\\",\\\"year\\\":1998,\\\"kind\\\":\\\"movie\\\"}]\", \"feedback\": \"{\\\"more_like\\\":[\\\"Coherence (2013)\\\",\\\"The Man from Earth (2007)\\\"],\\\"less_like\\\":[\\\"Annihilation (2018)\\\"],\\\"seen\\\":[\\\"Source Code (2011)\\\"],\\\"keep\\\":[\\\"Primer (2004)\\\"],\\\"note\\\":\\\"a bit warmer, less bleak\\\"}\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\",\\\"Source Code\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";
$base = "https://api.skillsafe.ai/v1/app-api";
$body = "{\"task\": \"refine\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"Source Code (2011)\", \"previous\": \"[{\\\"title\\\":\\\"Primer\\\",\\\"year\\\":2004,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Coherence\\\",\\\"year\\\":2013,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Timecrimes\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Man from Earth\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Another Earth\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Source Code\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Predestination\\\",\\\"year\\\":2014,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Annihilation\\\",\\\"year\\\":2018,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Endless\\\",\\\"year\\\":2017,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Pi\\\",\\\"year\\\":1998,\\\"kind\\\":\\\"movie\\\"}]\", \"feedback\": \"{\\\"more_like\\\":[\\\"Coherence (2013)\\\",\\\"The Man from Earth (2007)\\\"],\\\"less_like\\\":[\\\"Annihilation (2018)\\\"],\\\"seen\\\":[\\\"Source Code (2011)\\\"],\\\"keep\\\":[\\\"Primer (2004)\\\"],\\\"note\\\":\\\"a bit warmer, less bleak\\\"}\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\",\\\"Source Code\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
$ch = curl_init("$base/estimate");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $token", "Content-Type: application/json"],
]);
print_r(json_decode(curl_exec($ch), true)["data"]);
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var body = new StringContent("{\"task\": \"refine\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"Source Code (2011)\", \"previous\": \"[{\\\"title\\\":\\\"Primer\\\",\\\"year\\\":2004,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Coherence\\\",\\\"year\\\":2013,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Timecrimes\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Man from Earth\\\",\\\"year\\\":2007,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Another Earth\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Source Code\\\",\\\"year\\\":2011,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Predestination\\\",\\\"year\\\":2014,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Annihilation\\\",\\\"year\\\":2018,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"The Endless\\\",\\\"year\\\":2017,\\\"kind\\\":\\\"movie\\\"},{\\\"title\\\":\\\"Pi\\\",\\\"year\\\":1998,\\\"kind\\\":\\\"movie\\\"}]\", \"feedback\": \"{\\\"more_like\\\":[\\\"Coherence (2013)\\\",\\\"The Man from Earth (2007)\\\"],\\\"less_like\\\":[\\\"Annihilation (2018)\\\"],\\\"seen\\\":[\\\"Source Code (2011)\\\"],\\\"keep\\\":[\\\"Primer (2004)\\\"],\\\"note\\\":\\\"a bit warmer, less bleak\\\"}\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\",\\\"Source Code\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}", Encoding.UTF8, "application/json");
var res = await http.PostAsync($"{baseUrl}/estimate", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
4. Run and poll
/run returns a job. Always send an Idempotency-Key — the same key is the same run, so a retry cannot bill twice. The web app derives it from the task and a hash of the input, plus an attempt number for its one automatic reformat retry.
curl -s -X POST "https://api.skillsafe.ai/v1/app-api/run" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: watch-tonight:pick:k3x9q1-2f0:a1" \
-d '{"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}'
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
body = {
"task": "pick",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
}
r = requests.post(f"{BASE}/run", headers={**H, "Idempotency-Key": "watch-tonight:pick:k3x9q1-2f0:a1"}, json=body, timeout=120).json()
job_id = r["data"]["job_id"]
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
const body = {
"task": "pick",
"loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene",
"disliked": "Tenet (2020) - too loud, too busy",
"moods": "Mind-bending, Thought-provoking",
"format": "movies",
"runtime": "under_120",
"era": "any",
"language": "any",
"language_note": "",
"audience": "solo",
"avoid": "",
"count": "10",
"notes": "I like a big idea with a small cast.",
"seen": "",
"prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"
};
const r = await (await fetch(`${BASE}/run`, { method: "POST", headers: { ...H, "Idempotency-Key": "watch-tonight:pick:k3x9q1-2f0:a1" }, body: JSON.stringify(body) })).json();
const jobId = r.data.job_id;
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
func main() {
payload := []byte(`{"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}`)
req, _ := http.NewRequest("POST", base+"/run", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "watch-tonight:pick:k3x9q1-2f0:a1")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out["data"])
}
var token = "YOUR_TOKEN";
var base = "https://api.skillsafe.ai/v1/app-api";
var http = java.net.http.HttpClient.newHttpClient();
var body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create(base + "/run"))
.header("Authorization", "Bearer " + token).header("Content-Type", "application/json")
.header("Idempotency-Key", "watch-tonight:pick:k3x9q1-2f0:a1")
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(body)).build();
System.out.println(http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body());
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI("#{BASE}/run")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = "watch-tonight:pick:k3x9q1-2f0:a1"
req.body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = "YOUR_TOKEN";
$base = "https://api.skillsafe.ai/v1/app-api";
$body = "{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}";
$ch = curl_init("$base/run");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $token", "Content-Type: application/json", "Idempotency-Key: watch-tonight:pick:k3x9q1-2f0:a1"],
]);
print_r(json_decode(curl_exec($ch), true)["data"]);
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var body = new StringContent("{\"task\": \"pick\", \"loved\": \"Arrival (2016) - the quiet, the ending\\nEx Machina (2014)\\nMoon (2009)\\nInterstellar (2014) - the docking scene\", \"disliked\": \"Tenet (2020) - too loud, too busy\", \"moods\": \"Mind-bending, Thought-provoking\", \"format\": \"movies\", \"runtime\": \"under_120\", \"era\": \"any\", \"language\": \"any\", \"language_note\": \"\", \"audience\": \"solo\", \"avoid\": \"\", \"count\": \"10\", \"notes\": \"I like a big idea with a small cast.\", \"seen\": \"\", \"prescan\": \"{\\\"loved_n\\\":4,\\\"disliked_n\\\":1,\\\"moods\\\":[\\\"Mind-bending\\\",\\\"Thought-provoking\\\"],\\\"conflicts\\\":[],\\\"exclude\\\":[\\\"Arrival\\\",\\\"Ex Machina\\\",\\\"Moon\\\",\\\"Interstellar\\\",\\\"Tenet\\\"],\\\"limits\\\":{\\\"format\\\":\\\"movies\\\",\\\"movie_max_min\\\":120,\\\"episode_max_min\\\":null,\\\"limited_only\\\":false,\\\"year_min\\\":null,\\\"year_max\\\":null,\\\"language\\\":\\\"any\\\",\\\"language_note\\\":\\\"\\\"},\\\"clipped\\\":[]}\"}", Encoding.UTF8, "application/json");
http.DefaultRequestHeaders.Add("Idempotency-Key", "watch-tonight:pick:k3x9q1-2f0:a1");
var res = await http.PostAsync($"{baseUrl}/run", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
curl -s "https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID" \ -H "Authorization: Bearer YOUR_TOKEN" # repeat until data.status is "succeeded" or "failed"; # the pick set is the JSON string at data.output.output
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
import json, time
job_id = "JOB_ID"
while True:
job = requests.get(f"{BASE}/jobs/{job_id}", headers=H, timeout=30).json()["data"]
if job["status"] in ("succeeded", "failed"):
break
time.sleep(1)
picks = json.loads(job["output"]["output"])
print(picks["status"], [p["title"] for p in picks["picks"]])
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
let job;
for (;;) {
job = (await (await fetch(`${BASE}/jobs/JOB_ID`, { headers: H })).json()).data;
if (job.status === "succeeded" || job.status === "failed") break;
await new Promise((r) => setTimeout(r, 1000));
}
const set = JSON.parse(job.output.output);
console.log(set.status, set.picks.map((p) => p.title));
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
// GET base+"/jobs/JOB_ID" every second until data.status is
// "succeeded" or "failed", then json.Unmarshal data.output.output.
func main() { fmt.Println("poll", base+"/jobs/JOB_ID"); _ = bytes.NewReader; _ = json.Marshal; _ = http.Get }
var token = "YOUR_TOKEN"; var base = "https://api.skillsafe.ai/v1/app-api"; var http = java.net.http.HttpClient.newHttpClient(); // GET base + "/jobs/JOB_ID" every second until data.status is // "succeeded" or "failed"; the pick set is the JSON string at data.output.output.
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
loop do
uri = URI("#{BASE}/jobs/JOB_ID")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
job = JSON.parse(Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body)["data"]
if %w[succeeded failed].include?(job["status"])
puts JSON.parse(job["output"]["output"])["picks"]
break
end
sleep 1
end
<?php
$token = "YOUR_TOKEN";
$base = "https://api.skillsafe.ai/v1/app-api";
do {
$ch = curl_init("$base/jobs/JOB_ID");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
$job = json_decode(curl_exec($ch), true)["data"];
if (!in_array($job["status"], ["succeeded", "failed"])) sleep(1);
} while (!in_array($job["status"], ["succeeded", "failed"]));
print_r(json_decode($job["output"]["output"], true)["picks"]);
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// GET {baseUrl}/jobs/JOB_ID every second until data.status is
// "succeeded" or "failed"; the pick set is the JSON string at data.output.output.
5. Stream it
/run-stream answers with server-sent events and ends with a done frame carrying the whole output, charged_credits and truncated. If truncated is true the balance ran low mid-answer: parse the picks that arrived whole and top up for the rest.
curl -sN -X POST "https://api.skillsafe.ai/v1/app-api/run-stream" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Idempotency-Key: watch-tonight:pick:k3x9q1-2f0:a1" \
-d '{"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}'
# event: job data: {"job_id": ...}
# event: delta data: {"text": "..."} (may be absent: a browser often gets only
# event: tick heartbeats and the whole answer in the final frame)
# event: done data: {"output": {"output": "<the JSON pick set>"}, "charged_credits": ..., "truncated": false}
import requests
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
import json
body = {"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}
with requests.post(f"{BASE}/run-stream", json=body, stream=True, timeout=300,
headers={**H, "Accept": "text/event-stream", "Idempotency-Key": "watch-tonight:pick:k3x9q1-2f0:a1"}) as r:
event, done = None, None
for line in r.iter_lines(decode_unicode=True):
if line.startswith("event:"): event = line[6:].strip()
elif line.startswith("data:") and event == "done": done = json.loads(line[5:])
picks = json.loads(done["output"]["output"])
print(picks["title"], done.get("charged_credits"), done.get("truncated"))
const TOKEN = "YOUR_TOKEN";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const H = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" };
const res = await fetch(`${BASE}/run-stream`, { method: "POST",
headers: { ...H, Accept: "text/event-stream", "Idempotency-Key": "watch-tonight:pick:k3x9q1-2f0:a1" },
body: JSON.stringify({"task": "pick", "loved": "Arrival (2016) - the quiet, the ending\nEx Machina (2014)\nMoon (2009)\nInterstellar (2014) - the docking scene", "disliked": "Tenet (2020) - too loud, too busy", "moods": "Mind-bending, Thought-provoking", "format": "movies", "runtime": "under_120", "era": "any", "language": "any", "language_note": "", "audience": "solo", "avoid": "", "count": "10", "notes": "I like a big idea with a small cast.", "seen": "", "prescan": "{\"loved_n\":4,\"disliked_n\":1,\"moods\":[\"Mind-bending\",\"Thought-provoking\"],\"conflicts\":[],\"exclude\":[\"Arrival\",\"Ex Machina\",\"Moon\",\"Interstellar\",\"Tenet\"],\"limits\":{\"format\":\"movies\",\"movie_max_min\":120,\"episode_max_min\":null,\"limited_only\":false,\"year_min\":null,\"year_max\":null,\"language\":\"any\",\"language_note\":\"\"},\"clipped\":[]}"}) });
const text = await res.text(); // or read res.body incrementally
let done = null;
for (const frame of text.split("\n\n")) {
const ev = /^event: *(.*)$/m.exec(frame), data = /^data: *(.*)$/m.exec(frame);
if (ev && ev[1] === "done" && data) done = JSON.parse(data[1]);
}
const set = JSON.parse(done.output.output);
console.log(set.title, done.truncated);
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
const token = "YOUR_TOKEN"
// POST base+"/run-stream" with Accept: text/event-stream, read lines with
// bufio.Scanner, and json.Unmarshal the data line of the "done" event.
func main() { fmt.Println("see the cURL tab for the frame format"); _ = bytes.NewReader; _ = json.Marshal; _ = http.Get }
var token = "YOUR_TOKEN"; var base = "https://api.skillsafe.ai/v1/app-api"; var http = java.net.http.HttpClient.newHttpClient(); // POST base + "/run-stream" with Accept: text/event-stream, read the body // line by line (BodyHandlers.ofLines()), and parse the data line of the "done" event.
require "net/http"
require "json"
TOKEN = "YOUR_TOKEN"
BASE = "https://api.skillsafe.ai/v1/app-api"
# POST to "#{BASE}/run-stream" with Accept: text/event-stream; read the body,
# split frames on blank lines, and JSON.parse the data line of the "done" event.
<?php $token = "YOUR_TOKEN"; $base = "https://api.skillsafe.ai/v1/app-api"; // POST to "$base/run-stream" with Accept: text/event-stream; split the body on // blank lines and json_decode the data line of the "done" event.
using System.Net.Http.Headers;
using System.Text;
var token = "YOUR_TOKEN";
var baseUrl = "https://api.skillsafe.ai/v1/app-api";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// POST {baseUrl}/run-stream with Accept: text/event-stream; read the response
// stream line by line and parse the data line of the "done" event.