Skip to content
Log inGet an API key
Getting started

Quickstart

Denoise one audio file with zilapi/nr-4.1 in five steps. With a test key this costs nothing; with a live key, a 3-minute file costs about $0.10.

Before you start: you need a ZilAPI account and an API key in ZILAPI_KEY. Create a key (a test key is fine). The curl examples use jq and bash; Python uses requests; JavaScript runs on Node 20 or later as an ES module.
01

Start an upload

Tell us the file name, size in bytes and audio type. You get a file id and a signed upload URL, valid for a limited time. Files up to 5 GB and 6 h are accepted; this example covers a single PUT, which is what you get for files up to 100 MB. Test keys accept files up to 25 MB.

curl -s https://api.zilapi.com/v1/files \
  -H "Authorization: Bearer $ZILAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "talk.wav", "size_bytes": '"$(wc -c < talk.wav)"', "content_type": "audio/wav"}' \
  > upload.json

FILE_ID=$(jq -r .id upload.json)   # file_3Lm8QpZ2rT6w
02

Upload the bytes

PUT the file to upload.url and send every header in upload.headers with it. The bytes go straight to storage, not through the API.

mapfile -t HEADERS < <(jq -r '.upload.headers | to_entries[] | "-H", "\(.key): \(.value)"' upload.json)

curl -X PUT "$(jq -r .upload.url upload.json)" "${HEADERS[@]}" --data-binary @talk.wav
03

Finish the upload and wait until it is ready

Finishing starts validation: we check the size and measure the audio. The file is validating for a few seconds, then ready (with duration_seconds) or rejected (not audio, or longer than 6 h).

curl -s -X POST https://api.zilapi.com/v1/files/$FILE_ID/complete \
  -H "Authorization: Bearer $ZILAPI_KEY"

while :; do
  FILE_STATUS=$(curl -s https://api.zilapi.com/v1/files/$FILE_ID -H "Authorization: Bearer $ZILAPI_KEY" | jq -r .status)
  [ "$FILE_STATUS" != "validating" ] && break
  sleep 2
done
echo "$FILE_STATUS"   # ready
04

Create a denoise job

Name the file and the model. The job is queued and runs on our GPUs. With a live key we hold the exact cost from your credit; if your available credit is too low you get 402 insufficient_credit and nothing starts.

curl -s https://api.zilapi.com/v1/audio/denoise \
  -H "Authorization: Bearer $ZILAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file": "'"$FILE_ID"'", "model": "zilapi/nr-4.1"}' \
  > job.json

JOB_ID=$(jq -r .id job.json)   # job_7Hq2xV9mKp4R
05

Wait for the job, then download the MP3

Poll every few seconds while the job is queued or processing. When it has succeeded, ask for a fresh download URL (valid for 1 hour). Results are kept for 7 days after the job succeeds.

while :; do
  STATUS=$(curl -s https://api.zilapi.com/v1/jobs/$JOB_ID -H "Authorization: Bearer $ZILAPI_KEY" | jq -r .status)
  case "$STATUS" in queued|processing) sleep 5 ;; *) break ;; esac
done
echo "$STATUS"   # succeeded

curl -s https://api.zilapi.com/v1/jobs/$JOB_ID/output -H "Authorization: Bearer $ZILAPI_KEY" > output.json
curl -o "$(jq -r .filename output.json)" "$(jq -r .url output.json)"

The same with the SDK

The official SDKs, zilapi for Python and @zilapi/sdk for JavaScript, wrap the same five calls: uploads (single or multipart), polling with backoff, retries and idempotency keys. They are in beta: the raw HTTP above is the reference.

The whole quickstart
curl -s https://api.zilapi.com/v1/files \
  -H "Authorization: Bearer $ZILAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "talk.wav", "size_bytes": '"$(wc -c < talk.wav)"', "content_type": "audio/wav"}' \
  > upload.json

FILE_ID=$(jq -r .id upload.json)   # file_3Lm8QpZ2rT6w

mapfile -t HEADERS < <(jq -r '.upload.headers | to_entries[] | "-H", "\(.key): \(.value)"' upload.json)

curl -X PUT "$(jq -r .upload.url upload.json)" "${HEADERS[@]}" --data-binary @talk.wav

curl -s -X POST https://api.zilapi.com/v1/files/$FILE_ID/complete \
  -H "Authorization: Bearer $ZILAPI_KEY"

while :; do
  FILE_STATUS=$(curl -s https://api.zilapi.com/v1/files/$FILE_ID -H "Authorization: Bearer $ZILAPI_KEY" | jq -r .status)
  [ "$FILE_STATUS" != "validating" ] && break
  sleep 2
done
echo "$FILE_STATUS"   # ready

curl -s https://api.zilapi.com/v1/audio/denoise \
  -H "Authorization: Bearer $ZILAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file": "'"$FILE_ID"'", "model": "zilapi/nr-4.1"}' \
  > job.json

JOB_ID=$(jq -r .id job.json)   # job_7Hq2xV9mKp4R

while :; do
  STATUS=$(curl -s https://api.zilapi.com/v1/jobs/$JOB_ID -H "Authorization: Bearer $ZILAPI_KEY" | jq -r .status)
  case "$STATUS" in queued|processing) sleep 5 ;; *) break ;; esac
done
echo "$STATUS"   # succeeded

curl -s https://api.zilapi.com/v1/jobs/$JOB_ID/output -H "Authorization: Bearer $ZILAPI_KEY" > output.json
curl -o "$(jq -r .filename output.json)" "$(jq -r .url output.json)"

Next steps

  • Files: uploads over 100 MB in parts, limits and retention.
  • Jobs: the lifecycle, polling guidance and canceling.
  • Errors: every error code, what it means and what to do.
  • Pricing & billing: per-second billing, the 1-minute minimum and receipts.
  • API reference: every endpoint and field, from the OpenAPI spec.