# TTS / STT / Translation API

TTS/STT/Dịch dùng web_api_key tại Tài khoản qua Authorization: Bearer. Hai loại key không thay thế nhau. Domain cũ tiếp tục hoạt động; chuyển sang domain mới chỉ thay hostname, giữ đường dẫn, key và nội dung. Gói, Credit ngày, cache và thu phí giữ cơ chế cũ: TTS theo ký tự từng dòng, STT làm tròn lên phút (tối thiểu 1), Dịch theo ký tự bản dịch. Phần đã xử lý có thể được thu phí khi tác vụ lỗi hoặc dừng. Hạn mức USD của API Model không áp dụng. POST tạo dự án cũ không có bảo đảm Idempotency-Key; khi mất phản hồi không tự gửi lại POST.

## Reference

### Lấy số dư credit

GET https://api.ntsgenai.com/api/v1/public-api/balance

Key: web_api_key · /account

Trả về số dư credit hiện tại (base_balance + daily_balance), credit miễn phí trong ngày, gói trả phí đang còn hạn và hạng VIP.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |

Response example:

```json
{
  "success": true,
  "data": {
    "credit_balance": 105000,
    "base_balance": 100000,
    "daily_balance": 5000,
    "free_daily_credit": 5000,
    "free_chars_used": 0,
    "free_daily_char_limit": 1000000,
    "plans": [
      {
        "plan_id": "pro",
        "name": "Chuyên nghiệp",
        "daily_allowance": 100000,
        "expires_at": "2026-07-23 00:00:00"
      }
    ],
    "vip": {
      "tier": 1,
      "name": "Đồng"
    }
  }
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/balance \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/balance"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/balance";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/balance",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/balance"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/balance")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/balance");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/balance")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/balance";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/balance")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Danh sách giọng

GET https://api.ntsgenai.com/api/v1/public-api/voices

Key: web_api_key · /account

Danh sách giọng đọc (phân trang + lọc). Mỗi giọng có `voice_id` là chuỗi opaque, dùng trực tiếp cho `voices[].voice_id` ở endpoint tạo TTS.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| language | query | string | false | Mã ngôn ngữ BCP-47 (vd `vi-VN`, `en-US`). |
| gender | query | string | false | `male` hoặc `female`. |
| tier | query | string | false | `free` / `standard` / `professional` / `professional_clone`. |
| search | query | string | false | Tìm theo tên hoặc mô tả giọng. |
| sort | query | string | false | `popular` (mặc định) / `least` / `name`. |
| offset | query | number | false | Bỏ qua N kết quả đầu. |
| limit | query | number | false | Số kết quả tối đa (mặc định 20). |

Response example:

```json
{
  "success": true,
  "data": [
    {
      "voice_id": "6748abc1234567890def0123",
      "name": "Hoài My",
      "language": "vi-VN",
      "gender": "female",
      "tier": "standard",
      "price_base": 1
    }
  ],
  "total": 100,
  "has_more": true
}
```

#### curl

```shell
curl --request GET \
  --url 'https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20' \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/voices";

    let querystring = [
        ("language", "vi-VN"),
        ("sort", "popular"),
        ("offset", "0"),
        ("limit", "20"),
    ];

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .query(&querystring)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/voices?language=vi-VN&sort=popular&offset=0&limit=20")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Tạo dự án TTS

POST https://api.ntsgenai.com/api/v1/public-api/tts

Key: web_api_key · /account

