---
url: /docs/asr_text_align.md
description: >-
  Text alignment API. Align your own text against the audio and return per-line
  timelines plus the per-character timeline of the audio (microseconds).
---

# ASR\_TEXT\_ALIGN API Documentation

## 🌐 Language Switch

[中文版](/docs/asr_text_align.zh) | [English](/docs/asr_text_align)

## Interface Information

```
POST /openapi/autosubrt/v1/asr/text/align
```

::: warning This is the speech recognition service
The base URL of this interface is `https://autosubrt.jcaigc.cn`, which is a different service (and a different path prefix) from CapCut Mate (`https://capcut-mate.jcaigc.cn`).

```
Full URL = https://autosubrt.jcaigc.cn + /openapi/autosubrt/v1/asr/text/align
```

:::

## Function Description

Align **the text you provide** against the audio: the server first recognizes the audio, then splits your text into subtitle lines and returns the start and end time of every line, together with the timeline of each character in the audio.

Use it when the copy is already written and only the timing is missing (for example a proof-read script). When the recognition result is itself the copy you want, [ASR](/docs/asr) is simpler.

::: warning Time unit is microseconds
This interface returns timelines in **microseconds** (1 second = 1000000), while [ASR](/docs/asr) returns **milliseconds** — a factor of 1000 apart.
:::

## Request Parameters

```json
{
  "audio_url": "https://example.com/audio.mp3",
  "text": "多谢，请入席吧。",
  "max_chars_per_line": 15,
  "apiKey": "123e4567-e89b-12d3-a456-426614174000"
}
```

### Parameter Description

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| audio\_url | string | ✅ | - | Audio file URL, must be downloadable by the server |
| text | string | ✅ | - | Subtitle text matching that audio |
| max\_chars\_per\_line | int | ❌ | 15 | Maximum characters per line; longer text is split into several cues |
| apiKey | string | Required when billing is on | - | A valid UUID; get one at https://jcaigc.cn |

### Parameter Details

#### audio\_url

* **Type**: string
* **Description**: The server downloads this URL before transcribing, so it must be a **direct, publicly reachable link** (not a page that requires login)
* **Format**: Any common audio or video file: wav, mp3, m4a, aac, flac, ogg; video files such as mp4 and mov also work (the audio track is extracted server-side)
* **Limit**: 100MB per file (server-side `FILE_SIZE_LIMIT`, 104857600 bytes by default)

#### text

* **Type**: string
* **Description**: The subtitle text to align; its content should match what is actually said in the audio
* **Splitting rules**: the text is first split into sentences on punctuation (`。！？，；.!?,;:` and newlines) and the **punctuation is dropped** from the result; sentences longer than `max_chars_per_line` are then split into several lines, evenly

#### max\_chars\_per\_line

* **Type**: int
* **Description**: Maximum characters per line, 15 by default. Splitting is even rather than punctuation-aware: 30 characters become 2 lines of 15; 31 characters become 3 lines (11, 10, 10); 46 characters become 4 lines (12, 12, 11, 11)

#### apiKey

* **Type**: string
* **Description**: Must be a valid UUID; required while billing is enabled (it is by default), and the account balance must be **greater than 1**

## Response Format

### Success Response (200)

```json
{
  "code": 0,
  "message": "Success",
  "data": {
    "texts": ["多谢", "请入席吧"],
    "timelines": [
      { "start": 2810000, "end": 3330000 },
      { "start": 4889000, "end": 6009000 }
    ],
    "words": ["多", "谢", "请", "入", "席", "吧"],
    "words_timelines": [
      { "start": 2810000, "end": 3070000 },
      { "start": 3070000, "end": 3330000 },
      { "start": 4889000, "end": 5320000 },
      { "start": 5320000, "end": 5620000 },
      { "start": 5620000, "end": 5860000 },
      { "start": 5860000, "end": 6009000 }
    ]
  }
}
```

### Response Field Description

| Field | Type | Description |
|-------|------|-------------|
| code | int | 0 means success; see the error codes below otherwise |
| message | string | Human-readable message, Chinese by default, English when `Accept-Language: en` is sent |
| data.texts | array | Aligned subtitle lines, one per entry, **punctuation removed** |
| data.timelines | array | Per-line timelines matching `texts` one to one, **in microseconds** |
| data.timelines\[].start | int | Start time (microseconds) |
| data.timelines\[].end | int | End time (microseconds) |
| data.words | array | **Characters recognized from the audio**, in order (not the characters of your text) |
| data.words\_timelines | array | Per-character timelines matching `words` one to one, **in microseconds** |

### Error Response

```json
{
  "code": 2003,
  "message": "Download file failed"
}
```

Error bodies contain **no** `data` field; every error is returned with HTTP 200, so check `code` instead of the status code.

## Usage Examples

