# SpeechCore · audio/video transcription — neuraldeep.tech API

> One section of the machine-readable reference. All sections: https://neuraldeep.tech/llms.txt · everything in one file: https://neuraldeep.tech/llms-full.txt

Base URL: `https://speechcore.neuraldeep.tech/api`
Auth: `Authorization: Bearer $YOUR_KEY`

A separate service [speechcore.neuraldeep.tech](https://speechcore.neuraldeep.tech) — transcription of long audio and video (files up to 2 GB): speaker diarization, word-level timecodes, export to SRT/VTT/TSV/DOCX/PDF. On the web — sign in with the same Hub account; **via API — with the same `sk-*` key** as for `/v1/*`. For short audio and OpenAI compatibility there's also the synchronous `whisper-1` (the "Transcription" section above) — while SpeechCore is tailored for long recordings and diarization.

API base — `https://speechcore.neuraldeep.tech/api` (NOT `api.neuraldeep.tech`). The limit is counted in **transcriptions per day**; a key sees its remainder in `GET /usage` (`used_today`, `daily_limit`, `remaining`). Beyond the limit an upload still goes through, billed per minute, if the wallet has money. Otherwise you get `429` saying when it resets ("Resets at 00:00 UTC (03:00 Moscow time).") and a `Retry-After` header until midnight UTC. Works **asynchronously**: `POST /upload` → `{task_id}` → poll `GET /transcriptions/{id}/status` until `status=completed` → fetch the result: `GET /transcriptions/{id}` (JSON with segments, speakers, `detected_language`, `duration`) or `GET /transcriptions/{id}/markdown` (text with timecodes).

**Transcription options** — query parameters for `POST /upload` (the file goes in the multipart body, options in the URL):

- `diarize=true` — **speaker separation**: segments gain a `speaker` field (`SPEAKER_00`, `SPEAKER_01`…), and the summary shows each speaker's % of speaking time. `diarize_speakers_num=N` (1–20) — hint the exact number of voices if it's known (more accurate than auto-detection).
- `language=ru` — fix the language (auto-detection by default).
- **Special words / terminology:** `hotwords` — keywords separated by spaces or commas (≤500 chars) that the model will recognize more accurately: names, brands, acronyms, products (`NeuralDeep, Kimi, RAG, FZ-152`).
- `initial_prompt` — a context prompt (≤2000 chars): sets the topic/style and improves accuracy on specific vocabulary (`«A technical call about LLM infrastructure»`).

**`POST /upload` errors.** `task_id` comes only with a 200, so check the status first. An error body is `{"detail": "..."}` with the reason: **400** — unsupported file format (accepted: wav, flac, aiff, mp3, m4a, aac, ogg, oga, opus, wma, amr, mp4, m4v, mov, avi, mkv, webm, wmv, flv, 3gp, mpg, mpeg, ts), **401** — the key is wrong or revoked, **422** — a parameter out of range (e.g. `diarize_speakers_num` not in 1–20), **429** — the daily limit is used up and the wallet is empty, **503** — the service is temporarily unavailable, retry later.

```bash
# options in the URL query string, file in the multipart body (-F)
curl -s "https://speechcore.neuraldeep.tech/api/upload?diarize=true&diarize_speakers_num=3&language=ru&hotwords=NeuralDeep,Kimi,RAG&initial_prompt=A%20technical%20call%20about%20LLM" \
  -H "Authorization: Bearer $YOUR_KEY" \
  -F "file=@meeting.mp3"
```

```bash
# 1. upload (the same sk-key as for /v1/*)
tid=$(curl -s https://speechcore.neuraldeep.tech/api/upload \
  -H "Authorization: Bearer $YOUR_KEY" \
  -F "file=@meeting.mp3" | jq -r .task_id)

# 2. status
curl "https://speechcore.neuraldeep.tech/api/transcriptions/$tid/status" \
  -H "Authorization: Bearer $YOUR_KEY"

# 3. result as text (markdown with timecodes)
curl "https://speechcore.neuraldeep.tech/api/transcriptions/$tid/markdown" \
  -H "Authorization: Bearer $YOUR_KEY"

# your transcriptions as a list
curl "https://speechcore.neuraldeep.tech/api/transcriptions?limit=20" \
  -H "Authorization: Bearer $YOUR_KEY"
```

```python
import time, httpx

BASE = "https://speechcore.neuraldeep.tech/api"
H = {"Authorization": "Bearer $YOUR_KEY"}

# 1. upload a file (audio/video, up to 2 GB) + options in params
params = {
    "diarize": "true",            # speaker separation
    "diarize_speakers_num": 3,    # if the number of voices is known
    "language": "ru",
    "hotwords": "NeuralDeep, Kimi, RAG",          # special words/terms
    "initial_prompt": "A technical call about LLM",
}
with open("meeting.mp3", "rb") as f:
    r = httpx.post(f"{BASE}/upload", headers=H, params=params, files={"file": f}, timeout=300)
# task_id comes only with a 200; an error body is {"detail": "..."} with the reason
if r.status_code != 200:
    raise RuntimeError(f"upload {r.status_code}: {r.text}")
tid = r.json()["task_id"]

# 2. polling (every couple of seconds)
while True:
    st = httpx.get(f"{BASE}/transcriptions/{tid}/status", headers=H)
    st.raise_for_status()
    st = st.json()
    if st["status"] in ("completed", "failed"):
        break
    time.sleep(2)

# 3. result: segments + timecodes, or ready markdown
data = httpx.get(f"{BASE}/transcriptions/{tid}", headers=H).json()
print("language:", data["detected_language"], "duration:", data["duration"])
print(httpx.get(f"{BASE}/transcriptions/{tid}/markdown", headers=H).text)
```

**Web-interface features** ([speechcore.neuraldeep.tech](https://speechcore.neuraldeep.tech)), beyond the API:

- **Speaker diarization** — utterances are labeled by speaker (SPEAKER_00…); you can see who spoke how much (% of time).
- **Meeting summary** — a structured LLM recap (topics, decisions, tasks) accounting for speakers. With the same sk-key.
- **Chat over the transcript** — questions about the transcript ("what did we decide on X?", "what are the deadlines?") with timecode-referenced answers.
- **Export** — SRT · VTT · TSV · DOCX · PDF · TXT, plus markdown with timecodes.
- **Special words and context** — hotwords / context-prompt fields right in the upload form (the same as `hotwords`/`initial_prompt` in the API).
- **Public link** — share a transcript read-only without sign-in.
- **Realtime** — streaming transcription from the microphone/tab (via the "Meeting Recorder" extension).

> Polling and fetching the result don't consume the limit — it's spent only at upload (1 file = 1 transcription). Files up to 2 GB, the upload has to finish within 30 minutes; upload large files whole — processing runs in the background on GPU, so keep a reasonable timeout on polling. Diarization and special words work the same in the web and via the API.