Tạo dự án text-to-speech rồi START luôn (giới hạn luồng y hệt giao diện web). Trả về `project_id` + `req_id` để theo dõi. Dự án sẽ hiện trên trang `/tts` của tài khoản.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| name | body | string | true | Tên dự án (hiển thị trên web). |
| source_type | body | string | false | `text` \| `srt` \| `vtt` \| `ass` (mặc định `text`). |
| has_timing | body | boolean | false | `true` nếu có dấu thời gian (SRT/VTT/ASS). |
| lines | body | array | true | Mảng dòng: `[{ text, vocal_no }]` — `vocal_no` map sang `voices`. |
| voices | body | array | true | Mảng giọng: `[{ vocal_no, voice_id }]` — `voice_id` lấy từ `/voices`. |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "req_id": "fedcba987654321...",
    "status": "pending",
    "cost": 30
  }
}
```

#### curl

```shell
curl --request POST \
  --url https://api.ntsgenai.com/api/v1/public-api/tts \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "My TTS project",
  "source_type": "text",
  "has_timing": false,
  "lines": [
    {
      "text": "Xin chào, đây là bản test của API.",
      "vocal_no": 1
    }
  ],
  "voices": [
    {
      "vocal_no": 1,
      "voice_id": "6748abc1234567890def0123"
    }
  ]
}'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/tts"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\",\"Content-Type\":\"application/json\"}")
payload = json.loads("{\"name\":\"My TTS project\",\"source_type\":\"text\",\"has_timing\":false,\"lines\":[{\"text\":\"Xin chào, đây là bản test của API.\",\"vocal_no\":1}],\"voices\":[{\"vocal_no\":1,\"voice_id\":\"6748abc1234567890def0123\"}]}")
with requests.request("POST", url, headers=headers, json=payload,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/tts";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>",
  "Content-Type": "application/json"
};
const payload = {
  "name": "My TTS project",
  "source_type": "text",
  "has_timing": false,
  "lines": [
    {
      "text": "Xin chào, đây là bản test của API.",
      "vocal_no": 1
    }
  ],
  "voices": [
    {
      "vocal_no": 1,
      "voice_id": "6748abc1234567890def0123"
    }
  ]
};
const response = await fetch(url, {
  method: "POST", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
  body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/tts",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => json_encode([
    'name' => 'My TTS project',
    'source_type' => 'text',
    'has_timing' => null,
    'lines' => [
        [
                'text' => 'Xin chào, đây là bản test của API.',
                'vocal_no' => 1
        ]
    ],
    'voices' => [
        [
                'vocal_no' => 1,
                'voice_id' => '6748abc1234567890def0123'
        ]
    ]
  ]),
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>",
    "Content-Type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/tts"

	payload := strings.NewReader("{\n  \"name\": \"My TTS project\",\n  \"source_type\": \"text\",\n  \"has_timing\": false,\n  \"lines\": [\n    {\n      \"text\": \"Xin chào, đây là bản test của API.\",\n      \"vocal_no\": 1\n    }\n  ],\n  \"voices\": [\n    {\n      \"vocal_no\": 1,\n      \"voice_id\": \"6748abc1234567890def0123\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"name\": \"My TTS project\",\n  \"source_type\": \"text\",\n  \"has_timing\": false,\n  \"lines\": [\n    {\n      \"text\": \"Xin chào, đây là bản test của API.\",\n      \"vocal_no\": 1\n    }\n  ],\n  \"voices\": [\n    {\n      \"vocal_no\": 1,\n      \"voice_id\": \"6748abc1234567890def0123\"\n    }\n  ]\n}");
Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/tts")
  .post(body)
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .addHeader("Content-Type", "application/json")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/tts");
var request = new RestRequest("", Method.Post);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"name\": \"My TTS project\",\n  \"source_type\": \"text\",\n  \"has_timing\": false,\n  \"lines\": [\n    {\n      \"text\": \"Xin chào, đây là bản test của API.\",\n      \"vocal_no\": 1\n    }\n  ],\n  \"voices\": [\n    {\n      \"vocal_no\": 1,\n      \"voice_id\": \"6748abc1234567890def0123\"\n    }\n  ]\n}", ParameterType.RequestBody);
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/tts")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"name\": \"My TTS project\",\n  \"source_type\": \"text\",\n  \"has_timing\": false,\n  \"lines\": [\n    {\n      \"text\": \"Xin chào, đây là bản test của API.\",\n      \"vocal_no\": 1\n    }\n  ],\n  \"voices\": [\n    {\n      \"vocal_no\": 1,\n      \"voice_id\": \"6748abc1234567890def0123\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use serde_json::json;
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/tts";

    let payload = json!({
        "name": "My TTS project",
        "source_type": "text",
        "has_timing": false,
        "lines": (
            json!({
                "text": "Xin chào, đây là bản test của API.",
                "vocal_no": 1
            })
        ),
        "voices": (
            json!({
                "vocal_no": 1,
                "voice_id": "6748abc1234567890def0123"
            })
        )
    });

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());
    headers.insert("Content-Type", "application/json".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.post(url)
        .headers(headers)
        .json(&payload)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/tts")!)
request.httpMethod = "POST"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data("{\"name\":\"My TTS project\",\"source_type\":\"text\",\"has_timing\":false,\"lines\":[{\"text\":\"Xin chào, đây là bản test của API.\",\"vocal_no\":1}],\"voices\":[{\"vocal_no\":1,\"voice_id\":\"6748abc1234567890def0123\"}]}".utf8)
let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Lấy kết quả

GET https://api.ntsgenai.com/api/v1/public-api/tts/{project_id}

Key: web_api_key · /account

Poll trạng thái dự án. Khi `status` = `done`, `audio_url` là URL file âm thanh (presigned 48 giờ); `srt_url` là file phụ đề (nếu có).

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| project_id | path | string | true | ID dự án (trả về từ `POST /tts`). |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "name": "My TTS project",
    "status": "done",
    "progress": 100,
    "audio_url": "https://cdn.example.com/tts/user/abc.mp3?...",
    "srt_url": "",
    "queue_position": null,
    "created_at": "2026-06-23 10:00:00",
    "updated_at": "2026-06-23 10:02:30"
  }
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789... \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789..."
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789..."

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/tts/abc123def456789...")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Danh sách model STT

GET https://api.ntsgenai.com/api/v1/public-api/stt/models

Key: web_api_key · /account

