Trace

Results & Billing

Poll any Trace job until it completes, read its scores, and fetch results too large to return inline. This page also covers Trace errors, what is billed, budget caps, safe retries and rate limits.

Job status

GET/trace/v3/jobs/{job_id}status and results of any Trace job

Works for every Trace job: custom search, comprehensive search, use case and ingestion. Polling is free.

200 OK { "success": true, "data": { "job_id": "018f0000-0000-7000-8000-0000000000a2", "status": "completed", "error": null, "results": { ... } } }
FieldTypeRequiredDefaultDescription
data.statusstringNo—pending (queued), processing, completed or failed.
data.errorstring | nullNo—Why the job failed, when status is failed. Failed jobs are not charged.
data.resultsobjectNo—The scores. Present once the job has completed.
data.results_completebooleanNo—Present and false when the result was too large to include. See Large results.
data.results_urlstringNo—Present with results_complete: the path to fetch the full result from.

A job that does not exist, or that belongs to another account, returns 404 with "job not found". The two cases are deliberately indistinguishable.

Polling

Most searches finish within seconds to a few minutes. A 1:1 search over many layers takes longest, because both songs are analysed on the spot. Poll every 5 to 15 seconds.

import time import requests API = "https://apiv2.soundverse.ai" HEADERS = {"Authorization": "Bearer sksoundverse_..."} def wait_for_job(job_id: str, interval: float = 10.0) -> dict: while True: data = requests.get(f"{API}/trace/v3/jobs/{job_id}", headers=HEADERS).json()["data"] if data["status"] == "completed": if data.get("results_complete") is False: return requests.get(API + data["results_url"], headers=HEADERS).json() return data["results"] if data["status"] == "failed": raise RuntimeError(data["error"]) time.sleep(interval)

Reading the results

Every layer reports three scores from 0 (no similarity) to 1 (identical): hungarian_mean (the headline score), max_then_mean and max. See Reading scores. percentage is the ranking score multiplied by 100.

Results can include more fields than shown here, such as per-window detail (score_matrix, matched_windows). Read the fields you need and ignore the rest; fields may be added over time.

1:1 result

{ "status": "success", "search_type": "1:1", "depth": "track", "full_audio_latent_scores": { "hungarian_mean": 0.576373, "max_then_mean": 0.636297, "max": 0.725281, "matched_windows": [ { "query_index": 3, "candidate_index": 5, "similarity_score": 0.725281, "query_audio_timestamps": [24.0, 32.0], "candidate_audio_timestamps": [40.0, 48.0] } ], "score_matrix": [[0.51, 0.48, ...], ...] } }

matched_windows shows which moments of the two songs line up, as start and end times in seconds.

1:N result

matches lists every song in the dataset, most similar first:

{ "status": "success", "search_type": "1:n", "depth": "track", "candidate_dataset_id": "018f0000-0000-7000-8000-0000000000d1", "matches": [ { "id": "d79efad889c32e5537210f024e073632ec65fb9b5feb8576e91cad7c2bdc0466.mp3", "score": 1.0, "percentage": 100.0, "full_audio_latent_scores": { "hungarian_mean": 1.0, "max_then_mean": 1.0, "max": 1.0 } }, { "id": "b3ea82b7b97c8e2f733cd7850d12db41f4f334aa59c88cdd09538c3554d28326.mp3", "score": 0.636743, "percentage": 63.67, "full_audio_latent_scores": { "hungarian_mean": 0.636743, "max_then_mean": 0.69, "max": 0.81 } } ] }

Mapping a match to your file

Each match’s id is Trace’s audio ID. Find it in the dataset’s tracks list: the track with that audio_id carries your file_id as source_ref.

M:N result

results holds one entry per query song, each with its own matches:

{ "search_type": "m:n", "depth": "track", "query_dataset_id": "018f0000-0000-7000-8000-0000000000d2", "candidate_dataset_id": "018f0000-0000-7000-8000-0000000000d1", "results": [ { "query_audio_id": "e4a4d5f567804d885eb41b56ad039079049f9f11a10ae743f397a42504ad6bea.mp3", "matches": [ { "id": "b3ea82b7b97c8e2f733cd7850d12db41f4f334aa59c88cdd09538c3554d28326.mp3", "score": 0.71, "percentage": 71.0 }, { "id": "d79efad889c32e5537210f024e073632ec65fb9b5feb8576e91cad7c2bdc0466.mp3", "score": 0.62, "percentage": 62.0 } ] } ] }

