Karaoke Sync API
Submit an MP3 audio file with pre-segmented lyrics and receive word-level timestamps for karaoke synchronization. Results are delivered asynchronously and retained per tier: 24 hours for hot (real-time) jobs, 72 hours for batch jobs.
https://karaoke.cohesium.ai
Authentication
All API requests require an API key sent via the X-API-Key header.
X-API-Key: kk_your_api_key_here
API keys are provided by the platform administrator. Keys prefixed with kk_ are Tonael keys.
Free Tier & Sign-up
Start free — 10 free minutes, no card required. Add a card and your free allowance rises to 60 total free minutes. Free-tier keys run on hot (real-time) processing with a small concurrency cap; making your first top-up lifts those limits and unlocks batch processing.
POST /api/v1/signup
Create an account from an email address. The API key is returned exactly once. Your free minutes unlock after you verify your email.
curl -X POST https://karaoke.cohesium.ai/api/v1/signup \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
GET /api/v1/verify
Redeem the verification token from your sign-up email. Verifying your email releases the 10 free minutes.
curl "https://karaoke.cohesium.ai/api/v1/verify?token=your_verification_token"
Pricing & Metering
You are billed per second of audio, rounded up, with a 30-second minimum per job. Prices are in USD, exclusive of VAT/sales tax.
| Model | Hot (real-time) | Batch (48h SLA, −37.5%) |
|---|---|---|
tonaelword-level alignment |
$0.045 / min | $0.028 / min |
tonael_ultravocal isolation + alignment |
$0.095 / min | $0.059 / min |
A 3.5-minute song costs from $0.16 (tonael) or $0.33 (tonael_ultra) at hot rates. Batch pricing applies a minimum pool of 60 audio-minutes per submission window. Processing more than 10,000 minutes/month? Talk to us.
Failed jobs are never charged. If a job fails or is cancelled, the reserved amount is automatically re-credited to your balance.
Credits
Credit is prepaid. Buy a pack — $15, $50, or $200 — or top up a custom amount. Minimum top-up: $15. Jobs draw down your balance as they are metered.
GET /api/v1/credits/balance
Return the current balance in dollars.
curl https://karaoke.cohesium.ai/api/v1/credits/balance \
-H "X-API-Key: kk_your_api_key_here"
{ "balance_usd": "42.50" }
POST /api/v1/credits/topup
Charge your registered card for a credit pack (pack_15, pack_50, pack_200) or a custom amount_usd (minimum 15). Provide exactly one of the two.
curl -X POST https://karaoke.cohesium.ai/api/v1/credits/topup \
-H "X-API-Key: kk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"pack": "pack_50"}'
{ "status": "credited", "amount_usd": "$50.00", "balance_usd": "92.50" }
Refunds: unused credit is refundable to your card within 14 days of purchase — contact support. Consumed credit is final.
POST /api/v1/sync
Submit an alignment job. Returns immediately with a job ID. Results are delivered asynchronously.
Request
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
audio |
file | Yes | MP3 audio file (max 300 MB, max 60 minutes) |
lyrics |
string | Yes | Lyrics text (max 80,000 chars). Auto-segmented if no line breaks provided. |
language |
string | Yes | ISO 639-1 language code: fr, en, es, de, it, pt, ja, ko |
model |
string | No | tonael (default) or tonael_ultra. Tonael Ultra delivers better results than Tonael, at a higher cost. Try Tonael first; switch to Tonael Ultra if the result is not good enough. |
granularity |
string | No | char (default) or word. The default char returns word timestamps plus a nested chars array of per-letter timestamps on each word. Pass word for a lighter, word-only response. Same price either way — granularity does not change billing. |
priority |
string | No | hot (default, real-time) or batch (lower cost, delivered within a 48-hour SLA). See Batch processing. Batch requires a paid account. |
callback_url |
string | No | HTTPS URL notified when the job completes. Deliveries are HMAC-SHA256 signed — see Webhooks. If omitted, poll the status endpoint instead. |
Example
curl -X POST https://karaoke.cohesium.ai/api/v1/sync \
-H "X-API-Key: kk_your_api_key_here" \
-F "audio=@song.mp3" \
-F "lyrics=Joyeux anniversaire
Cher Antoine
On te souhaite
Plein de bonheur" \
-F "language=fr"
Response 202 Accepted
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing",
"created_at": "2026-04-04T10:30:00Z"
}
GET /api/v1/sync/{job_id}
Retrieve the status and result of an alignment job. Results are available for 24 hours after completion for hot jobs, and 72 hours for batch jobs. After that window the endpoint returns 410 Gone.
Query Parameters
| Param | Default | Description |
|---|---|---|
format |
json |
Output format. Options: json, lrc, srt, vtt, ass, ttml, sbv, txt |
Export Examples
# Get as Enhanced LRC (karaoke)
curl "https://karaoke.cohesium.ai/api/v1/sync/{job_id}?format=lrc" -H "X-API-Key: ..."
# Get as SRT (subtitles)
curl "https://karaoke.cohesium.ai/api/v1/sync/{job_id}?format=srt" -H "X-API-Key: ..."
# Get as WebVTT (web video)
curl "https://karaoke.cohesium.ai/api/v1/sync/{job_id}?format=vtt" -H "X-API-Key: ..."
# Get as ASS (video karaoke with word highlighting)
curl "https://karaoke.cohesium.ai/api/v1/sync/{job_id}?format=ass" -H "X-API-Key: ..."
Example
curl https://karaoke.cohesium.ai/api/v1/sync/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "X-API-Key: kk_your_api_key_here"
Response 200 OK (completed)
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"language": "fr",
"model": "tonael",
"product_type": "sync_tonael",
"created_at": "2026-04-04T10:30:00Z",
"completed_at": "2026-04-04T10:30:12Z",
"expires_at": "2026-04-05T10:30:12Z",
"result": {
"processing": { "processing_time_ms": 3437 },
"result": {
"segments": [
{
"index": 0,
"text": "Joyeux anniversaire",
"start": 5.240,
"end": 7.810,
"words": [
{ "word": "Joyeux", "start": 5.240, "end": 5.880, "confidence": 0.94 },
{ "word": "anniversaire", "start": 5.920, "end": 7.810, "confidence": 0.89 }
]
}
],
"statistics": {
"total_segments": 4,
"total_words": 12,
"avg_confidence": 0.87,
"low_confidence_words": 1,
"unaligned_words": 0,
"duration_s": 30.5
}
}
}
}
Per-letter timestamps (default)
By default (no granularity param), each word carries both its own timestamps and a nested chars array of per-letter timestamps. Pass granularity=word for a lighter, word-only response with no chars array. Word-level fields are identical either way, and the price is the same.
{
"word": "café",
"start": 5.240,
"end": 5.880,
"confidence": 0.94,
"chars": [
{ "char": "c", "start": 5.240, "end": 5.360, "confidence": 0.95 },
{ "char": "a", "start": 5.360, "end": 5.520, "confidence": 0.93 },
{ "char": "f", "start": 5.520, "end": 5.700, "confidence": 0.92 },
{ "char": "é", "start": 5.700, "end": 5.880, "confidence": 0.90 }
]
}
Notes: character timestamps are approximately 20 ms in resolution. Some characters — punctuation, and certain diacritics or accents — may not receive an independent timestamp, so a word's chars array can be shorter than its letter count. Characters are always returned in reading order with non-decreasing start times within a word.
Response 202 Accepted (still processing)
{
"job_id": "a1b2c3d4-...",
"status": "processing",
"created_at": "2026-04-04T10:30:00Z"
}
Response 410 Gone (expired)
{
"error": {
"code": "RESULT_EXPIRED",
"message": "Result has expired and is no longer available",
"details": "Results are retained for 24 hours (72 hours for batch jobs)"
}
}
DELETE /api/v1/sync/{job_id}
Cancel a job that is still in the queue. Only jobs with status queued can be cancelled. Jobs that are already processing, completed, or failed cannot be cancelled.
Example
curl -X DELETE https://karaoke.cohesium.ai/api/v1/sync/{job_id} \
-H "X-API-Key: kk_your_api_key_here"
Response 200 OK
{ "job_id": "...", "status": "cancelled" }
Response 409 Conflict (not cancellable)
{ "error": { "code": "NOT_CANCELLABLE", "message": "Job is 'processing' — only 'queued' jobs can be cancelled" } }
Batch Processing
Not in a hurry? Submit with priority=batch for a
37.5% lower rate ($0.028/min tonael, $0.059/min tonael_ultra), delivered within a
48-hour SLA. Batch is available on paid accounts; make your first top-up to unlock it.
- Same endpoint (
POST /api/v1/sync), same request shape — just addpriority=batch. - The cost is held from your balance at submission (batch rate), exactly as for hot jobs — insufficient balance is rejected synchronously with
INSUFFICIENT_CREDIT. - Batch results are retained 72 hours after completion (hot: 24 hours).
- Provide a
callback_urlto be notified on completion, or poll the status endpoint.
curl -X POST https://karaoke.cohesium.ai/api/v1/sync \
-H "X-API-Key: kk_your_api_key_here" \
-F "audio=@song.mp3" \
-F "lyrics=..." \
-F "language=fr" \
-F "priority=batch" \
-F "callback_url=https://your-app.example.com/hooks/tonael"
Webhooks
If you pass a callback_url, we POST a JSON body to it when the job reaches a
terminal state. Delivery is asynchronous and applies to both hot and batch jobs with a single, identical signing scheme.
Delivery guarantees
- At-least-once delivery. A webhook may be delivered more than once. Deduplicate on
job_id— treat a repeat of ajob_idyou have already processed as a no-op. - Failed deliveries are retried with exponential backoff. Respond with a
2xxto acknowledge.
Verifying the signature
Every delivery carries an X-Webhook-Signature header. It is an
HMAC-SHA256 hex digest computed with your webhook signing secret
over the canonical JSON body — the payload serialized with
keys sorted alphabetically, UTF-8 encoded. Recompute it on your side and compare
(use a constant-time comparison). Reject the delivery if it does not match.
import hmac, hashlib, json
def verify(body_dict, signing_secret, received_signature):
canonical = json.dumps(body_dict, ensure_ascii=False, sort_keys=True)
expected = hmac.new(
signing_secret.encode("utf-8"),
canonical.encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, received_signature)
Payload (completed)
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"result_url": "https://karaoke.cohesium.ai/api/v1/jobs/a1b2c3d4-.../result",
"expires_at": "2026-04-05T10:30:12Z",
"result": { "...": "inline alignment result" }
}
GET /api/v1/health
Health check endpoint. No authentication required. Returns the status of all system components.
Response 200 OK (all systems operational)
{
"status": "healthy",
"checks": {
"api": "ok",
"database": "ok",
"processing": "ok"
}
}
Response 503 Service Unavailable (degraded)
{
"status": "degraded",
"checks": {
"api": "ok",
"database": "ok",
"processing": "down"
}
}
Use this endpoint for uptime monitoring. Status healthy = all systems go. degraded = partial outage. unhealthy = critical failure.
Error Codes
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_AUDIO_FORMAT | File is not a valid MP3 |
| 400 | AUDIO_TOO_LARGE | File exceeds 300 MB |
| 400 | AUDIO_TOO_LONG | Audio exceeds 60 minutes |
| 400 | INVALID_LYRICS | Lyrics exceed 80,000 chars |
| 422 | LYRICS_REQUIRED | No lyrics provided — this endpoint aligns the lyrics you provide with your audio |
| 400 | UNSUPPORTED_LANGUAGE | Language code not supported |
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 402 | INSUFFICIENT_CREDIT | Not enough credit for this job — the message shows available, required, and the exact shortfall in dollars. Top up via POST /api/v1/credits/topup (minimum $15) |
| 403 | KEY_PAUSED | API key is paused by administrator |
| 403 | KEY_REVOKED | API key has been revoked |
| 403 | BATCH_REQUIRES_PAID | Batch priority requires a paid account — add credit with your first top-up to unlock it (POST /api/v1/credits/topup) |
| 403 | STEMS_UNAVAILABLE | Vocal isolation output is not available on this request — use tonael_ultra for isolation + alignment |
| 400 | INVALID_PRIORITY | The priority value is invalid — valid values are hot and batch |
| 404 | JOB_NOT_FOUND | Job ID does not exist or belongs to another key |
| 429 | CONCURRENCY_LIMIT | Too many jobs in flight for this key — retry once an in-flight job finishes |
| 409 | LEGACY_TOPUP_UNSUPPORTED | This account is dollar-denominated — use POST /api/v1/credits/topup with a pack (pack_15, pack_50, pack_200) or a custom amount_usd (minimum $15) |
| 409 | ACCOUNT_NOT_MIGRATED | This account is not yet dollar-denominated — the /api/v1/credits endpoints become available after migration |
| 410 | RESULT_EXPIRED | Result deleted after its retention window (24 hours for hot jobs, 72 hours for batch) |
| 503 | BATCH_UNAVAILABLE | Batch processing is temporarily unavailable — resubmit with priority=hot to process in the real-time lane |
| 500 | INTERNAL_ERROR | Unexpected server error |
All errors follow the format: {"error": {"code": "...", "message": "..."}}
Billing & Refunds
Failed jobs are never charged. When a job fails or is cancelled, the credit reserved for it is automatically returned to your balance — no action needed on your side. The recredit appears in GET /api/v1/billing/usage as a positive entry labeled “Job failed or cancelled (credit returned to balance)”.
Every job is billed individually. When you submit multiple jobs, each one is charged on its own: a failed job is recredited on its own, and never affects what you pay for the jobs that completed.
Cash refunds. Unused credit is refundable to your card within 14 days of purchase — contact support to request one. Credit that has already been used is final. Refunds issued to your card appear in your usage history as “Cash refund to card (unused credit)”.
Credit validity (business accounts). For accounts registered with a business VAT ID, purchased credits are valid for 12 months from the date of purchase; a reminder email is sent one month before any credit is due to expire. Accounts without a business VAT ID are not subject to any credit expiry. Contact support with your VAT ID to register a business account.
Lyrics Guidelines
What you should do
Send the lyrics as-is: Just paste the raw lyrics text. The API handles everything else — formatting, cleaning, and segmentation are fully automatic.
Tip: For best results, include only what is actually sung or spoken. But even if you include metadata or headers, the API will do its best.
What the API handles automatically
AUTO Line segmentation: Long lines are automatically split at natural points (punctuation, pauses) for optimal karaoke display. You can send a single block of text — the API will segment it intelligently.
AUTO
Bracket tags: Section markers like [Verse 1], [Chorus], [SFX], [Female] are automatically stripped.
AUTO
Numbers: Digits are converted to words (10 → dix, 5 → cinq) based on the language parameter.
AUTO
Accented characters: Characters like é, à, ç, ö, ü are normalized internally. Original text is preserved in the output.
AUTO
Punctuation: Standalone punctuation (!, ?, :) is handled gracefully.
Limits
| Max audio duration | 60 minutes |
| Max file size | 300 MB |
| Max lyrics length | 80,000 characters |
| Result retention | 24 hours (hot) · 72 hours (batch) |
| Processing time | ~10-15 seconds (typical 3-4 min song) |
Karaoke video rendering is not available for new API keys.