Danh sách model nhận dạng giọng nói (Speech-to-Text). Mỗi model có `model_id` (dùng khi tạo dự án) và `credit_per_minute` (tỷ lệ trừ credit theo số phút audio).

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |

Response example:

```json
{
  "success": true,
  "data": [
    {
      "model_id": "whisper-standard",
      "name": "Whisper Standard",
      "credit_per_minute": 100,
      "description": "Nhận dạng giọng nói đa ngôn ngữ, có timestamp theo từ."
    }
  ]
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/stt/models \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/stt/models"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/stt/models";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/stt/models",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/stt/models"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/stt/models")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/stt/models");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/stt/models")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/stt/models";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/stt/models")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Tạo dự án STT (nhận dạng)

POST https://api.ntsgenai.com/api/v1/public-api/stt/transcribe

Key: web_api_key · /account

Tạo dự án nhận dạng giọng nói rồi START luôn (giới hạn 2 luồng như web). Gửi dạng `multipart/form-data` hoặc `application/x-www-form-urlencoded`: hoặc `url_file` (link video/audio công khai) HOẶC tải lên `file` (multipart). Trả `project_id` + `req_id` để poll. Trừ credit theo PHÚT khi worker xử lý xong. Dự án hiện trên trang `/stt` của tài khoản.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| model_id | body | string | true | ID model STT (lấy từ `/stt/models`). |
| url_file | body | string | false | Link video/audio công khai (YouTube/TikTok/link file trực tiếp). Bỏ trống nếu tải `file`. |
| file | body | string | false | File video/audio (multipart/form-data). Dùng khi KHÔNG có `url_file`. |
| language | body | string | false | Mã ngôn ngữ nói (vd `vi`, `en`). Bỏ trống = tự động nhận diện. |
| name | body | string | false | Tên dự án (hiển thị trên /stt). |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "req_id": "fedcba987654321...",
    "status": "pending"
  }
}
```

#### curl

```shell
curl --request POST \
  --url https://api.ntsgenai.com/api/v1/public-api/stt/transcribe \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data model_id=whisper-standard \
  --data url_file=https://example.com/audio.mp3 \
  --data language= \
  --data 'name=My transcription'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\",\"Content-Type\":\"application/x-www-form-urlencoded\"}")
payload = json.loads("{\"model_id\":\"whisper-standard\",\"url_file\":\"https://example.com/audio.mp3\",\"language\":\"\",\"name\":\"My transcription\"}")
with requests.request("POST", url, headers=headers, data=payload,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>",
  "Content-Type": "application/x-www-form-urlencoded"
};
const payload = {
  "model_id": "whisper-standard",
  "url_file": "https://example.com/audio.mp3",
  "language": "",
  "name": "My transcription"
};
const response = await fetch(url, {
  method: "POST", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
  body: new URLSearchParams(payload),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My%20transcription",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>",
    "Content-Type: application/x-www-form-urlencoded"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe"

	payload := strings.NewReader("model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My%20transcription")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My%20transcription");
Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/stt/transcribe")
  .post(body)
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .addHeader("Content-Type", "application/x-www-form-urlencoded")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/stt/transcribe");
var request = new RestRequest("", Method.Post);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My%20transcription", ParameterType.RequestBody);
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/stt/transcribe")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'
request["Content-Type"] = 'application/x-www-form-urlencoded'
request.body = "model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My%20transcription"

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use serde_json::json;
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe";

    let payload = json!({
        "model_id": "whisper-standard",
        "url_file": "https://example.com/audio.mp3",
        "language": "",
        "name": "My transcription"
    });

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());
    headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.post(url)
        .headers(headers)
        .form(&payload)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/stt/transcribe")!)
request.httpMethod = "POST"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = Data("model_id=whisper-standard&url_file=https%3A%2F%2Fexample.com%2Faudio.mp3&language=&name=My+transcription".utf8)
let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Lấy kết quả STT

GET https://api.ntsgenai.com/api/v1/public-api/stt/{project_id}

Key: web_api_key · /account

Poll trạng thái dự án STT. Khi `status` = `done`, `transcript_url` là URL file JSON kết quả (chuẩn Whisper: `segments` + `words`, presigned 48 giờ). Tải URL đó để lấy nội dung phụ đề; xuất txt/srt/vtt/ass tự thực hiện phía bạn.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| project_id | path | string | true | ID dự án (trả về từ `POST /stt/transcribe`). |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "name": "My transcription",
    "status": "done",
    "progress": 100,
    "duration_seconds": 184.5,
    "segment_count": 42,
    "language": "vi",
    "transcript_url": "https://cdn.example.com/stt/user/abc.json?...",
    "queue_position": null,
    "created_at": "2026-06-26 10:00:00",
    "updated_at": "2026-06-26 10:03:10"
  }
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789... \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789..."
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789..."

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/stt/abc123def456789...")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Danh sách model dịch

GET https://api.ntsgenai.com/api/v1/public-api/translate/models

Key: web_api_key · /account

Danh sách model dịch phụ đề + danh sách ngôn ngữ đích hỗ trợ. Mỗi model có `model_id` (dùng khi tạo dự án) và `price_base` (tỷ lệ trừ credit / ký tự bản dịch). `languages` là tên ngôn ngữ đích hợp lệ cho `target_lang`.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |

Response example:

```json
{
  "success": true,
  "data": {
    "models": [
      {
        "model_id": "context-10",
        "name": "Ngữ cảnh phim 10",
        "price_base": 10,
        "description": "Dịch theo ngữ cảnh, chất lượng phim."
      }
    ],
    "languages": [
      "Vietnamese",
      "English",
      "Japanese",
      "Korean"
    ]
  }
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/translate/models \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/translate/models"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/translate/models";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/translate/models",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/translate/models"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/translate/models")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/translate/models");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/translate/models")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/translate/models";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/translate/models")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Tạo dự án dịch phụ đề

