curl --request POST \
--url https://api.interhuman.ai/v2/upload/analyze \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form wait_seconds=0import requests
url = "https://api.interhuman.ai/v2/upload/analyze"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "wait_seconds": "0" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('wait_seconds', '0');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.interhuman.ai/v2/upload/analyze', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interhuman.ai/v2/upload/analyze",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interhuman.ai/v2/upload/analyze"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interhuman.ai/v2/upload/analyze")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interhuman.ai/v2/upload/analyze")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_bodyInter-2 and Inter-2 Audio Upload Analyze
Submit a video or audio file for analysis by an Inter-2 model as an asynchronous job. Answers 202 with the queued job envelope, or 200 with the finished job when wait_seconds is set and the job finishes in time.
curl --request POST \
--url https://api.interhuman.ai/v2/upload/analyze \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form wait_seconds=0import requests
url = "https://api.interhuman.ai/v2/upload/analyze"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "wait_seconds": "0" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('wait_seconds', '0');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.interhuman.ai/v2/upload/analyze', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interhuman.ai/v2/upload/analyze",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interhuman.ai/v2/upload/analyze"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interhuman.ai/v2/upload/analyze")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interhuman.ai/v2/upload/analyze")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"wait_seconds\"\r\n\r\n0\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_bodymodel form field:
inter-2reads the video: picture and sound together. It accepts mp4, mov, avi, mkv, webm, or mpeg-ts files carrying both a video and an audio track, and reports all ten social signals.inter-2-audioreads the audio alone. It accepts wav, flac, mp3, m4a, or ogg files, or a webm or mp4 carrying an audio track (any video track is ignored), and reportsagreement,confidence,frustration,hesitation,interest, anduncertainty.
interhumanai.upload scope, and both return the same job envelope and result shape. A file without the track a model reads is refused: ih5008 when a file has no audio, and ih5001 when an inter-2 file has no video or any file is empty. A format the model does not read is refused with ih4002.
Quickstarts: Video Analysis · Audio Analysis
POST /v1/upload/analyze integration? See Migrate from V1 to V2 for the request, job lifecycle, and result changes. A new integration, including any use of Inter-2 Audio, needs no migration.Response Headers
f47ac10b-58cc-4372-a567-0e02b2c3d479Authorizations
Send your API key, or a client token minted by POST /v1/client_tokens, in the Authorization header as Bearer <credential>. This endpoint requires the interhumanai.upload scope: an API key must hold it, and a client token must be minted with it (the minting key must have held it). A credential without it is refused with ih2003.
Headers
Optional identifier supplied by the client to correlate this request with their own logs. When provided, the value is recorded alongside the server-assigned correlation ID in Interhuman logs to aid lookup and support investigations. This header is not echoed back in the response; the server returns its own correlation ID in the X-Correlation-ID HTTP response header.
Body
The file to analyze. For inter-2, an mp4, mov, avi, mkv, webm or mpeg-ts video carrying both a video and an audio track. For inter-2-audio, a wav, flac, mp3, m4a or ogg audio file, or a webm or mp4 carrying an audio track. At least 3 seconds of media, at most 32 MB, and no longer than the deployment's maximum duration (30 minutes by default).
The Inter-2 model to analyze with: inter-2, which reads the video, picture and sound together, or inter-2-audio, which reads the audio alone and reports a subset of the signals inter-2 reports. Either way the credential must carry the interhumanai.upload scope.
inter-2, inter-2-audio Hold the request open for up to this many seconds waiting for the job to finish. 0 (the default) answers 202 at once. Values above the deployment's bound are rejected with ih4005.
x >= 0Optional flags naming whole-file sections the job's result should carry. conversation_quality_overall and/or conversation_quality_timeline, the same values as POST /v1/upload/analyze, add the Conversation Quality Index as conversation_quality. signals adds signals: every window's signals in one chronological list, with same-type signals in adjacent windows merged by the same rules as POST /v1/upload/analyze, and no modality. Each section is built once the job completes and is absent when its flag is omitted.
Optional sections a POST /v2/upload/analyze job's result carries.
conversation_quality_overall: Include the overall conversation quality index.
conversation_quality_timeline: Include the conversation quality timeline.
signals: Include the whole-file signal list, with adjacent same-type signals merged.
conversation_quality_overall, conversation_quality_timeline, signals Response
The job reached a terminal state within wait_seconds: completed with result, or failed with error.
The job envelope, returned by both v2 upload routes.
Identifier of the job. Use it with GET /v2/upload/jobs/{job_id}.
"3f1c2b7a9d4e4c8fa1b2c3d4e5f60718"
Where the job is: queued (accepted, not started), running, completed (result is set) or failed (error is set).
queued, running, completed, failed "queued"
The model the job was submitted for.
inter-2, inter-2-audio "inter-2"
When the job was accepted (UTC).
"2026-09-11T10:00:00Z"
When the job record, and its result, stop being readable (UTC). A read after this time answers ih4021.
"2026-09-11T11:00:00Z"
Path of the job's status resource, relative to the API base URL: /v2/upload/jobs/{job_id}.
"/v2/upload/jobs/3f1c2b7a9d4e4c8fa1b2c3d4e5f60718"
The result of a completed upload job: one entry per analyzed window.
signals and conversation_quality are whole-file views of those
windows, each present only when the submit's include[] asked for it.
Show child attributes
Show child attributes
Structured error returned by all HTTP error paths.
Only error_id is required. The other fields are included when available.
Show child attributes
Show child attributes