API reference

Sound grouping

4 operations. Base URL https://api.scarleta.ai. Select an operation to see its parameters, samples and errors.

Jump to an operation
POST/v1/audio/library/cluster

Group your files by how they sound

Paid accounts only
  • API key required
  • Paid accounts only
  • No token cost

Starts a job that sorts your files into groups that sound alike. The answer is 202 at once with the job in state queued; read the job (or the latest completed one) until its status is completed, then use clusters. n_clusters is a whole number from 2 to 20 (default 8); filters narrows which files are grouped (same filters as the library query; default all files); embedding_model picks the fingerprint kind, by default the one most of the chosen files have. Only files with a sound fingerprint are grouped. Every check runs before a job is created, so a refused request leaves nothing behind: fewer fingerprinted files than groups is 422 not_enough_files, more than 5000 files is 422 cluster_too_large (add filters), and a second start while one job is queued or running is 409 cluster_in_progress (its error.details.job_id names the running job). Sound grouping is for paid accounts: a free key gets 403 cluster_paid_only. A job cannot be deleted; completed jobs stay readable. If grouping cannot start right now the route answers 503 cluster_unavailable; retry later.

Cost. Free of charge to start. Files need an embedding analysis first, which is billed when you run it.

curl -X POST "https://api.scarleta.ai/v1/audio/library/cluster" \
  -H "Authorization: Bearer $SCARLETA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "n_clusters": 2,
    "filters": {
      "name_contains": "demo"
    }
  }'

Request body

