COSMIC

COSMIC APIVersion 1 · result schema 1.1

API documentation

Send audio from your own service and receive the same verdict and evidence as the website, as JSON.

Quick start

An analysis is asynchronous. Upload the audio, then read the job at its status_url until it is finished.

# 1. Upload a recording
curl https://cosmic.audio/v1/analyses \
  -H "Authorization: Bearer $COSMIC_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F [email protected]

# 202 Accepted
{"id": "6f1c…", "state": "queued", "status_url": "https://cosmic.audio/v1/analyses/6f1c…", …}

# 2. Read the job until state is complete, failed or cancelled
curl https://cosmic.audio/v1/analyses/6f1c… \
  -H "Authorization: Bearer $COSMIC_API_KEY"

Authentication

Create a key on your account page. An account can create keys when it has a plan or a credit balance. A key is shown once, when it is created. Store it as a secret and send it with every request:

Authorization: Bearer csk_…

Keys belong to the account. They share its plan allowance, its credits and its history. An analysis created with a key can be read or cancelled only with a key of the same account. Revoking a key on the account page stops it at once.

Call the API from your server, never from a browser or an app you ship: anyone who can read the key can spend your credits.

Endpoints

Base URL: https://cosmic.audio. Request and response bodies are JSON unless stated.

POST/v1/analyses

Upload a recording for analysis, as multipart/form-data.

FieldTypeMeaning
audiofile, requiredThe recording. Up to 90 MiB for lossless audio and 25 MiB for any other codec.
profileauto · full · evidence_onlyauto by default. evidence_only reads provenance, watermarks and metadata without the COSMIC model.
localized_scantrue · falseAlso locate generated passages of about 20 seconds or more inside the recording. Off by default; about a third slower. Counts one analysis per started 5 minutes (seconds_per_count in capabilities).

Send an Idempotency-Key header (8 to 200 characters) to make retries safe: the same key returns the same job for 24 hours instead of starting a new one.

Returns 202 with a job.

GET/v1/analyses/{id}

Read a job. Poll every one to two seconds until state is complete, failed or cancelled. Jobs and their results expire 24 hours after they are created.

DELETE/v1/analyses/{id}

Cancel a job and delete its audio. Returns 204.

POST/v1/analyses/{id}/feedback

Tell us whether an ai or not_detected result was right. One answer per analysis while its result is available.

{"accurate": true}

Optional fields: notes (2,000 characters), song_title and artist (200 each), and source_url (2,048). A source link must use HTTP or HTTPS without embedded credentials. You can send an answer with no other fields.

For an analysis created with an API key, send a key belonging to the same account. Returns 204, or 404 when the result is expired, already answered or unavailable to that key.

We review reports to investigate errors and improve accuracy. Feedback does not retain your audio or download source links. Individual reports are kept for up to 12 months; see Privacy.

POST/v1/feedback

Send a bug report, suggestion or other feedback using a valid API key. No plan allowance or credits are spent.

{"message": "The result link was difficult to find."}

message is required (1–2,000 characters). name (60 characters) and a valid reply email (254) are optional. No attachments are accepted. Returns 201 with a feedback id; invalid fields return 422.

Five submissions per hour per account, shared across its keys and signed-in website submissions. Over the limit, 429. The website form uses a bot check instead of an API key. Messages and optional contact details are kept for up to 12 months.

GET/v1/capabilities

What this deployment accepts: max_upload_bytes, max_lossy_upload_bytes, min_duration_seconds, retention_seconds, the profiles, the localized_scan options and the link remote_sources. No key is needed.

The job

FieldMeaning
idThe analysis id.
statequeued, running, complete, failed or cancelled.
stageWhat is running now, while the job runs.
status_urlWhere to read the job.
created_at, expires_atISO 8601 times, UTC.
resultThe result, once state is complete.
errorA readable reason, when state is failed. A failed analysis is not charged.

The result

A complete job carries one result. Read summary first: it combines every check into one answer.

{
  "schema_version": "1.1",
  "analysis_id": "6f1c…",
  "profile": "auto",
  "summary": {
    "verdict": "ai",
    "headline": "…",
    "basis": "…",
    "provider": null,
    "conflicts": []
  },
  "cosmic": {
    "outcome": "detected",
    "verdict": "ai",
    "ai_probability": 0.97
  },
  "file": {"name": "track.flac", "sha256": "…", "bytes": 31457280, "decoded_duration_seconds": 213.4, …},
  "evidence": […],
  "capabilities": {…},
  "billable": true
}

Fields