POST https://api.ntsgenai.com/api/v1/public-api/translate/subtitle

Key: web_api_key · /account

Gửi NỘI DUNG phụ đề định dạng SRT (`content`) rồi START luôn (giới hạn luồng + hàng đợi như web). Trả `project_id` + `req_id` để poll. Trừ credit theo số ký tự BẢN DỊCH khi xong. Dự án hiện trên trang `/translate` của tài khoản. `detect_mode`: `none` (không nhận dạng người nói) / `gender` (Nam–Nữ) / `multi` (nhiều người nói).

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| content | body | string | true | Nội dung phụ đề SRT (raw text — gồm số thứ tự, mốc thời gian, văn bản). |
| target_lang | body | string | true | Tên ngôn ngữ đích (PHẢI thuộc `languages` của `/translate/models`). |
| model_id | body | string | true | ID model dịch (lấy từ `/translate/models`). |
| source_lang | body | string | false | Tên ngôn ngữ nguồn. Bỏ trống = để engine tự nhận. |
| detect_mode | body | string | false | `none` (mặc định) / `gender` / `multi` — nhận dạng người nói. |
| name | body | string | false | Tên dự án (hiển thị trên /translate). |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "req_id": "fedcba987654321...",
    "status": "pending"
  }
}
```

#### curl

```shell
curl --request POST \
  --url https://api.ntsgenai.com/api/v1/public-api/translate/subtitle \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
  "content": "1\n00:00:01,000 --> 00:00:02,500\nHello world\n\n2\n00:00:03,000 --> 00:00:04,000\nHow are you?",
  "target_lang": "Vietnamese",
  "model_id": "context-10",
  "source_lang": "English",
  "detect_mode": "none",
  "name": "My translation"
}'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle"
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\",\"Content-Type\":\"application/json\"}")
payload = json.loads("{\"content\":\"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\"target_lang\":\"Vietnamese\",\"model_id\":\"context-10\",\"source_lang\":\"English\",\"detect_mode\":\"none\",\"name\":\"My translation\"}")
with requests.request("POST", url, headers=headers, json=payload,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>",
  "Content-Type": "application/json"
};
const payload = {
  "content": "1\n00:00:01,000 --> 00:00:02,500\nHello world\n\n2\n00:00:03,000 --> 00:00:04,000\nHow are you?",
  "target_lang": "Vietnamese",
  "model_id": "context-10",
  "source_lang": "English",
  "detect_mode": "none",
  "name": "My translation"
};
const response = await fetch(url, {
  method: "POST", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
  body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => json_encode([
    'content' => '1
00:00:01,000 --> 00:00:02,500
Hello world

2
00:00:03,000 --> 00:00:04,000
How are you?',
    'target_lang' => 'Vietnamese',
    'model_id' => 'context-10',
    'source_lang' => 'English',
    'detect_mode' => 'none',
    'name' => 'My translation'
  ]),
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>",
    "Content-Type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle"

	payload := strings.NewReader("{\n  \"content\": \"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\n  \"target_lang\": \"Vietnamese\",\n  \"model_id\": \"context-10\",\n  \"source_lang\": \"English\",\n  \"detect_mode\": \"none\",\n  \"name\": \"My translation\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"content\": \"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\n  \"target_lang\": \"Vietnamese\",\n  \"model_id\": \"context-10\",\n  \"source_lang\": \"English\",\n  \"detect_mode\": \"none\",\n  \"name\": \"My translation\"\n}");
Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/translate/subtitle")
  .post(body)
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .addHeader("Content-Type", "application/json")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/translate/subtitle");
var request = new RestRequest("", Method.Post);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"content\": \"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\n  \"target_lang\": \"Vietnamese\",\n  \"model_id\": \"context-10\",\n  \"source_lang\": \"English\",\n  \"detect_mode\": \"none\",\n  \"name\": \"My translation\"\n}", ParameterType.RequestBody);
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/translate/subtitle")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"content\": \"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\n  \"target_lang\": \"Vietnamese\",\n  \"model_id\": \"context-10\",\n  \"source_lang\": \"English\",\n  \"detect_mode\": \"none\",\n  \"name\": \"My translation\"\n}"

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use serde_json::json;
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle";

    let payload = json!({
        "content": "1
00:00:01,000 --> 00:00:02,500
Hello world

2
00:00:03,000 --> 00:00:04,000
How are you?",
        "target_lang": "Vietnamese",
        "model_id": "context-10",
        "source_lang": "English",
        "detect_mode": "none",
        "name": "My translation"
    });

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());
    headers.insert("Content-Type", "application/json".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.post(url)
        .headers(headers)
        .json(&payload)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/translate/subtitle")!)
request.httpMethod = "POST"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data("{\"content\":\"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\"target_lang\":\"Vietnamese\",\"model_id\":\"context-10\",\"source_lang\":\"English\",\"detect_mode\":\"none\",\"name\":\"My translation\"}".utf8)
let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

### Lấy kết quả dịch

GET https://api.ntsgenai.com/api/v1/public-api/translate/{project_id}

Key: web_api_key · /account

Poll trạng thái dự án dịch. Mảng `lines` cập nhật dần: mỗi dòng có `text` (gốc), `translate_text` (bản dịch), `vocal` (số người nói khi `detect_mode` ≠ `none`). Khi `status` = `done` là dịch xong; bạn tự dựng SRT/VTT/ASS/JSON từ `lines`.

| Field | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| project_id | path | string | true | ID dự án (trả về từ `POST /translate/subtitle`). |

Response example:

```json
{
  "success": true,
  "data": {
    "project_id": "abc123def456789...",
    "name": "My translation",
    "status": "done",
    "progress": 100,
    "source_lang": "English",
    "target_lang": "Vietnamese",
    "detect_mode": "none",
    "segment_count": 2,
    "translated_lines": 2,
    "lines": [
      {
        "line_no": 1,
        "start_time": "00:00:01,000",
        "end_time": "00:00:02,500",
        "text": "Hello world",
        "translate_text": "Xin chào thế giới",
        "vocal": 0
      }
    ],
    "queue_position": null,
    "created_at": "2026-06-29 10:00:00",
    "updated_at": "2026-06-29 10:01:20"
  }
}
```

#### curl

```shell
curl --request GET \
  --url https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789... \
  --header 'Authorization: Bearer <YOUR_API_KEY>'
```

#### Python

```python
# Python 3: pip install requests
import json
import requests

url = "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789..."
headers = json.loads("{\"Authorization\":\"Bearer <YOUR_API_KEY>\"}")

with requests.request("GET", url, headers=headers,
                      timeout=(10, 330), allow_redirects=False, stream=True) as response:
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    if "text/event-stream" in response.headers.get("Content-Type", ""):
        for line in response.iter_lines():
            if line: print(line.decode("utf-8"), flush=True)
    else:
        print(response.json())

```

#### JavaScript

```javascript
// Node.js 18+ or a browser with fetch; save as .mjs for Node.
const url = "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...";
const headers = {
  "Authorization": "Bearer <YOUR_API_KEY>"
};

const response = await fetch(url, {
  method: "GET", headers, redirect: 'error',
  signal: AbortSignal.timeout(330000),
});
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
if (response.headers.get('content-type')?.includes('text/event-stream')) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    process.stdout.write(decoder.decode(value, { stream: true })); // Browser: console.log(...)
  }
} else { console.log(await response.json()); }

```

#### PHP

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <YOUR_API_KEY>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
```

#### Go

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789..."

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

#### Java

```java
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false).build();