### cURL Examples

```bash
curl -X POST https://autosubrt.jcaigc.cn/openapi/autosubrt/v1/asr/text/align \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://example.com/audio.mp3",
    "text": "多谢，请入席吧。",
    "max_chars_per_line": 15,
    "apiKey": "123e4567-e89b-12d3-a456-426614174000"
  }'
```

### Python Example

```python
import requests

resp = requests.post(
    "https://autosubrt.jcaigc.cn/openapi/autosubrt/v1/asr/text/align",
    json={
        "audio_url": "https://example.com/audio.mp3",
        "text": "多谢，请入席吧。",
        "max_chars_per_line": 15,
        "apiKey": "123e4567-e89b-12d3-a456-426614174000",
    },
    timeout=600,
)

result = resp.json()
if result["code"] != 0:
    raise RuntimeError(result["message"])

for line, timeline in zip(result["data"]["texts"], result["data"]["timelines"]):
    # Timelines are microseconds and can be passed to add_captions as start / end
    print(f"{timeline['start'] / 1e6:.2f}s - {timeline['end'] / 1e6:.2f}s  {line}")
```

## Billing

This interface is charged by **audio duration** and shares the same points system as CapCut Mate.

| Item | Description |
| --- | --- |
| Price | **0.00022 points / second** |
| Basis | Actual audio duration in seconds, as reported by the recognition engine |
| When charged | Once the audio is downloaded and recognized — **even when no speech is found**, the actual duration is charged |
| Balance gate | The account must hold **more than 1** point before the call, otherwise it is rejected (2035) |

| Audio duration | Cost (points) |
| --- | --- |
| 10 seconds | 0.002200 |
| 60 seconds | 0.013200 |
| 1 hour | 0.792000 |

`apiKey` must be a valid UUID and can be obtained at <https://jcaigc.cn>.

## Error Code Description

| Error Code | Error Message | Description | Solution |
|------------|---------------|-------------|----------|
| 1001 | Parameter validation failed | audio\_url or text missing, or apiKey is not a valid UUID | Check the request parameters |
| 2001 | Failed to recognize audio | Audio could not be decoded, model not loaded, or audio\_url returned 401/403/404 | Try a playable file or a reachable direct link |
| 2002 | File size exceeds the limit | Audio is larger than 100MB | Compress or trim it and retry |
| 2003 | Download file failed | Network or server error during download, still failing after retries | Make sure it is a public direct download link |
| 2005 | Download file timeout | Overall download timed out | Use a faster source or a smaller file |
| 2035 | Insufficient account balance | The account needs more than 1 point to keep using the service | Recharge and retry |
| 2036 | Invalid apiKey | apiKey missing while billing is enabled | Get an apiKey at https://jcaigc.cn |
| 9997 | Server is busy | Concurrency limit reached; the request was not queued | Retry later |
| 9998 | Internal server error | Unexpected internal error | Contact technical support |
| 9999 | Unknown error | Points service unavailable, etc. | Retry later |

## Notes

1. **Time unit is microseconds** (1 second = 1000000), unlike the milliseconds of [ASR](/docs/asr) and [ASR SRT](/docs/asr_srt). The returned timelines can be passed straight to CapCut Mate [add\_captions](/docs/add_captions) as `start` / `end`.
2. **`texts` carries no punctuation**: punctuation is dropped while splitting. Use [ASR](/docs/asr) when you need punctuated copy.
3. **`words` are the characters recognized from the audio**, not the characters of your `text`; they line up with `words_timelines` one to one and are useful for custom calibration or per-character highlighting.
4. **The call still succeeds when text and audio disagree**: alignment uses edit distance, so extra or missing characters are skipped or spread proportionally over neighboring ranges — the timelines are only approximately correct.
5. **When no speech is recognized** the response is `texts = [your original text]`, `timelines = [{start: 0, end: 30000000}]` (a 30-second placeholder) and empty `words` / `words_timelines`; callers must detect this fallback themselves.
6. **Repeated calls are charged repeatedly**; do not blindly retry after a timeout.

## Workflow

1. Validate apiKey and account balance (while billing is enabled)
2. Download the audio/video behind `audio_url` into a server-side temp directory
3. Run speech recognition to obtain per-character timelines
4. Split the text into sentences on punctuation, then into lines by `max_chars_per_line`, and align text characters to recognized characters by edit distance
5. Compute per-line timelines and calibrate them globally against the character timeline
6. Charge by audio duration once recognition is done
7. Return the texts, per-line timelines and per-character timeline, then delete the temp file

## Related Interfaces

* [ASR](/docs/asr)
* [ASR Text](/docs/asr_text)
* [ASR SRT](/docs/asr_srt)

***

📚 **Project Resources**\
**GitHub**: <https://github.com/Hommy-master/autosubrt>
