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/clusterGroup 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
| Name | Type | Required | Description |
|---|---|---|---|
| n_clustersbody | integer | Optional | How many groups to make, 2 to 20. Defaults to 8. |
| filtersbody | object | Optional | Which 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.bpmbody | object | object | Optional | Tempo range `{min, max}`, or `{near, tolerance}` (tolerance defaults to 3). |
| filters.bpm.minbody | number | Optional | |
| filters.bpm.maxbody | number | Optional | |
| filters.bpm.nearbody | number | Optional | |
| filters.bpm.tolerancebody | number | Optional | |
| filters.tempo_stabilitybody | object | Optional | |
| filters.tempo_stability.minbody | number | Optional | |
| filters.tempo_stability.maxbody | number | Optional | |
| filters.loudness_integratedbody | object | Optional | |
| filters.loudness_integrated.minbody | number | Optional | |
| filters.loudness_integrated.maxbody | number | Optional | |
| filters.loudness_rangebody | object | Optional | |
| filters.loudness_range.minbody | number | Optional | |
| filters.loudness_range.maxbody | number | Optional | |
| filters.true_peak_dbfsbody | object | Optional | |
| filters.true_peak_dbfs.minbody | number | Optional | |
| filters.true_peak_dbfs.maxbody | number | Optional | |
| filters.pitch_lowest_midibody | object | Optional | |
| filters.pitch_lowest_midi.minbody | number | Optional | |
| filters.pitch_lowest_midi.maxbody | number | Optional | |
| filters.pitch_highest_midibody | object | Optional | |
| filters.pitch_highest_midi.minbody | number | Optional | |
| filters.pitch_highest_midi.maxbody | number | Optional | |
| filters.notes_per_secondbody | object | Optional | |
| filters.notes_per_second.minbody | number | Optional | |
| filters.notes_per_second.maxbody | number | Optional | |
| filters.duration_secondsbody | object | Optional | |
| filters.duration_seconds.minbody | number | Optional | |
| filters.duration_seconds.maxbody | number | Optional | |
| filters.key_labelbody | string | array<string> | Optional | One key name or a list, for example `A minor`. |
| filters.key_rootbody | string | array<string> | Optional | |
| filters.key_modebody | "major" | "minor" | Optional | |
| filters.meterbody | string | array<string> | Optional | One time signature or a list, for example `4/4`. |
| filters.speech_languagebody | string | array<string> | Optional | |
| filters.formatbody | array<string> | Optional | File formats to keep, for example `["wav", "mp3"]`. |
| filters.transcript_containsbody | string | Optional | Keep files whose transcript contains this text. |
| filters.sound_descriptionbody | string | Optional | |
| filters.name_containsbody | string | Optional | Keep files whose name contains this text. |
| filters.user_tagsbody | object | Optional | Tag rules: keep files with any, with all, or with none of the listed tags. |
| filters.user_tags.anybody | array<string> | Optional | |
| filters.user_tags.allbody | array<string> | Optional | |
| filters.user_tags.nonebody | array<string> | Optional | |
| filters.auto_tagsbody | array<string> | Optional | |
| filters.analyzedbody | array<string> | Optional | Keep files that have results for every listed analysis category. |
| filters.analyzed_anybody | array<string> | Optional | Keep files that have results for at least one listed category. |
| filters.not_analyzedbody | boolean | Optional | True keeps only files with no analysis results. |
| filters.missingbody | array<string> | Optional | Keep files that lack a value, for example `["bpm"]`. |
| filters.uploaded_afterbody | number | Optional | Milliseconds since the Unix epoch. |
| filters.uploaded_beforebody | number | Optional | Milliseconds since the Unix epoch. |
| filters.starredbody | boolean | Optional | |
| filters.archivedbody | "hide" | "include" | "only" | Optional | Archived files: hidden (default), included, or the only ones shown. |
| filters.include_derivedbody | boolean | Optional | True also returns files made from other files, such as stems. |
| filters.sounds_likebody | object | Optional | Keep files that sound like the given file. |
| filters.sounds_like.audio_idbody | string | Required | |
| filters.sounds_like.min_scorebody | number | Optional | |
| filters.sound_groupbody | object | Optional | Keep the files of one sound group from a grouping job. |
| filters.sound_group.job_idbody | string | Required | |
| filters.sound_group.group_idbody | integer | Required | Allowed: -9007199254740991 to 9007199254740991. |
| filters.trashbody | "hide" | "include" | "only" | Optional | Trashed files: hidden (default), included, or the only ones shown. |
| filters.duplicatesbody | boolean | Optional | True keeps only files that have an identical copy. |
| filters.artistbody | array<string> | Optional | |
| filters.projectbody | array<string> | Optional | |
| filters.albumbody | array<string> | Optional | |
| filters.title_containsbody | string | Optional | |
| filters.credits_containsbody | string | Optional | |
| embedding_modelbody | string | Optional | Name 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
| Field | Type | Description |
|---|---|---|
| job_id | string | Identifier of the new job. |
| status | "queued" | Always `queued` at this point. |
| file_count | integer | Files the job will group. Allowed: -9007199254740991 to 9007199254740991. |
| embedding_model | string | Name of the fingerprint kind that will be compared. |
Errors
| Status | Code | Description |
|---|---|---|
| 400 | invalid_n_clusters | 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 | invalid_key | Missing, malformed, unknown or revoked API key. |
| 403 | cluster_paid_only | The key belongs to a free account. |
| 409 | cluster_in_progress | A job of yours is already queued or running. |
| 422 | not_enough_files | Too few files with a fingerprint (`not_enough_files`), or more than the limit (`cluster_too_large`). |
| 429 | rate_limit_exceeded | Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry. |
| 503 | cluster_unavailable | 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`. |
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
| Name | Type | Required | Description |
|---|---|---|---|
| job_idpath | string | Required | The `job_id` returned when the grouping was started. |
Response fields
| Field | Type | Description |
|---|---|---|
| job_id | string | Identifier of the grouping job. |
| status | "queued" | "running" | "completed" | "failed" | `queued` and `running` while the job works, then `completed` or `failed`. |
| n_clusters | integer | How many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991. |
| embedding_model | string | Name of the fingerprint kind the job compared. |
| file_count | integer | Files the job covers. Allowed: -9007199254740991 to 9007199254740991. |
| filters | object | The filters the job was started with. |
| iterations | integer | null | Rounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991. |
| error | string | null | Why a failed job failed, or null. |
| created_at | number | When the job was created, in milliseconds since the Unix epoch. |
| updated_at | number | When the job last changed, in milliseconds since the Unix epoch. |
| clusters | array<object> | The groups. Empty until the job is `completed`. |
| clusters[].cluster_id | integer | Number of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].label | string | null | Name you gave the group, or null. |
| clusters[].file_count | integer | Files in the group that still exist. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].members | array<string> | `audio_id` of every file in the group, closest to the centre first. |
| clusters[].representative_files | array<object> | The files nearest the centre of the group, to show as a preview. |
| clusters[].representative_files[].audio_id | string | |
| clusters[].representative_files[].name | string | Display name of the file. |
| clusters[].centroid_audio_id | string | null | The file nearest the centre, or null when the group is empty. |
Errors
| Status | Code | Description |
|---|---|---|
| 401 | invalid_key | Missing, malformed, unknown or revoked API key. |
| 404 | cluster_job_not_found | No grouping job with this id exists in your account (an id that is not a UUID is the same 404). |
| 429 | rate_limit_exceeded | Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry. |
| 503 | auth_unavailable | Temporary 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
| Name | Type | Required | Description |
|---|---|---|---|
| job_idpath | string | Required | The `job_id` returned when the grouping was started. |
| group_idpath | string | Required | The group's `cluster_id`, a whole number. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| labelbody | string | Required | The new name, 1 to 60 characters; spaces around it are removed first, so the schema limit applies to the trimmed text. |
Response fields
| Field | Type | Description |
|---|---|---|
| job_id | string | Identifier of the grouping job. |
| status | "queued" | "running" | "completed" | "failed" | `queued` and `running` while the job works, then `completed` or `failed`. |
| n_clusters | integer | How many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991. |
| embedding_model | string | Name of the fingerprint kind the job compared. |
| file_count | integer | Files the job covers. Allowed: -9007199254740991 to 9007199254740991. |
| filters | object | The filters the job was started with. |
| iterations | integer | null | Rounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991. |
| error | string | null | Why a failed job failed, or null. |
| created_at | number | When the job was created, in milliseconds since the Unix epoch. |
| updated_at | number | When the job last changed, in milliseconds since the Unix epoch. |
| clusters | array<object> | The groups. Empty until the job is `completed`. |
| clusters[].cluster_id | integer | Number of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].label | string | null | Name you gave the group, or null. |
| clusters[].file_count | integer | Files in the group that still exist. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].members | array<string> | `audio_id` of every file in the group, closest to the centre first. |
| clusters[].representative_files | array<object> | The files nearest the centre of the group, to show as a preview. |
| clusters[].representative_files[].audio_id | string | |
| clusters[].representative_files[].name | string | Display name of the file. |
| clusters[].centroid_audio_id | string | null | The file nearest the centre, or null when the group is empty. |
Errors
| Status | Code | Description |
|---|---|---|
| 400 | invalid_label | 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 | invalid_key | Missing, malformed, unknown or revoked API key. |
| 404 | cluster_group_not_found | The job does not exist (`cluster_job_not_found`) or has no such group (`cluster_group_not_found`). |
| 409 | cluster_job_not_ready | The job is still queued or running, or failed. |
| 429 | rate_limit_exceeded | Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry. |
| 503 | auth_unavailable | Temporary 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/latestGet 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
| Name | Type | Required | Description |
|---|---|---|---|
| embedding_modelquery | string | Optional | Only consider jobs of this fingerprint kind. |
Response fields
| Field | Type | Description |
|---|---|---|
| job_id | string | Identifier of the grouping job. |
| status | "queued" | "running" | "completed" | "failed" | `queued` and `running` while the job works, then `completed` or `failed`. |
| n_clusters | integer | How many groups the job was asked for. Allowed: -9007199254740991 to 9007199254740991. |
| embedding_model | string | Name of the fingerprint kind the job compared. |
| file_count | integer | Files the job covers. Allowed: -9007199254740991 to 9007199254740991. |
| filters | object | The filters the job was started with. |
| iterations | integer | null | Rounds the grouping needed, once it has finished; null before. Allowed: -9007199254740991 to 9007199254740991. |
| error | string | null | Why a failed job failed, or null. |
| created_at | number | When the job was created, in milliseconds since the Unix epoch. |
| updated_at | number | When the job last changed, in milliseconds since the Unix epoch. |
| clusters | array<object> | The groups. Empty until the job is `completed`. |
| clusters[].cluster_id | integer | Number of the group inside the job. Use it as `group_id` to name the group. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].label | string | null | Name you gave the group, or null. |
| clusters[].file_count | integer | Files in the group that still exist. Allowed: -9007199254740991 to 9007199254740991. |
| clusters[].members | array<string> | `audio_id` of every file in the group, closest to the centre first. |
| clusters[].representative_files | array<object> | The files nearest the centre of the group, to show as a preview. |
| clusters[].representative_files[].audio_id | string | |
| clusters[].representative_files[].name | string | Display name of the file. |
| clusters[].centroid_audio_id | string | null | The file nearest the centre, or null when the group is empty. |
Errors
| Status | Code | Description |
|---|---|---|
| 401 | invalid_key | Missing, malformed, unknown or revoked API key. |
| 404 | cluster_job_not_found | No grouping job with this id exists in your account (an id that is not a UUID is the same 404). |
| 429 | rate_limit_exceeded | Over the account request budget or the per-minute cap of this route. Wait for the `Retry-After` header, then retry. |
| 503 | auth_unavailable | Temporary 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.