Request request = new Request.Builder()
  .url("https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...")
  .get()
  .addHeader("Authorization", "Bearer <YOUR_API_KEY>")
  .build();

Response response = client.newCall(request).execute();
```

#### C#

```csharp
var client = new RestClient("https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...");
var request = new RestRequest("", Method.Get);
request.AddHeader("Authorization", "Bearer <YOUR_API_KEY>");
var response = client.Execute(request);
```

#### Ruby

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <YOUR_API_KEY>'

response = http.request(request)
puts response.read_body
```

#### Rust

```rust
use reqwest;

#[tokio::main]
pub async fn main() {
    let url = "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...";

    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert("Authorization", "Bearer <YOUR_API_KEY>".parse().unwrap());

    let client = reqwest::Client::new();
    let response = client.get(url)
        .headers(headers)
        .send()
        .await;

    let results = response.unwrap()
        .json::<serde_json::Value>()
        .await
        .unwrap();

    dbg!(results);
}

```

#### Swift

```swift
// Swift command-line example (Foundation).
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api.ntsgenai.com/api/v1/public-api/translate/abc123def456789...")!)
request.httpMethod = "GET"
request.timeoutInterval = 330
request.setValue("Bearer <YOUR_API_KEY>", forHTTPHeaderField: "Authorization")

let finished = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: request) { data, response, error in
    defer { finished.signal() }
    if let error = error { print(error); return }
    if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
    if let data = data { print(String(decoding: data, as: UTF8.self)) }
}.resume()
finished.wait()

```