Multiple layers

When a search covers several layers, each layer adds its own block next to the others:

FieldTypeRequiredDefaultDescription
tracklayerNo—full_audio_latent_scores
light_stemlayerNo—vocal_latent_scores and instrumental_latent_scores
stemlayerNo—Per-instrument scores. A 1:n search with only stem groups its matches by instrument in matches_by_stem.
sectionlayerNo—section_breakdown
motiflayerNo—motif_matches
lyrics / translationlayerNo—lyrics_similarity and translation_similarity
insightslayerNo—tag_overlap

Matches are ranked by the first available layer in this order: track, light_stem, stem, section, stem_section, motif.

Large results

GET/trace/v3/jobs/{job_id}/resultsthe complete result

When a result is larger than about 200 KB, which is common for m:n searches over several layers, the job response does not include it:

{ "success": true, "data": { "job_id": "018f0000-0000-7000-8000-0000000000a4", "status": "completed", "error": null, "results": {}, "results_complete": false, "results_url": "/trace/v3/jobs/018f0000-0000-7000-8000-0000000000a4/results" } }

Fetch results_url. It returns the results object directly, without the success/data envelope, in the same shape as data.results above. It also works for small results.

Download large results within 24 hours

The stored copy of a large result is available for 24 hours after the job completes. After that the endpoint returns 404 and you need to run the search again.

FieldTypeRequiredDefaultDescription
job not found404No—The job does not exist or belongs to another account.
this job has no results yet404No—The job is still running, or it failed. Check its status.
results unavailable -- the link has expired; …404No—More than 24 hours have passed since a large result was produced. Run the search again.

Errors

Trace uses the same error responses as the rest of the API — see Errors. Validation errors name the field in message and are listed on Search and Datasets & Ingest. Trace adds a few of its own:

FieldTypeRequiredDefaultDescription
404ResourceNotFoundNo—A job or dataset does not exist or belongs to another account. Trace never answers 403, so it never confirms another account's IDs.
409detailNo—A dataset name is already taken, or an ingestion is still running on a dataset you are deleting. Dataset endpoints return the Trace service's own detail message.
400legacy uploadNo—POST /trace/v3/upload received no file or an unsupported format.
502service unreachableNo—The Trace service was briefly unreachable on a dataset, library or upload call. Retry with backoff.

Billing

Trace bills your balance per job, based on usage.

FieldTypeRequiredDefaultDescription
Billedper jobNo—Ingestion, custom search, comprehensive search and every use case.
Freeno chargeNo—Uploads, creating, listing, viewing and deleting datasets, the library, job status and full results.
  • What a job costs depends on the songs’ length, the layers involved and, for searches, the number of comparisons.
  • When you are charged: a hold may be placed when a job is queued, and the final amount settles after the job completes, usually within a minute or two.
  • Failed jobs are not charged, and any hold is returned in full. Requests rejected with a 4xx never create a job.

Check your balance with GET /v1/account/balance.

Budget cap

Add budget_tokens to any ingest or search request to cap what the job may cost. Trace prices the job before doing any work. If the price is more than 10% above your cap, the job is refused and finishes as failed, and you are not charged. The error states the job’s price:

This job prices at 77 tokens but was reserved for only 1. Re-submit so the correct amount is held; running now would spend more than was checked against the balance.

Re-submit with a budget_tokens at least as high as the quoted price, or leave it out to run without a cap.

Safe retries

Send an Idempotency-Key header on every request that creates a Trace job. Repeating a request with the same key within 24 hours returns the same job instead of creating, and charging for, a second one. The key rules are on Idempotency-Key.

Idempotency-Key: catalog-check-2026-10-22-0001

Rate limits

Job-creating requests are rate-limited per account over hourly and daily windows. Each kind of job (ingestion, custom search, comprehensive search, use case) has its own limit, and all Trace jobs also share an overall limit; the eight use cases count together as one kind. Limits depend on your account. Status checks, results, dataset management and uploads do not count towards them.

Exceeding a limit returns 429 RateLimited; wait, then retry with backoff and the same Idempotency-Key. See Rate limits.