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.