## TTS · python

```python
# Python 3: pip install requests
import json, os, time, uuid
from pathlib import Path
from urllib.parse import quote, urlparse
import requests

BASE = "https://api.ntsgenai.com/api/v1/public-api"
KEY = os.environ["NTS_WEB_API_KEY"]
headers = {"Authorization": "Bearer " + KEY}
# Legacy creation has no idempotency guarantee. Never automatically retry POST.
payload = json.loads("{\"name\":\"My TTS project\",\"source_type\":\"text\",\"has_timing\":false,\"lines\":[{\"text\":\"Xin chào, đây là bản test của API.\",\"vocal_no\":1}],\"voices\":[{\"vocal_no\":1,\"voice_id\":\"6748abc1234567890def0123\"}]}")
# Replace sample model / voice IDs using the catalog first.
created = requests.post(BASE + "/tts", headers=headers,
    json=payload, timeout=330, allow_redirects=False)
created.raise_for_status()
if created.status_code >= 300: raise RuntimeError("Unexpected redirect")
document = created.json()
if document.get("success") is False: raise RuntimeError(document)
job_id = document["data"]["project_id"]
Path("nts-job-id.txt").write_text(job_id, encoding="utf-8")
print("Saved job:", job_id)
deadline = time.monotonic() + 1800
while True:
    response = requests.get(BASE + "/tts/__JOB_ID__".replace("__JOB_ID__", quote(job_id, safe="")),
        headers={"Authorization": "Bearer " + KEY}, timeout=60, allow_redirects=False)
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    document = response.json()
    if document.get("success") is False: raise RuntimeError(document)
    job = document["data"]
    print(job["status"])
    if job["status"] in ("completed", "done", "succeeded"): break
    if job["status"] in ("failed", "error", "cancelled", "stopped", "needs_review"):
        raise RuntimeError(job)  # Reconcile this job; do not submit a replacement automatically.
    if time.monotonic() >= deadline: raise TimeoutError("Keep job ID; poll again later")
    time.sleep(3)
url = job.get("audio_url")
if not url: raise RuntimeError("No download URL; retain job ID and check expiry")
if urlparse(url).scheme != "https": raise RuntimeError("Expected HTTPS result URL")
# No Authorization header on the signed storage URL.
with requests.get(url, stream=True, timeout=120, allow_redirects=False) as download:
    download.raise_for_status()
    if download.status_code >= 300: raise RuntimeError("Unexpected storage redirect")
    with open("audio.wav", "wb") as output:
        for chunk in download.iter_content(1024 * 1024): output.write(chunk)
print("Saved", "audio.wav")

```

## TTS · javascript

```javascript
// Node.js 18+. Save as workflow.mjs; run: node workflow.mjs
import { writeFile } from 'node:fs/promises';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { randomUUID } from 'node:crypto';

const base = "https://api.ntsgenai.com/api/v1/public-api";
const key = process.env.NTS_WEB_API_KEY;
if (!key) throw new Error('Set the correct API key environment variable');
const headers = { Authorization: 'Bearer ' + key, 'Content-Type': 'application/json' };
const payload = {"name":"My TTS project","source_type":"text","has_timing":false,"lines":[{"text":"Xin chào, đây là bản test của API.","vocal_no":1}],"voices":[{"vocal_no":1,"voice_id":"6748abc1234567890def0123"}]}; // Replace sample model / voice IDs first.
async function jsonRequest(path, options = {}) {
  const response = await fetch(base + path, { ...options, redirect: 'error', signal: AbortSignal.timeout(330000) });
  if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
  const document = await response.json();
  if (document.success === false) throw new Error(JSON.stringify(document));
  return document;
}
// POST exactly once. On uncertain submission, reconcile; do not auto-create another job.
const created = await jsonRequest("/tts", { method: 'POST', headers,
  body: JSON.stringify(payload) });
const jobId = created.data.project_id;
await writeFile('nts-job-id.txt', jobId);
console.log('Saved job:', jobId);
const deadline = Date.now() + 1800000;
let job;
while (true) {
  const document = await jsonRequest("/tts/__JOB_ID__".replace('__JOB_ID__', encodeURIComponent(jobId)),
    { headers: { Authorization: 'Bearer ' + key } });
  job = document.data;
  console.log(job.status);
  if (['completed', 'done', 'succeeded'].includes(job.status)) break;
  if (['failed', 'error', 'cancelled', 'stopped', 'needs_review'].includes(job.status)) throw new Error(JSON.stringify(job));
  if (Date.now() >= deadline) throw new Error('Keep job ID; poll again later');
  await new Promise(resolve => setTimeout(resolve, 3000));
}
const url = job.audio_url;
if (!url || new URL(url).protocol !== 'https:') throw new Error('No valid download URL; check job expiry');
// No API key is sent to file storage.
const download = await fetch(url, { redirect: 'error', signal: AbortSignal.timeout(120000) });
if (!download.ok || !download.body) throw new Error('Download failed: ' + download.status);
await pipeline(Readable.fromWeb(download.body), createWriteStream("audio.wav"));
console.log('Saved', "audio.wav");

```

