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:

fileRequired. 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.

offsetRequired. 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.

hexOptional. 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.

alignmentOptional. 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 left8 · Top9 · Top right
4 · Middle left5 · Middle6 · Middle right
1 · Bottom left2 · Bottom3 · 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_metadataDefault true. Drops a cue that is nothing but [MUSIC] or (applause).
remove_sdhDefault true. Strips bracketed sound descriptions and parenthetical sound cues built around a fixed list of sound-related words.
remove_speaker_labelsDefault false. Strips a leading speaker label like SARAH: from the start of a cue.
remove_music_lyricsDefault false. Strips ♪-wrapped lines and [MUSIC]/[singing]-style tags.
remove_creditsDefault true. Drops a cue that reads like a subtitle-site credit, translator or ripper line.
remove_narrativeDefault false. Drops "Previously on…", "To be continued" and similar recap/narration cues.
remove_adsDefault true. Drops unambiguous ad boilerplate — "subscribe to our channel", "download our app" — while leaving plausible dialogue alone.
remove_speaker_markersDefault true. Strips Whisper-style >> markers.
remove_repetitiveDefault true. Collapses consecutive duplicate cues — targets ASR hallucination loops without touching legitimate repeated dialogue spread through the file.
max_repetitionsDefault 3. Integer 1–20. How many consecutive repeats to keep before dropping the rest.
fix_encodingDefault true. Repairs UTF-8 mis-decoded as Latin-1 (mojibake) and normalizes Unicode typography to ASCII.
remove_html_tagsDefault true. Strips any HTML tag from cue text.
normalize_punctuationDefault true. Normalizes smart quotes, long dash runs, and 4+-dot ellipses.
fix_ocr_errorsDefault false. Fixes a narrow set of OCR misreads — a standalone "l" that should read "I".
trim_whitespaceDefault true. Trims and collapses extra internal whitespace.
lowercase_all_capsDefault false. Converts a fully upper-case line to sentence case.
fix_overlappingDefault true. Pulls back a cue's end time so it never overlaps the next cue's start.
min_gapDefault 0.04 (seconds). Numeric 0–5. Minimum gap enforced between consecutive cues when fixing overlaps.
min_durationDefault 0.5 (seconds). Numeric 0–10. Cues shorter than this have their end time extended. Send 0 to disable.
merge_similarDefault false. Merges consecutive near-duplicate cues within a 5-second gap into one.
similarity_thresholdDefault 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.

FAQ

Do I need an API key?
Not yet. The API is open and unauthenticated for now. That will change when we launch paid access — the endpoints and parameters here are meant to stay the same when it does.
Is the output the same as using the website?
Yes — the API and the /tools web forms call the exact same processing code, so a given file and the same parameters always produce byte-identical output either way.
What formats are supported?
SRT, WebVTT and ASS/SSA, both in and out, for shift-timing, shift-section, color and position. Cleanup supports SRT and WebVTT only — ASS/SSA returns a 422. The format is detected from the file's content, so it doesn't need to match the extension.
What happens to files I upload?
Each request is processed in memory for that one call and never written to disk on our end.
Is there a rate limit?
Yes, 60 requests per minute per IP address. Going over it returns a 429 response.
Can I use this in production?
It's a public preview: functional and stable in shape, but without an SLA or API key yet. If you're building something that depends on it long-term, keep an eye out for the keyed/paid version.