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 implementation in Python:
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.