## STT · python

```python
# Python 3: pip install requests
import json, os, time, uuid
from pathlib import Path
from urllib.parse import quote, urlparse
import requests

BASE = "https://api.ntsgenai.com/api/v1/public-api"
KEY = os.environ["NTS_WEB_API_KEY"]
headers = {"Authorization": "Bearer " + KEY}
# Legacy creation has no idempotency guarantee. Never automatically retry POST.
payload = json.loads("{\"model_id\":\"whisper-standard\",\"url_file\":\"https://example.com/audio.mp3\",\"language\":\"\",\"name\":\"My transcription\"}")
# Replace sample model / voice IDs using the catalog first.
created = requests.post(BASE + "/stt/transcribe", headers=headers,
    data=payload, timeout=330, allow_redirects=False)
created.raise_for_status()
if created.status_code >= 300: raise RuntimeError("Unexpected redirect")
document = created.json()
if document.get("success") is False: raise RuntimeError(document)
job_id = document["data"]["project_id"]
Path("nts-job-id.txt").write_text(job_id, encoding="utf-8")
print("Saved job:", job_id)
deadline = time.monotonic() + 1800
while True:
    response = requests.get(BASE + "/stt/__JOB_ID__".replace("__JOB_ID__", quote(job_id, safe="")),
        headers={"Authorization": "Bearer " + KEY}, timeout=60, allow_redirects=False)
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    document = response.json()
    if document.get("success") is False: raise RuntimeError(document)
    job = document["data"]
    print(job["status"])
    if job["status"] in ("completed", "done", "succeeded"): break
    if job["status"] in ("failed", "error", "cancelled", "stopped", "needs_review"):
        raise RuntimeError(job)  # Reconcile this job; do not submit a replacement automatically.
    if time.monotonic() >= deadline: raise TimeoutError("Keep job ID; poll again later")
    time.sleep(3)
url = job.get("transcript_url")
if not url: raise RuntimeError("No download URL; retain job ID and check expiry")
if urlparse(url).scheme != "https": raise RuntimeError("Expected HTTPS result URL")
# No Authorization header on the signed storage URL.
with requests.get(url, stream=True, timeout=120, allow_redirects=False) as download:
    download.raise_for_status()
    if download.status_code >= 300: raise RuntimeError("Unexpected storage redirect")
    with open("transcript.json", "wb") as output:
        for chunk in download.iter_content(1024 * 1024): output.write(chunk)
print("Saved", "transcript.json")

```

## STT · javascript

```javascript
// Node.js 18+. Save as workflow.mjs; run: node workflow.mjs
import { writeFile } from 'node:fs/promises';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { randomUUID } from 'node:crypto';

const base = "https://api.ntsgenai.com/api/v1/public-api";
const key = process.env.NTS_WEB_API_KEY;
if (!key) throw new Error('Set the correct API key environment variable');
const headers = { Authorization: 'Bearer ' + key, 'Content-Type': 'application/x-www-form-urlencoded' };
const payload = {"model_id":"whisper-standard","url_file":"https://example.com/audio.mp3","language":"","name":"My transcription"}; // Replace sample model / voice IDs first.
async function jsonRequest(path, options = {}) {
  const response = await fetch(base + path, { ...options, redirect: 'error', signal: AbortSignal.timeout(330000) });
  if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
  const document = await response.json();
  if (document.success === false) throw new Error(JSON.stringify(document));
  return document;
}
// POST exactly once. On uncertain submission, reconcile; do not auto-create another job.
const created = await jsonRequest("/stt/transcribe", { method: 'POST', headers,
  body: new URLSearchParams(payload) });
const jobId = created.data.project_id;
await writeFile('nts-job-id.txt', jobId);
console.log('Saved job:', jobId);
const deadline = Date.now() + 1800000;
let job;
while (true) {
  const document = await jsonRequest("/stt/__JOB_ID__".replace('__JOB_ID__', encodeURIComponent(jobId)),
    { headers: { Authorization: 'Bearer ' + key } });
  job = document.data;
  console.log(job.status);
  if (['completed', 'done', 'succeeded'].includes(job.status)) break;
  if (['failed', 'error', 'cancelled', 'stopped', 'needs_review'].includes(job.status)) throw new Error(JSON.stringify(job));
  if (Date.now() >= deadline) throw new Error('Keep job ID; poll again later');
  await new Promise(resolve => setTimeout(resolve, 3000));
}
const url = job.transcript_url;
if (!url || new URL(url).protocol !== 'https:') throw new Error('No valid download URL; check job expiry');
// No API key is sent to file storage.
const download = await fetch(url, { redirect: 'error', signal: AbortSignal.timeout(120000) });
if (!download.ok || !download.body) throw new Error('Download failed: ' + download.status);
await pipeline(Readable.fromWeb(download.body), createWriteStream("transcript.json"));
console.log('Saved', "transcript.json");

```