FieldMeaning
summary.verdictai, not_detected or inconclusive.
summary.headline, summary.basisThe answer in plain language, and the strongest evidence behind it.
summary.providerWho or what generated the audio, when the evidence supports naming it. Otherwise null.
summary.conflictsChecks that disagree. Conflicts are reported, never hidden.
cosmic.verdictThe COSMIC model's own answer: ai, not_detected, unavailable, or insufficient_duration for recordings under 20 seconds.
cosmic.outcomeHow the check ended (see outcomes).
cosmic.ai_probabilityThe model's score, from 0 to 1, rounded to two decimals (a whole percent: 0.97 is 97%).
cosmic.provider_guess, cosmic.provider_confidenceThe generator family the model finds most likely, for an ai verdict. The confidence is rounded to two decimals. provider_weak_or_ood is true when that guess is weak.
cosmic.reasonWhy there is no verdict, when there is none.
cosmic.partialOnly when localized_scan was requested: status, and the generated passages found as localized_regions (start_seconds, end_seconds, confidence).
evidence[]One entry per check: signed provenance (C2PA), each watermark family, and self-declared metadata. Each has kind, detector_id, outcome and strength (authoritative, cryptographic, statistical or self_declared), and may name a provider, technology or signer.
fileName, SHA-256, size, container, codec, sample rate, channels, duration from the header, decoded_duration_seconds counted from the decoded audio, and the tags that bear on provenance.
capabilitiesThe outcome of every check that ran for this job.
billableWhether this analysis used an allowance place or a credit.
reusedPresent only when your account analysed this exact audio before: first_analyzed_at.

Fields not listed here carry no data.

Outcomes

OutcomeMeaning
detectedThe check found its signal.
not_detectedThe check ran and found nothing.
unverified, invalidA signature or manifest was found but could not be trusted.
unsupportedThe check does not apply to this audio, such as a recording under 20 seconds.
unavailable, error, timed_outThe check could not run. An analysis whose required checks did not run is not charged.

not_detected means no supported signal was found. It is not proof that a person made the recording.

Errors

An error has a JSON body with detail: a readable message, or an object with a code and a message.

{"detail": {"code": "credits_required", "message": "This account has no analyses left. …"}}
StatusMeaning
400Not readable audio, an empty file, or a link that cannot be used.
402 credits_requiredThe account has no plan allowance left and no credits. Uploads are refused too when the account has neither a plan nor credits.
403The key is missing, invalid or revoked, or the account is on hold or suspended.
404No such analysis, it has expired, or it belongs to another account.
413Over 90 MiB, a lossy codec over 25 MiB, or a recording longer than 10 minutes.
415Not an audio file, or a file with a video stream other than cover art.
422A field is missing or has the wrong type.
429An account request limit, temporary cooldown, or invalid-key network limit. Honour Retry-After; account responses include a reason code and retry time.
504The linked service took too long to return the track.

Limits

  • Lossless audio (FLAC, WAV, AIFF, ALAC, WavPack, APE, TTA, TAK) up to 90 MiB; every other codec up to 25 MiB. The codec found in the file decides the limit, not its name.
  • Recordings up to 10 minutes (max_duration_seconds in capabilities), measured from the decoded audio, not the file's header. Longer uploads, and links to longer tracks, are refused with 413 or 400 and not charged.
  • COSMIC gives a verdict on 20 seconds or more of decoded audio. Shorter recordings return 422 audio_too_short, with no queued job, inference call or scan charge. Older saved results may contain insufficient_duration.
  • Audio is deleted when its job ends. A result can be read for 24 hours. What else is kept is in the Privacy policy.
  • All keys and website sessions share the account's rolling-hour admission limit (600 by default); website admissions additionally have a 30/hour cap. One upload flow counts once. Authenticated traffic also shares read (300/minute) and write (60/minute) token buckets. A 429 response includes Retry-After; honour it before retrying. Creating or rotating keys never resets these limits.

Plans and credits

Each new analysis submission uses normal allowance or credits, including the same file again and a cached result. Credits are bought on your account page and do not need a plan.

  • A new submission spends usage even for identical audio. Reusing its Idempotency-Key retrieves the same operation without another charge; a deliberate new scan needs a new key. Reading an existing result is free. There is no free-rescan allowance.
  • Before upload or retrieval, a temporary processing slot is reserved. Delivered charged successes release it. Short, invalid, failed, cancelled or abandoned work consumes the existing protection allowance of 10 per subject per UTC day. Used daily protection returns 429 unpaid_limit; slots temporarily occupied by ongoing work return 503 busy with Retry-After. This is separate from purchased scan totals.
  • Recordings under 20 seconds and failed analyses are free. A credit spent on an analysis that fails is returned.
  • A localized scan counts one analysis per started 5 minutes of the recording: up to 5 minutes uses one, longer uses two. Every new localized scan spends its normal units.
  • The website and your keys share one allowance, one credit balance and one history.

For catalogue-scale volumes, talk to us.

Python example

Upload, wait, and print the answer. Needs requests.

import os, sys, time, uuid
import requests

API = "https://cosmic.audio"
HEADERS = {"Authorization": f"Bearer {os.environ['COSMIC_API_KEY']}"}


def analyse(path: str) -> dict:
    with open(path, "rb") as audio:
        r = requests.post(f"{API}/v1/analyses", headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
                          files={"audio": audio}, timeout=300)
    r.raise_for_status()
    job = r.json()
    while job["state"] in ("queued", "running"):
        time.sleep(1.5)
        job = requests.get(job["status_url"], headers=HEADERS, timeout=30).json()
    if job["state"] != "complete":
        raise RuntimeError(job.get("error") or job["state"])
    return job["result"]


result = analyse(sys.argv[1])
print(result["summary"]["verdict"], "-", result["summary"]["headline"])