KickAssSubtitles API lets users access various site features like searching/converting/uploading subtitles from withing any programming language/environment capable of performing HTTP requests. This guide demonstrates how to consume the API using Python language. All demonstrated code samples are also available in our GitHub repository.
Before continuing make sure you have at least Python 3.x.
You will also need requests module installed:
pip install requests # or `apt-get install python3-requests` on Debian-based Linux distros
Obtaining API key
You can obtain YOUR_API_KEY from
account information screen.
Click Generate API key button to generate new key.
After generation the key will be displayed on screen for short period
of time - you should copy it and store in safe place. For this guide
lets assume this is our generated key: 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69
Testing connection
In order to test API connection lets send GET query to
/api/user endpoint which returns various information about
user's account:
import requests
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
response = requests.request("GET", "https://kickasssubtitles.com/api/user", headers=headers)
print(response.text) # should return JSON string: `{"username":"admin","email":"[email protected]", ...}`
Uploading subtitle
Suppose we have movie file Some.Movie.2006.1080p.BluRay.x264.mp4 along with
matching subtitle file Some.Movie.2006.1080p.BluRay.x264.srt
In order to upload subtitle, we must first calculate hash (fingerprint) of movie file. Most popular way to do it is to use OpenSubtitles hashing function. Here is Python implementation:
import struct
import os
def hash_opensubtitles(video_path):
"""Compute a hash using OpenSubtitles' algorithm.
:param str video_path: path of the video.
:return: the hash.
:rtype: str
"""
bytesize = struct.calcsize(b'<q')
with open(video_path, 'rb') as f:
filesize = os.path.getsize(video_path)
filehash = filesize
if filesize < 65536 * 2:
return
for _ in range(65536 // bytesize):
filebuffer = f.read(bytesize)
(l_value,) = struct.unpack(b'<q', filebuffer)
filehash += l_value
filehash &= 0xFFFFFFFFFFFFFFFF # to remain as 64bit number
f.seek(max(0, filesize - 65536), 0)
for _ in range(65536 // bytesize):
filebuffer = f.read(bytesize)
(l_value,) = struct.unpack(b'<q', filebuffer)
filehash += l_value
filehash &= 0xFFFFFFFFFFFFFFFF
returnedhash = '%016x' % filehash
return returnedhash
With hashing function in place we can proceed to uploading subtitle by sending
POST query to /api/upload endpoint. Note that
we also need to pass movie IMDb URL (imdb_url) and
subtitle language code:
import os
import requests
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
payload = {
"filename": "Some.Movie.2006.1080p.BluRay.x264.mp4",
"filesize": os.path.getsize("Some.Movie.2006.1080p.BluRay.x264.mp4"),
"hashes[opensubtitles]": hash_opensubtitles("Some.Movie.2006.1080p.BluRay.x264.mp4"),
"imdb_url": "https://www.imdb.com/title/tt0000000/",
"language": "en"
}
files = [
("subtitle",("Some.Movie.2006.1080p.BluRay.x264.srt", open("Some.Movie.2006.1080p.BluRay.x264.srt", "rb"), "application/octet-stream"))
]
response = requests.request("POST", "https://kickasssubtitles.com/api/upload", headers=headers, data=payload, files=files)
print(response.text) # should return JSON string: `{"id":"dcszd","created_at":"2024-07-10T21:00:29.000000Z", ...}`
Searching subtitle
Suppose we have movie file Some.Movie.2006.1080p.BluRay.x264.mp4 but this time
we want to search for subtitles matching this file. In order to do that
we need to send POST query to /api/search endpoint:
import requests
import os
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
payload = {
"filename": "Some.Movie.2006.1080p.BluRay.x264.mp4",
"filesize": os.path.getsize("Some.Movie.2006.1080p.BluRay.x264.mp4"),
"hashes[opensubtitles]": hash_opensubtitles("Some.Movie.2006.1080p.BluRay.x264.mp4"),
"language": "pl", # optional - defaults to "en"
"encoding": "UTF-8", # optional - defaults to "UTF-8"
"format": "subrip" # optional - defaults to "subrip"
}
response = requests.request("POST", "https://kickasssubtitles.com/api/search", headers=headers, data=payload)
task = response.json()
print(task["id"]) # should return task identifier - for instance: `01j2pfmd5sth39v4kdrvtbwan2`
Note that /api/search endpoint works in asynchronous fashion.
So first search "task" is created with some id and later we
will need to query the API for this task by its id to see
if it has been processed. Usually it takes few seconds to process a task
depending on current site load.
import requests
import base64
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
response = requests.request("GET", "https://kickasssubtitles.com/api/tasks/01j2pfmd5sth39v4kdrvtbwan2", headers=headers)
task = response.json()
if task["status"] != "completed":
raise Exception("Something went wrong.")
subtitle_file = "subtitle." + task["result"]["subtitles"][0]["extension"]
with open(subtitle_file, "wb") as f:
f.write(base64.b64decode(task["result"]["subtitles"][0]["contents_base64"]))
print(f"Subtitle written to {subtitle_file}")
Subtitles are base64-encoded in the response. You can find more about task response structure in the docs.
Converting subtitle
To convert our example subtitle Some.Movie.2006.1080p.BluRay.x264.srt to
different format we can send POST query to
/api/convert endpoint:
import requests
import os
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
payload = {
"language": "pl", # optional
"input_encoding": "UTF-8", # optional
"encoding": "UTF-8", # optional - defaults to "UTF-8"
"format": "webvtt", # optional - defaults to "subrip"
"fps": None # optional
}
files = [
("subtitle",("Some.Movie.2006.1080p.BluRay.x264.srt", open("Some.Movie.2006.1080p.BluRay.x264.srt", "rb"), "application/octet-stream"))
]
response = requests.request("POST", "https://kickasssubtitles.com/api/convert", headers=headers, data=payload)
task = response.json()
print(task["id"]) # should return task identifier - for instance: `01j2pfmd5sth39v4kdrvtbwan2`
Similarily to previous example /api/convert also works in
async fashion - we get task id which we use to query
task status:
import requests
import base64
headers = {
"Accept": "application/json",
"Authorization": "Bearer 1VS3lrNBp8UBB3eBBMWAWynHWh7KtiyuMKVJ4HiQ377c1d69"
}
response = requests.request("GET", "https://kickasssubtitles.com/api/tasks/01j2pfmd5sth39v4kdrvtbwan2", headers=headers)
task = response.json()
if task["status"] != "completed":
raise Exception("Something went wrong.")
subtitle_file = "subtitle." + task["result"]["subtitles"][0]["extension"]
with open(subtitle_file, "wb") as f:
f.write(base64.b64decode(task["result"]["subtitles"][0]["contents_base64"]))
print(f"Subtitle written to {subtitle_file}")
Subtitles are base64-encoded in the response. You can find more about task response structure in the docs.