Fingerprint Video

Generate perceptual hashes for duplicate detection and content matching.

POST /v1/fingerprint 1 credit

Description

Generate perceptual hashes from a video frame for duplicate detection and content matching. Returns three hash algorithms (average hash, perceptual hash, difference hash) as hex strings that can be compared using Hamming distance.

Request Body

Send a JSON body with the following parameters:

Parameter Type Required Default Description
url string Yes Source video URL
timestamp string No 25% of duration Frame to hash (seconds or "MM:SS")
hash_size int No 16 Hash size: 8, 16, or 32. Larger = more precise.

Hash Algorithms

average_hash

Fast, simple. Resizes image, computes mean brightness, creates binary hash. Good for exact or near-exact duplicate detection.

perceptual_hash (pHash)

Gold standard for visual similarity. Uses DCT (discrete cosine transform) to capture frequency information. Robust to minor edits, resizing, and compression.

difference_hash (dHash)

Captures structural/gradient information. Compares adjacent pixel brightness. Good for detecting cropping and minor modifications.

Code Examples

curl -X POST "https://videoconduit.com/v1/fingerprint" \
  -H "Authorization: Bearer vc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
    "hash_size": 16
  }'
import requests

response = requests.post(
    "https://videoconduit.com/v1/fingerprint",
    headers={"Authorization": "Bearer vc_your_api_key"},
    json={
        "url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
        "hash_size": 16,
    },
)
data = response.json()
print(data["job_id"])
const response = await fetch("https://videoconduit.com/v1/fingerprint", {
  method: "POST",
  headers: {
    "Authorization": "Bearer vc_your_api_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
    hash_size: 16,
  }),
});
const data = await response.json();
console.log(data.job_id);
$client = new GuzzleHttp\Client();
$response = $client->post("https://videoconduit.com/v1/fingerprint", [
    "headers" => ["Authorization" => "Bearer vc_your_api_key"],
    "json" => [
        "url" => "https://youtube.com/watch?v=dQw4w9WgXcQ",
        "hash_size" => 16,
    ],
]);
$data = json_decode($response->getBody(), true);
echo $data["job_id"];
body := strings.NewReader(`{
  "url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
  "hash_size": 16
}`)
req, _ := http.NewRequest("POST", "https://videoconduit.com/v1/fingerprint", body)
req.Header.Set("Authorization", "Bearer vc_your_api_key")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var data map[string]interface{}
json.NewDecoder(resp.Body).Decode(&data)
require "net/http"
require "json"

uri = URI("https://videoconduit.com/v1/fingerprint")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer vc_your_api_key"
req["Content-Type"] = "application/json"
req.body = {
  url: "https://youtube.com/watch?v=dQw4w9WgXcQ",
  hash_size: 16,
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
puts data["job_id"]

Response

Completed Job (result_data)

Returned from GET /v1/jobs/{id} when the job finishes:

{
  "job_id": "e5f6a7b8-c9d0-1234-efab-567890123456",
  "status": "completed",
  "result_data": {
    "average_hash": "ff818181818181ff",
    "perceptual_hash": "e4b4b4b4343434e4",
    "difference_hash": "8e8e8e8e0e0e0e8e",
    "hash_size": 16,
    "frame_timestamp": "53",
    "title": "Rick Astley - Never Gonna Give You Up"
  }
}

Comparing Hashes

Compare two hashes using Hamming distance: convert hex strings to binary and count differing bits. Lower distance means more similar content.

def hamming_distance(hash1: str, hash2: str) -> int:
    """Compare two hex hash strings. Lower = more similar."""
    b1 = bin(int(hash1, 16))[2:]
    b2 = bin(int(hash2, 16))[2:]
    return sum(c1 != c2 for c1, c2 in zip(b1.zfill(len(b2)), b2.zfill(len(b1))))

# Identical videos: distance ≈ 0
# Similar (re-encoded): distance ≈ 1-5
# Different videos: distance > 10

Try It

{# Usage: {% include "docs/_playground.html" with endpoint_method="POST" endpoint_path="/v1/download" fields=playground_fields %} playground_fields is a list of dicts passed from the view: [ {"name": "url", "type": "text", "required": True, "placeholder": "https://youtube.com/watch?v=...", "label": "Video URL"}, {"name": "quality", "type": "select", "options": ["best", "1080p", "720p", "480p", "audio"], "default": "best", "label": "Quality"}, ] #}

Try It

POST /v1/fingerprint
Response

Notes

Use pHash for Best Results

The perceptual_hash (pHash) is the most robust algorithm for detecting re-uploads, re-encodes, and minor edits. Use it as your primary comparison, with average_hash and difference_hash as secondary signals.

Hash Size Trade-off

Larger hash sizes (32) are more precise but produce longer strings. For most use cases, the default size of 16 provides a good balance between accuracy and performance.

This site uses only essential cookies required for the service to function (session authentication and security). We do not use analytics, tracking, or advertising cookies. Learn more