# Free tools API (https://voicelabs.now/docs/free-tools-api)



The free tools on voicelabs.now also answer over HTTP, with **no API key and no account**. They are separate from the [Developer API](/api): that one is keyed and part of VoiceLabs Pro; these are small utilities anyone can call from a script, a CI job or an AI agent.

The OpenAPI 3.1 document for them is at [`https://voicelabs.now/api/tools/openapi.json`](https://voicelabs.now/api/tools/openapi.json). Every operation in it has `security: []` and the tag `Free tools`.

## The contract [#the-contract]

* **No authentication.** Nothing is read from cookies or headers to identify you, so CORS is `*`.
* **POST** a JSON body, a form, or `multipart/form-data` with the file in the `file` field.
* **GET** takes the same fields as query parameters, for small inputs.
* **JSON by default.** Send `Accept: text/markdown` to get the result as a Markdown document instead, or `download=1` to get the resulting file itself with a `Content-Disposition: attachment` header.
* **Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents** (`application/problem+json`) with `title`, `status` and a `detail` written for a person.
* **Rate limit: 30 calls a minute and 300 a day per address.** Past either, the answer is `429` with a `Retry-After` header.
* Every response carries `X-Tool-Docs: https://voicelabs.now/docs/free-tools-api`.
* Inputs are processed in memory and not stored.

## Convert SRT to VTT (or VTT to SRT) [#convert-srt-to-vtt-or-vtt-to-srt]

`POST https://voicelabs.now/api/tools/srt-to-vtt` — the API behind [the SRT to VTT converter](https://voicelabs.now/tools/srt-to-vtt).

| Field               | Required | Meaning                                                                                               |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `input` (or `file`) | yes      | The whole `.srt` or `.vtt` file. The format is detected from the content. Up to 2,000,000 characters. |
| `to`                | no       | `vtt` or `srt`. Default: the other format.                                                            |
| `download`          | no       | `1` to get the file instead of JSON.                                                                  |

```sh
curl -s https://voicelabs.now/api/tools/srt-to-vtt -F file=@subtitles.srt -F download=1 -o subtitles.vtt
```

The JSON answer is `{ "output", "format", "cueCount", "warnings" }`. `warnings` lists everything the target format cannot hold and that was therefore removed: SRT `<font>` and `{\an8}` tags going to WebVTT; voice, class, language and ruby tags, cue settings and NOTE/STYLE/REGION blocks going to SRT. `<b>`, `<i>` and `<u>` are kept both ways, and every time is copied to the millisecond.

## Shift subtitle timing [#shift-subtitle-timing]

`POST https://voicelabs.now/api/tools/subtitle-timing-shift` — the API behind [the subtitle timing shift tool](https://voicelabs.now/tools/subtitle-timing-shift).

| Field               | Required | Meaning                                                                                                             |
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `input` (or `file`) | yes      | The whole `.srt` or `.vtt` file. The result keeps its format.                                                       |
| `offset_seconds`    | yes      | Seconds to move every cue: positive is later, negative earlier. Millisecond precision, at most 24 hours either way. |
| `from_seconds`      | no       | Shift only the cues that start at or after this time. Default `0`.                                                  |
| `download`          | no       | `1` to get the file instead of JSON.                                                                                |

```sh
curl -s https://voicelabs.now/api/tools/subtitle-timing-shift \
  -H 'Content-Type: application/json' \
  -d '{"input":"1\n00:00:05,000 --> 00:00:07,000\nHello","offset_seconds":-1.5}'
```

A cue that would end before `00:00:00` is removed and one that would start before it starts at `00:00:00`; both are counted in `warnings`. WebVTT inline timestamps (`<00:00:01.000>`) move with their cue.

## Validate SSML [#validate-ssml]

`POST https://voicelabs.now/api/tools/ssml-validator` — the API behind [the SSML validator](https://voicelabs.now/tools/ssml-validator).

| Field                          | Required | Meaning                                      |
| ------------------------------ | -------- | -------------------------------------------- |
| `ssml` (or `input`, or `file`) | yes      | The SSML document. Up to 200,000 characters. |

```sh
curl -s https://voicelabs.now/api/tools/ssml-validator \
  -H 'Accept: text/markdown' \
  --data-urlencode 'ssml=<speak>Hello <break time="500ms"/> world</speak>'
```

The JSON answer is `{ "valid", "errors", "warnings", "elementCount", "textCharacters" }`; each error and warning has a `line`, a `column` and a `message`. An invalid document is still a `200` with `valid: false`. Errors are what XML or the [W3C SSML 1.1 Recommendation](https://www.w3.org/TR/speech-synthesis11/) forbids: an unclosed or mismatched tag, an element in a place it is not allowed, a missing required attribute, or a value outside its grammar (such as `time="5"` on `<break>`). Vendor extensions such as `<amazon:effect>` or `<mstts:express-as>`, and a `<speak>` without `version`, `xml:lang` or `xmlns`, are warnings: most engines accept them.

## Errors [#errors]

| Status | When                                                                                                           |
| ------ | -------------------------------------------------------------------------------------------------------------- |
| `400`  | A required field is missing or not a number (`offset_seconds`), or `to` is not `srt`/`vtt`.                    |
| `413`  | The body is over 2.5 MB.                                                                                       |
| `415`  | The body is not JSON, a form or multipart.                                                                     |
| `422`  | The input has no subtitle cues in it, the shift would leave none, or the SSML document is over the size limit. |
| `429`  | Over 30 calls a minute or 300 a day from one address.                                                          |
