Tools API
The same five edits behind the free /tools pages — shift timing, shift a section, cleanup, colour, and position — are also reachable directly over HTTP, for anyone scripting against them instead of using the web form. A call here and the equivalent web upload always produce byte-identical output, since both go through the same underlying engine.
Base URL and authentication
https://www.aisubtitlestudio.com/api/v1/tools
The API is public and unauthenticated for now — no API key, no account, no billing. It's throttled to 60 requests per minute per IP address rather than gated behind a key. This is the surface we intend to sell metered, keyed access to later; the endpoints and request/response shapes below are meant to stay stable when that happens; only what sits in front of them will change. If you're building against it today, treat the current lack of a key as temporary rather than a guarantee.
A plain GET on the base URL above returns a small JSON index of the endpoints below, if you just want to poke at it in a browser.
Making a request
Every endpoint is a POST with a multipart/form-data body — the same shape a browser sends when uploading a file. Every one of them takes:
file | Required. The subtitle file to edit — SRT, WebVTT (.vtt/.webvtt) or ASS/SSA (.ass/.ssa). Up to 5 MB. |
|---|
The format is detected from the file's content first and its extension second, so a .srt file that's actually an ASS script underneath is still read and returned correctly as ASS. Each endpoint's own parameters are listed below it.
A successful call returns the processed file as the raw response body — Content-Type matches the subtitle format (application/x-subrip, text/vtt or text/x-ssa) and Content-Disposition: attachment names it {original name}-{suffix}.{ext}. Pipe the response straight to a file; there's no JSON envelope to unwrap on success.
POST /shift-timing
Moves every subtitle in the file by the same offset.
offset | Required. Seconds, positive or negative — decimals are fine. Positive moves subtitles later, negative moves them earlier. Times are clamped at zero; nothing is ever pushed before the start of the file. |
|---|
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/shift-timing \
-F "file=@movie.srt" \
-F "offset=-2.5" \
-o movie-shifted.srt
POST /shift-section
Moves one or more chosen ranges of subtitles and leaves the rest of the file untouched.
first[] | Optional, one per range. The first subtitle number the range covers (1-based). Omit or leave blank for "from the start of the file". |
|---|---|
last[] | Optional, one per range. The last subtitle number the range covers. Omit or leave blank for "to the end of the file". |
seconds[] | Required, one per range. The offset for that range, same rules as offset above. |
first[], last[] and seconds[] are parallel arrays — the nth value of each belongs to the same range. Where ranges overlap, the first one listed wins; a subtitle covered by more than one range takes its offset from the earliest range that covers it and ignores the rest. A range left at the default of 0 seconds doesn't count for this — it's treated as not yet configured, so a real offset on a range below it still applies.
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/shift-section \
-F "file=@movie.srt" \
-F "first[]=340" -F "last[]=" -F "seconds[]=12" \
-o movie-shifted.srt
Multiple ranges — shift subtitles 1–50 by +1s, and everything from 200 onward by +4s:
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/shift-section \
-F "file=@movie.srt" \
-F "first[]=1" -F "last[]=50" -F "seconds[]=1" \
-F "first[]=200" -F "last[]=" -F "seconds[]=4" \
-o movie-shifted.srt
POST /color
Sets the colour of every subtitle in the file, or strips colouring it already has.
hex | Optional. A 6-digit hex colour, with or without its leading # (ff0000 and #ff0000 both work). Omit it, or send an empty string, to strip existing colouring instead of applying a new one. |
|---|
Written as {\c&Hbbggrr&} for ASS, and a <font color="#rrggbb"> tag for SRT and WebVTT — see the Color tool for what each format actually does with it.
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/color \
-F "file=@movie.srt" \
-F "hex=ffff00" \
-o movie-recolored.srt
Strip existing colouring instead:
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/color \
-F "file=@movie.srt" \
-o movie-recolored.srt
POST /position
Moves every subtitle to a fixed position on screen, or clears any positioning the file already carries.
alignment | Optional. An integer 1–9 in the numpad scheme ASS uses — see the grid below. Omit it, or send an empty string, to clear positioning instead of setting it. |
|---|
| 7 · Top left | 8 · Top | 9 · Top right |
|---|---|---|
| 4 · Middle left | 5 · Middle | 6 · Middle right |
| 1 · Bottom left | 2 · Bottom | 3 · Bottom right |
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/position \
-F "file=@movie.srt" \
-F "alignment=8" \
-o movie-repositioned.srt
POST /cleanup
Strips SDH sound descriptions, credits, ads and other cruft; fixes common encoding, OCR and timing issues. The same rule-based logic as the desktop app's Cleanup tool — pattern matching, not an AI model. SRT and WebVTT only — ASS/SSA isn't supported by this endpoint.
Every parameter is optional and boolean unless noted — omit any of them to use the default shown. Send 1 or 0 to turn an option on or off (this is a multipart/form-data body, so fields are strings — true/false as text is rejected; only 0/1 are accepted).
remove_metadata | Default true. Drops a cue that is nothing but [MUSIC] or (applause). |
|---|---|
remove_sdh | Default true. Strips bracketed sound descriptions and parenthetical sound cues built around a fixed list of sound-related words. |
remove_speaker_labels | Default false. Strips a leading speaker label like SARAH: from the start of a cue. |
remove_music_lyrics | Default false. Strips ♪-wrapped lines and [MUSIC]/[singing]-style tags. |
remove_credits | Default true. Drops a cue that reads like a subtitle-site credit, translator or ripper line. |
remove_narrative | Default false. Drops "Previously on…", "To be continued" and similar recap/narration cues. |
remove_ads | Default true. Drops unambiguous ad boilerplate — "subscribe to our channel", "download our app" — while leaving plausible dialogue alone. |
remove_speaker_markers | Default true. Strips Whisper-style >> markers. |
remove_repetitive | Default true. Collapses consecutive duplicate cues — targets ASR hallucination loops without touching legitimate repeated dialogue spread through the file. |
max_repetitions | Default 3. Integer 1–20. How many consecutive repeats to keep before dropping the rest. |
fix_encoding | Default true. Repairs UTF-8 mis-decoded as Latin-1 (mojibake) and normalizes Unicode typography to ASCII. |
remove_html_tags | Default true. Strips any HTML tag from cue text. |
normalize_punctuation | Default true. Normalizes smart quotes, long dash runs, and 4+-dot ellipses. |
fix_ocr_errors | Default false. Fixes a narrow set of OCR misreads — a standalone "l" that should read "I". |
trim_whitespace | Default true. Trims and collapses extra internal whitespace. |
lowercase_all_caps | Default false. Converts a fully upper-case line to sentence case. |
fix_overlapping | Default true. Pulls back a cue's end time so it never overlaps the next cue's start. |
min_gap | Default 0.04 (seconds). Numeric 0–5. Minimum gap enforced between consecutive cues when fixing overlaps. |
min_duration | Default 0.5 (seconds). Numeric 0–10. Cues shorter than this have their end time extended. Send 0 to disable. |
merge_similar | Default false. Merges consecutive near-duplicate cues within a 5-second gap into one. |
similarity_threshold | Default 0.9. Numeric 0–1. How similar (by containment ratio) consecutive cues must be to merge. |
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/cleanup \
-F "file=@movie.srt" \
-o movie-cleaned.srt
Turn off ad-stripping and enable speaker-label removal:
curl -X POST https://www.aisubtitlestudio.com/api/v1/tools/cleanup \
-F "file=@movie.srt" \
-F "remove_ads=0" \
-F "remove_speaker_labels=1" \
-o movie-cleaned.srt
Errors
Anything other than success comes back as JSON with a 422 status — never a redirect, never HTML.
A file with no subtitles a tool can find (empty, wrong format, unparseable header):
{
"message": "No subtitles found in that file. Make sure it's a valid SRT, WebVTT or ASS/SSA file."
}
A validation failure — a missing or malformed field — carries the same shape Laravel uses everywhere, with per-field messages under errors:
{
"message": "The offset field is required.",
"errors": {
"offset": ["The offset field is required."]
}
}
Exceeding the rate limit returns 429 with the standard X-RateLimit-* headers rather than a JSON body.