NameTypeRequiredDescription
n_clustersbodyintegerOptionalHow many groups to make, 2 to 20. Defaults to 8.
filtersbodyobjectOptionalWhich files to keep. Every field you send narrows the result; an empty object keeps every file. An unknown field is refused with 400 `invalid_filter`.
filters.bpmbodyobject | objectOptionalTempo range `{min, max}`, or `{near, tolerance}` (tolerance defaults to 3).
filters.bpm.minbodynumberOptional
filters.bpm.maxbodynumberOptional
filters.bpm.nearbodynumberOptional
filters.bpm.tolerancebodynumberOptional
filters.tempo_stabilitybodyobjectOptional
filters.tempo_stability.minbodynumberOptional
filters.tempo_stability.maxbodynumberOptional
filters.loudness_integratedbodyobjectOptional
filters.loudness_integrated.minbodynumberOptional
filters.loudness_integrated.maxbodynumberOptional
filters.loudness_rangebodyobjectOptional
filters.loudness_range.minbodynumberOptional
filters.loudness_range.maxbodynumberOptional
filters.true_peak_dbfsbodyobjectOptional
filters.true_peak_dbfs.minbodynumberOptional
filters.true_peak_dbfs.maxbodynumberOptional
filters.pitch_lowest_midibodyobjectOptional
filters.pitch_lowest_midi.minbodynumberOptional
filters.pitch_lowest_midi.maxbodynumberOptional
filters.pitch_highest_midibodyobjectOptional
filters.pitch_highest_midi.minbodynumberOptional
filters.pitch_highest_midi.maxbodynumberOptional
filters.notes_per_secondbodyobjectOptional
filters.notes_per_second.minbodynumberOptional
filters.notes_per_second.maxbodynumberOptional
filters.duration_secondsbodyobjectOptional
filters.duration_seconds.minbodynumberOptional
filters.duration_seconds.maxbodynumberOptional
filters.key_labelbodystring | array<string>OptionalOne key name or a list, for example `A minor`.
filters.key_rootbodystring | array<string>Optional
filters.key_modebody"major" | "minor"Optional
filters.meterbodystring | array<string>OptionalOne time signature or a list, for example `4/4`.
filters.speech_languagebodystring | array<string>Optional
filters.formatbodyarray<string>OptionalFile formats to keep, for example `["wav", "mp3"]`.
filters.transcript_containsbodystringOptionalKeep files whose transcript contains this text.
filters.sound_descriptionbodystringOptional
filters.name_containsbodystringOptionalKeep files whose name contains this text.
filters.user_tagsbodyobjectOptionalTag rules: keep files with any, with all, or with none of the listed tags.
filters.user_tags.anybodyarray<string>Optional
filters.user_tags.allbodyarray<string>Optional
filters.user_tags.nonebodyarray<string>Optional
filters.auto_tagsbodyarray<string>Optional
filters.analyzedbodyarray<string>OptionalKeep files that have results for every listed analysis category.
filters.analyzed_anybodyarray<string>OptionalKeep files that have results for at least one listed category.
filters.not_analyzedbodybooleanOptionalTrue keeps only files with no analysis results.
filters.missingbodyarray<string>OptionalKeep files that lack a value, for example `["bpm"]`.
filters.uploaded_afterbodynumberOptionalMilliseconds since the Unix epoch.
filters.uploaded_beforebodynumberOptionalMilliseconds since the Unix epoch.
filters.starredbodybooleanOptional
filters.archivedbody"hide" | "include" | "only"OptionalArchived files: hidden (default), included, or the only ones shown.
filters.include_derivedbodybooleanOptionalTrue also returns files made from other files, such as stems.
filters.sounds_likebodyobjectOptionalKeep files that sound like the given file.
filters.sounds_like.audio_idbodystringRequired
filters.sounds_like.min_scorebodynumberOptional
filters.sound_groupbodyobjectOptionalKeep the files of one sound group from a grouping job.
filters.sound_group.job_idbodystringRequired
filters.sound_group.group_idbodyintegerRequiredAllowed: -9007199254740991 to 9007199254740991.
filters.trashbody"hide" | "include" | "only"OptionalTrashed files: hidden (default), included, or the only ones shown.
filters.duplicatesbodybooleanOptionalTrue keeps only files that have an identical copy.
filters.artistbodyarray<string>Optional
filters.projectbodyarray<string>Optional
filters.albumbodyarray<string>Optional
filters.title_containsbodystringOptional
filters.credits_containsbodystringOptional
embedding_modelbodystringOptionalName of the fingerprint kind to compare, as returned in `embedding_model`. Defaults to the kind most chosen files have. Allowed: 1 to 200 characters.
Response fields
FieldTypeDescription
job_idstringIdentifier of the new job.
status"queued"Always `queued` at this point.
file_countintegerFiles the job will group. Allowed: -9007199254740991 to 9007199254740991.
embedding_modelstringName of the fingerprint kind that will be compared.
Errors
StatusCodeDescription
400invalid_n_clustersThe body is not valid JSON (`invalid_json`), has an unknown field (`invalid_config`), has a bad `n_clusters` (`invalid_n_clusters`), `embedding_model` (`invalid_embedding_model`) or filter (`invalid_filter`).
401invalid_keyMissing, malformed, unknown or revoked API key.
403cluster_paid_onlyThe key belongs to a free account.
409cluster_in_progressA job of yours is already queued or running.
422not_enough_filesToo few files with a fingerprint (`not_enough_files`), or more than the limit (`cluster_too_large`).
429rate_limit_exceededOver the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
503cluster_unavailableGrouping cannot start right now (`cluster_unavailable`), or the database is briefly unavailable (`db_unavailable`); retry shortly. A key that cannot be checked is `auth_unavailable`.
Example responses
{
  "data": {
    "job_id": "7d2c9e1a-4b3f-4c68-9a5d-1e0f8b7c6a52",
    "status": "queued",
    "file_count": 120,
    "embedding_model": "fingerprint-6144"
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
  • 202 The job is queued.
  • 400 The body is not valid JSON (`invalid_json`), has an unknown field (`invalid_config`), has a bad `n_clusters` (`invalid_n_clusters`), `embedding_model` (`invalid_embedding_model`) or filter (`invalid_filter`).
  • 401 Missing, malformed, unknown or revoked API key.
  • 403 The key belongs to a free account.
  • 409 A job of yours is already queued or running.
  • 422 Too few files with a fingerprint (`not_enough_files`), or more than the limit (`cluster_too_large`).
  • 429 Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
  • 503 Grouping cannot start right now (`cluster_unavailable`), or the database is briefly unavailable (`db_unavailable`); retry shortly. A key that cannot be checked is `auth_unavailable`.

Try it

Sends this request from your browser to https://api.scarleta.ai with your own key. The key stays in this page only (never saved or put in the address) and is cleared when you leave. Revoke any key you have shared.

Other error codes this operation can return: auth_unavailable, cluster_too_large, db_unavailable, invalid_auth_format, invalid_config, invalid_embedding_model, invalid_filter, invalid_json, invalid_key_format, missing_api_key, missing_auth, rate_limit_unavailable, unauthorized.

GET/v1/audio/library/cluster/{job_id}

Get a sound grouping job

Free and paid accounts
  • API key required
  • Free and paid accounts
  • No token cost

Returns one grouping job. clusters is empty until status is completed. Files you deleted or trashed since the job ran are left out of every group. A job that stopped making progress is marked failed when you read it, with an error message. A job that is not yours, does not exist or whose id is not a UUID is 404 cluster_job_not_found.

Cost. Free of charge.

JOB_ID="YOUR_JOB_ID"
curl "https://api.scarleta.ai/v1/audio/library/cluster/$JOB_ID" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"

Parameters

NameTypeRequiredDescription
job_idpathstringRequiredThe `job_id` returned when the grouping was started.
Response fields
FieldTypeDescription
job_idstringIdentifier of the grouping job.
status"queued" | "running" | "completed" | "failed"`queued` and `running` while the job works, then `completed` or `failed`.
n_clustersintegerHow many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991.
embedding_modelstringName of the fingerprint kind the job compared.
file_countintegerFiles the job covers. Allowed: -9007199254740991 to 9007199254740991.
filtersobjectThe filters the job was started with.
iterationsinteger | nullRounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991.
errorstring | nullWhy a failed job failed, or null.
created_atnumberWhen the job was created, in milliseconds since the Unix epoch.
updated_atnumberWhen the job last changed, in milliseconds since the Unix epoch.
clustersarray<object>The groups. Empty until the job is `completed`.
clusters[].cluster_idintegerNumber of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991.
clusters[].labelstring | nullName you gave the group, or null.
clusters[].file_countintegerFiles in the group that still exist. Allowed: -9007199254740991 to 9007199254740991.
clusters[].membersarray<string>`audio_id` of every file in the group, closest to the centre first.
clusters[].representative_filesarray<object>The files nearest the centre of the group, to show as a preview.
clusters[].representative_files[].audio_idstring
clusters[].representative_files[].namestringDisplay name of the file.
clusters[].centroid_audio_idstring | nullThe file nearest the centre, or null when the group is empty.
Errors
StatusCodeDescription
401invalid_keyMissing, malformed, unknown or revoked API key.
404cluster_job_not_foundNo grouping job with this id exists in your account (an id that is not a UUID is the same 404).
429rate_limit_exceededOver the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
503auth_unavailableTemporary platform condition; retry shortly.
Example responses
{
  "data": {
    "job_id": "7d2c9e1a-4b3f-4c68-9a5d-1e0f8b7c6a52",
    "status": "completed",
    "n_clusters": 2,
    "embedding_model": "fingerprint-6144",
    "file_count": 2,
    "filters": {},
    "iterations": 3,
    "error": null,
    "created_at": 1791333870854,
    "updated_at": 1791333874854,
    "clusters": [
      {
        "cluster_id": 0,
        "label": "Drums",
        "file_count": 1,
        "members": [
          "audio_3f9c1a7d2b60"
        ],
        "representative_files": [
          {
            "audio_id": "audio_3f9c1a7d2b60",
            "name": "demo-mix.wav"
          }
        ],
        "centroid_audio_id": "audio_3f9c1a7d2b60"
      },
      {
        "cluster_id": 1,
        "label": null,
        "file_count": 1,
        "members": [
          "audio_a81e5c40d7f2"
        ],
        "representative_files": [
          {
            "audio_id": "audio_a81e5c40d7f2",
            "name": "demo-mix-2.wav"
          }
        ],
        "centroid_audio_id": "audio_a81e5c40d7f2"
      }
    ]
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
  • 200 The job and its groups.
  • 401 Missing, malformed, unknown or revoked API key.
  • 404 No grouping job with this id exists in your account (an id that is not a UUID is the same 404).
  • 429 Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
  • 503 Temporary platform condition; retry shortly.

Try it

Sends this request from your browser to https://api.scarleta.ai with your own key. The key stays in this page only (never saved or put in the address) and is cleared when you leave. Revoke any key you have shared.

Other error codes this operation can return: db_unavailable, invalid_auth_format, invalid_key_format, missing_api_key, missing_auth, rate_limit_unavailable, unauthorized.

PUT/v1/audio/library/cluster/{job_id}/groups/{group_id}

Name one group of a sound grouping

Free and paid accounts
  • API key required
  • Free and paid accounts
  • No token cost

Sets the label of one group (group_id is the group's cluster_id) of a completed job. The label is 1 to 60 characters after trimming, with no control characters. The answer is the whole job. The body and label are checked first (invalid_json, invalid_config for any field other than label, invalid_label), then the job (a job that is not yours, does not exist or whose id is not a uuid is 404 cluster_job_not_found.), then the group id and the job state, in this order: a group_id that is not a whole number is 404 cluster_group_not_found, a job that has not completed is 409 cluster_job_not_ready, and a number the job has no group for is 404 cluster_group_not_found.

Cost. Free of charge.

JOB_ID="YOUR_JOB_ID"
GROUP_ID="YOUR_GROUP_ID"
curl -X PUT "https://api.scarleta.ai/v1/audio/library/cluster/$JOB_ID/groups/$GROUP_ID" \
  -H "Authorization: Bearer $SCARLETA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Drums"
  }'

Parameters

NameTypeRequiredDescription
job_idpathstringRequiredThe `job_id` returned when the grouping was started.
group_idpathstringRequiredThe group's `cluster_id`, a whole number.

Request body

NameTypeRequiredDescription
labelbodystringRequiredThe new name, 1 to 60 characters; spaces around it are removed first, so the schema limit applies to the trimmed text.
Response fields
FieldTypeDescription
job_idstringIdentifier of the grouping job.
status"queued" | "running" | "completed" | "failed"`queued` and `running` while the job works, then `completed` or `failed`.
n_clustersintegerHow many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991.
embedding_modelstringName of the fingerprint kind the job compared.
file_countintegerFiles the job covers. Allowed: -9007199254740991 to 9007199254740991.
filtersobjectThe filters the job was started with.
iterationsinteger | nullRounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991.
errorstring | nullWhy a failed job failed, or null.
created_atnumberWhen the job was created, in milliseconds since the Unix epoch.
updated_atnumberWhen the job last changed, in milliseconds since the Unix epoch.
clustersarray<object>The groups. Empty until the job is `completed`.
clusters[].cluster_idintegerNumber of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991.
clusters[].labelstring | nullName you gave the group, or null.
clusters[].file_countintegerFiles in the group that still exist. Allowed: -9007199254740991 to 9007199254740991.
clusters[].membersarray<string>`audio_id` of every file in the group, closest to the centre first.
clusters[].representative_filesarray<object>The files nearest the centre of the group, to show as a preview.
clusters[].representative_files[].audio_idstring
clusters[].representative_files[].namestringDisplay name of the file.
clusters[].centroid_audio_idstring | nullThe file nearest the centre, or null when the group is empty.
Errors
StatusCodeDescription
400invalid_labelThe body is not valid JSON (`invalid_json`), has a field other than `label` (`invalid_config`), or the label is empty, too long or has control characters (`invalid_label`).
401invalid_keyMissing, malformed, unknown or revoked API key.
404cluster_group_not_foundThe job does not exist (`cluster_job_not_found`) or has no such group (`cluster_group_not_found`).
409cluster_job_not_readyThe job is still queued or running, or failed.
429rate_limit_exceededOver the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
503auth_unavailableTemporary platform condition; retry shortly.
Example responses
{
  "data": {
    "job_id": "7d2c9e1a-4b3f-4c68-9a5d-1e0f8b7c6a52",
    "status": "completed",
    "n_clusters": 2,
    "embedding_model": "fingerprint-6144",
    "file_count": 2,
    "filters": {},
    "iterations": 3,
    "error": null,
    "created_at": 1791333870854,
    "updated_at": 1791333874854,
    "clusters": [
      {
        "cluster_id": 0,
        "label": "Drums",
        "file_count": 1,
        "members": [
          "audio_3f9c1a7d2b60"
        ],
        "representative_files": [
          {
            "audio_id": "audio_3f9c1a7d2b60",
            "name": "demo-mix.wav"
          }
        ],
        "centroid_audio_id": "audio_3f9c1a7d2b60"
      },
      {
        "cluster_id": 1,
        "label": null,
        "file_count": 1,
        "members": [
          "audio_a81e5c40d7f2"
        ],
        "representative_files": [
          {
            "audio_id": "audio_a81e5c40d7f2",
            "name": "demo-mix-2.wav"
          }
        ],
        "centroid_audio_id": "audio_a81e5c40d7f2"
      }
    ]
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
  • 200 The job with the new label.
  • 400 The body is not valid JSON (`invalid_json`), has a field other than `label` (`invalid_config`), or the label is empty, too long or has control characters (`invalid_label`).
  • 401 Missing, malformed, unknown or revoked API key.
  • 404 The job does not exist (`cluster_job_not_found`) or has no such group (`cluster_group_not_found`).
  • 409 The job is still queued or running, or failed.
  • 429 Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
  • 503 Temporary platform condition; retry shortly.

Try it

Sends this request from your browser to https://api.scarleta.ai with your own key. The key stays in this page only (never saved or put in the address) and is cleared when you leave. Revoke any key you have shared.

Other error codes this operation can return: cluster_job_not_found, db_unavailable, invalid_auth_format, invalid_config, invalid_json, invalid_key_format, missing_api_key, missing_auth, rate_limit_unavailable, unauthorized.

GET/v1/audio/library/cluster/latest

Get your latest finished sound grouping

Free and paid accounts
  • API key required
  • Free and paid accounts
  • No token cost

Returns your most recent completed grouping job with its groups. Send embedding_model to get the latest one of one fingerprint kind. When you have no completed job the answer is 404 cluster_job_not_found.

Cost. Free of charge.

curl "https://api.scarleta.ai/v1/audio/library/cluster/latest" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"

Parameters

NameTypeRequiredDescription
embedding_modelquerystringOptionalOnly consider jobs of this fingerprint kind.
Response fields
FieldTypeDescription
job_idstringIdentifier of the grouping job.
status"queued" | "running" | "completed" | "failed"`queued` and `running` while the job works, then `completed` or `failed`.
n_clustersintegerHow many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991.
embedding_modelstringName of the fingerprint kind the job compared.
file_countintegerFiles the job covers. Allowed: -9007199254740991 to 9007199254740991.
filtersobjectThe filters the job was started with.
iterationsinteger | nullRounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991.
errorstring | nullWhy a failed job failed, or null.
created_atnumberWhen the job was created, in milliseconds since the Unix epoch.
updated_atnumberWhen the job last changed, in milliseconds since the Unix epoch.
clustersarray<object>The groups. Empty until the job is `completed`.
clusters[].cluster_idintegerNumber of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991.
clusters[].labelstring | nullName you gave the group, or null.
clusters[].file_countintegerFiles in the group that still exist. Allowed: -9007199254740991 to 9007199254740991.
clusters[].membersarray<string>`audio_id` of every file in the group, closest to the centre first.
clusters[].representative_filesarray<object>The files nearest the centre of the group, to show as a preview.
clusters[].representative_files[].audio_idstring
clusters[].representative_files[].namestringDisplay name of the file.
clusters[].centroid_audio_idstring | nullThe file nearest the centre, or null when the group is empty.
Errors
StatusCodeDescription
401invalid_keyMissing, malformed, unknown or revoked API key.
404cluster_job_not_foundNo grouping job with this id exists in your account (an id that is not a UUID is the same 404).
429rate_limit_exceededOver the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
503auth_unavailableTemporary platform condition; retry shortly.
Example responses
{
  "data": {
    "job_id": "7d2c9e1a-4b3f-4c68-9a5d-1e0f8b7c6a52",
    "status": "completed",
    "n_clusters": 2,
    "embedding_model": "fingerprint-6144",
    "file_count": 2,
    "filters": {},
    "iterations": 3,
    "error": null,
    "created_at": 1791333870854,
    "updated_at": 1791333874854,
    "clusters": [
      {
        "cluster_id": 0,
        "label": "Drums",
        "file_count": 1,
        "members": [
          "audio_3f9c1a7d2b60"
        ],
        "representative_files": [
          {
            "audio_id": "audio_3f9c1a7d2b60",
            "name": "demo-mix.wav"
          }
        ],
        "centroid_audio_id": "audio_3f9c1a7d2b60"
      },
      {
        "cluster_id": 1,
        "label": null,
        "file_count": 1,
        "members": [
          "audio_a81e5c40d7f2"
        ],
        "representative_files": [
          {
            "audio_id": "audio_a81e5c40d7f2",
            "name": "demo-mix-2.wav"
          }
        ],
        "centroid_audio_id": "audio_a81e5c40d7f2"
      }
    ]
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
  • 200 The latest completed job.
  • 401 Missing, malformed, unknown or revoked API key.
  • 404 No grouping job with this id exists in your account (an id that is not a UUID is the same 404).
  • 429 Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry.
  • 503 Temporary platform condition; retry shortly.

Try it

Sends this request from your browser to https://api.scarleta.ai with your own key. The key stays in this page only (never saved or put in the address) and is cleared when you leave. Revoke any key you have shared.

Other error codes this operation can return: db_unavailable, invalid_auth_format, invalid_key_format, missing_api_key, missing_auth, rate_limit_unavailable, unauthorized.