## Translation · python

```python
# Python 3: pip install requests
import json, os, time, uuid
from pathlib import Path
from urllib.parse import quote, urlparse
import requests

BASE = "https://api.ntsgenai.com/api/v1/public-api"
KEY = os.environ["NTS_WEB_API_KEY"]
headers = {"Authorization": "Bearer " + KEY}
# Legacy creation has no idempotency guarantee. Never automatically retry POST.
payload = json.loads("{\"content\":\"1\\n00:00:01,000 --> 00:00:02,500\\nHello world\\n\\n2\\n00:00:03,000 --> 00:00:04,000\\nHow are you?\",\"target_lang\":\"Vietnamese\",\"model_id\":\"context-10\",\"source_lang\":\"English\",\"detect_mode\":\"none\",\"name\":\"My translation\"}")
# Replace sample model / voice IDs using the catalog first.
created = requests.post(BASE + "/translate/subtitle", headers=headers,
    json=payload, timeout=330, allow_redirects=False)
created.raise_for_status()
if created.status_code >= 300: raise RuntimeError("Unexpected redirect")
document = created.json()
if document.get("success") is False: raise RuntimeError(document)
job_id = document["data"]["project_id"]
Path("nts-job-id.txt").write_text(job_id, encoding="utf-8")
print("Saved job:", job_id)
deadline = time.monotonic() + 1800
while True:
    response = requests.get(BASE + "/translate/__JOB_ID__".replace("__JOB_ID__", quote(job_id, safe="")),
        headers={"Authorization": "Bearer " + KEY}, timeout=60, allow_redirects=False)
    response.raise_for_status()
    if response.status_code >= 300: raise RuntimeError("Unexpected redirect")
    document = response.json()
    if document.get("success") is False: raise RuntimeError(document)
    job = document["data"]
    print(job["status"])
    if job["status"] in ("completed", "done", "succeeded"): break
    if job["status"] in ("failed", "error", "cancelled", "stopped", "needs_review"):
        raise RuntimeError(job)  # Reconcile this job; do not submit a replacement automatically.
    if time.monotonic() >= deadline: raise TimeoutError("Keep job ID; poll again later")
    time.sleep(3)
# Translation results are returned inline. Preserve timing and translated lines.
Path("translation.json").write_text(json.dumps(job, ensure_ascii=False, indent=2), encoding="utf-8")
print("Saved translation.json; use lines[].translate_text with original timing to export SRT")

```

## Translation · javascript

```javascript
// Node.js 18+. Save as workflow.mjs; run: node workflow.mjs
import { writeFile } from 'node:fs/promises';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { randomUUID } from 'node:crypto';

const base = "https://api.ntsgenai.com/api/v1/public-api";
const key = process.env.NTS_WEB_API_KEY;
if (!key) throw new Error('Set the correct API key environment variable');
const headers = { Authorization: 'Bearer ' + key, 'Content-Type': 'application/json' };
const payload = {"content":"1\n00:00:01,000 --> 00:00:02,500\nHello world\n\n2\n00:00:03,000 --> 00:00:04,000\nHow are you?","target_lang":"Vietnamese","model_id":"context-10","source_lang":"English","detect_mode":"none","name":"My translation"}; // Replace sample model / voice IDs first.
async function jsonRequest(path, options = {}) {
  const response = await fetch(base + path, { ...options, redirect: 'error', signal: AbortSignal.timeout(330000) });
  if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + await response.text());
  const document = await response.json();
  if (document.success === false) throw new Error(JSON.stringify(document));
  return document;
}
// POST exactly once. On uncertain submission, reconcile; do not auto-create another job.
const created = await jsonRequest("/translate/subtitle", { method: 'POST', headers,
  body: JSON.stringify(payload) });
const jobId = created.data.project_id;
await writeFile('nts-job-id.txt', jobId);
console.log('Saved job:', jobId);
const deadline = Date.now() + 1800000;
let job;
while (true) {
  const document = await jsonRequest("/translate/__JOB_ID__".replace('__JOB_ID__', encodeURIComponent(jobId)),
    { headers: { Authorization: 'Bearer ' + key } });
  job = document.data;
  console.log(job.status);
  if (['completed', 'done', 'succeeded'].includes(job.status)) break;
  if (['failed', 'error', 'cancelled', 'stopped', 'needs_review'].includes(job.status)) throw new Error(JSON.stringify(job));
  if (Date.now() >= deadline) throw new Error('Keep job ID; poll again later');
  await new Promise(resolve => setTimeout(resolve, 3000));
}
await writeFile('translation.json', JSON.stringify(job, null, 2));
console.log('Saved translation.json; use lines[].translate_text and original timing to export SRT');

```
