Tonael

v1

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.

Base URL: 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%)
tonael
word-level alignment
$0.045 / min $0.028 / min
tonael_ultra
vocal 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.

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

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
400INVALID_AUDIO_FORMATFile is not a valid MP3
400AUDIO_TOO_LARGEFile exceeds 300 MB
400AUDIO_TOO_LONGAudio exceeds 60 minutes
400INVALID_LYRICSLyrics exceed 80,000 chars
422LYRICS_REQUIREDNo lyrics provided — this endpoint aligns the lyrics you provide with your audio
400UNSUPPORTED_LANGUAGELanguage code not supported
401UNAUTHORIZEDMissing or invalid API key
402INSUFFICIENT_CREDITNot 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)
403KEY_PAUSEDAPI key is paused by administrator
403KEY_REVOKEDAPI key has been revoked
403BATCH_REQUIRES_PAIDBatch priority requires a paid account — add credit with your first top-up to unlock it (POST /api/v1/credits/topup)
403STEMS_UNAVAILABLEVocal isolation output is not available on this request — use tonael_ultra for isolation + alignment
400INVALID_PRIORITYThe priority value is invalid — valid values are hot and batch
404JOB_NOT_FOUNDJob ID does not exist or belongs to another key
429CONCURRENCY_LIMITToo many jobs in flight for this key — retry once an in-flight job finishes
409LEGACY_TOPUP_UNSUPPORTEDThis 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)
409ACCOUNT_NOT_MIGRATEDThis account is not yet dollar-denominated — the /api/v1/credits endpoints become available after migration
410RESULT_EXPIREDResult deleted after its retention window (24 hours for hot jobs, 72 hours for batch)
503BATCH_UNAVAILABLEBatch processing is temporarily unavailable — resubmit with priority=hot to process in the real-time lane
500INTERNAL_ERRORUnexpected 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 (10dix, 5cinq) 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 duration60 minutes
Max file size300 MB
Max lyrics length80,000 characters
Result retention24 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.