Browse API endpoints
post/batch/exports

Build and monitor a track or stem ZIP export

Select audio IDs, or an import batch, and package completed artifacts or render saved Studio mixes. The API returns an export ID immediately, tracks progress in the background, and provides a temporary ZIP URL when ready.

How this endpoint works

Use download mode to collect existing originals, restorations, masters, MIDI, and stems without rendering. For exact individual stem versions, provide one audio_id, versions=['stem'], stem_selections, and stem_format. Use render mode with source='individual_stems' to render each visible stem separately using its selected version and mix settings before archiving. Main and Combined Stems renders can also produce Atmos or MP4 where supported. Supply audio_ids for an explicit selection, or only batch_id for every owned track in that import batch. For one audio_id, an optional mix_snapshot captures current Studio edits; otherwise renders use saved mix settings.

  1. 1

    Choose a mode and selection

    Send audio_ids or batch_id with download versions and download_profile, or with a render source, target, format, and bit depth. For selected stems, each stem_selections item names a stem_id and version: restored requires upscaled_id, mastered requires mastered_id, and midi requires transcription_id plus stem_format='midi'. stem_format overrides the download profile. MIDI archives include only the explicitly selected transcriptions as separate .mid files.

  2. 2

    Monitor the export job

    Save export_id and poll GET /batch/exports/{export_id} until completed, completed_with_missing_files, failed, or cancelled. Studio uses realtime archive_exports updates. Partial success means the ZIP is ready and missing_items explains omissions. Resume using the saved ID; request_key does not deduplicate new POST requests. Cancel an active export with POST /batch/exports/{export_id}/cancel.

  3. 3

    Download the ZIP

    Use download_url before expires_at; archives remain available for 24 hours. To leave the progress UI and force email delivery, call POST /batch/exports/{export_id}/send-email.

Common use cases

Choose this operation when it matches the source and result your pipeline needs.

Export a user's music library

Package multiple downloaded or uploaded tracks into one file for backup or migration.

Deliver a stem pack

Package the exact original, restored, mastered, or MIDI version selected for each stem. MIDI archives omit non-MIDI selections and unselected transcriptions. Download mode does not apply mixer levels or realtime effects, and unavailable exact selections are rejected instead of substituted.

Deliver processed versions

Bundle the original, restored, and mastered versions for a project handoff.

Processing used by 80,000+ music makers

Results people rely on

“It worked! Well done :) Many thanks :))))”

Vicki (People Like Us)

2600+ songs saved

“I love the interface. I love the bulk upload/download features. They're a life saver! Also I just realized that Apollo is magic and I don't even need to use denoise. Apollo somehow removes noise much more naturally. So I'm actually spending way less credits than I expected.”
I

IMDK

“It can bring back clarity without making everything sound too artificial or overprocessed. Stem separation is also surprisingly clean.”
R

R.

Audio restoration and stem separation

“Does amazing job in restoring Audio from bad quality MP3s and badly extracted audio from CDs. Always my go to for audio restoration.”
N

N.

MP3 and CD audio restoration

Code examples

Server-side example

JavaScript (Node.js)
import { writeFile } from "node:fs/promises";

const API_URL = "https://api.neuralanalog.com";
const API_KEY = process.env.NEURALANALOG_API_KEY;
const audioId = "6c62f8e7-02a3-48c0-a5b5-5de87ed9c31a";
const response = await fetch(`${API_URL}/batch/exports`, {
  method: "POST",
  headers: {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    audio_ids: [audioId],
    mode: "download",
    versions: ["original", "restored", "mastered"],
    download_profile: "optimal",
  }),
});
if (!response.ok) {
  throw new Error(`Archive request failed with ${response.status}`);
}

const started = await response.json();
// Save export_id to resume polling without submitting another export.
console.log("Export ID:", started.export_id);
// Optional while processing: POST /batch/exports/{export_id}/send-email or /cancel.
const deadline = Date.now() + 2 * 60 * 60 * 1000;
let archive;
for (;;) {
  if (Date.now() >= deadline) throw new Error("Archive is still processing; resume with its export ID.");
  const statusResponse = await fetch(
    `${API_URL}/batch/exports/${started.export_id}`,
    { headers: { "X-API-Key": API_KEY } },
  );
  if (!statusResponse.ok) {
    throw new Error(`Archive status failed with ${statusResponse.status}`);
  }
  archive = await statusResponse.json();
  if (["completed", "completed_with_missing_files"].includes(archive.status)) break;
  if (["failed", "cancelled"].includes(archive.status)) {
    throw new Error(archive.error_message || `Archive ${archive.status}`);
  }
  await new Promise((resolve) => setTimeout(resolve, 2000));
}

if (!archive.download_url) throw new Error("Archive has no download URL; it may have expired.");
if (archive.files_skipped) console.warn("Unavailable files:", archive.missing_items);
const download = await fetch(archive.download_url, { redirect: "follow" });
if (!download.ok) {
  throw new Error(`Archive download failed with ${download.status}`);
}
await writeFile("neuralanalog-archive.zip", Buffer.from(await download.arrayBuffer()));

Parameters

Send the API key from a trusted server. Never expose it in client-side JavaScript.

JSON request body

audio_ids
optionalarray<string>

Selected audio asset IDs. Omit to export every owned track in batch_id.

batch_id
optionalstring | null

Import batch used to scope progress and, when audio_ids is omitted, select every owned non-deleted track in the batch.

request_key
optionalstring | null

Opaque client key echoed in progress records to reconnect the UI. It does not deduplicate POST requests; resume using export_id.

mode
optionalstring

render applies each track's persisted mix before archiving; download packages existing artifacts without changing them.

"download""render"

Default: "render"

format
optionalstring

Audio format for rendered artifacts.

Default: "original"

download_profile
optionalstring | null

Per-version format profile used in download mode. Optimal preserves original uploads and uses FLAC for processed audio; Standard uses MP3 originals and FLAC processed audio; Heavy uses MP3 originals and WAV processed audio.

"optimal""standard""heavy"
versions
optionalarray<string>

Existing artifact categories to include in download mode. Transcriptions remain MIDI files regardless of audio format.

source
optionalstring

Mix source used in render mode.

"main""stems""individual_stems"

Default: "main"

mix_snapshot
optionalobject | null

Current Studio mix state for a single audio_id in render mode. Overrides saved state for this export without modifying the session. Omit for batch renders to use each track's saved Studio mix.

render_track_ids
optionalarray<string> | null

Exact mixer track IDs captured with mix_snapshot for one Main or Combined Stems render. Main uses __main_audio_track__; combined uses stem IDs. Later stems are never added to this export.

stem_selections
optionalarray<object> | null

Exact stem versions for one audio_id in download mode with versions=['stem'] and stem_format. Restored selections require upscaled_id; mastered selections require mastered_id; MIDI selections require transcription_id and stem_format='midi'. Only the selected versions are included, never other available transcriptions. Omit stem_selections to use the saved Studio selection on visible stem rows; MIDI then includes only selected MIDI versions. Mixer levels and realtime effects are not rendered.

stem_format
optionalstring | null

Format for every explicitly selected stem version, overriding download_profile. Missing audio formats are converted by the archive worker; MIDI packages the existing selected MIDI files without conversion.

"wav""flac""mp3""m4a"
target
optionalstring

Audio export, original-video MP4 with replaced audio, or cover-art MP4.

"audio""video""cover-video"

Default: "audio"

bit_depth
optionalinteger

No description provided.

162432

Default: 24

Successful response

200Successful Response
export_id
requiredstring

No description provided.

user_id
requiredstring

No description provided.

audio_ids
requiredarray<string>

No description provided.

mode
requiredstring

No description provided.

"download""render"
status
requiredstring

No description provided.

"queued""rendering""archiving""completed""completed_with_missing_files""failed""cancelled"
created_at
requiredstring<date-time>

No description provided.

updated_at
requiredstring<date-time>

No description provided.

request_key
optionalstring | null

No description provided.

total_tracks
optionalinteger

No description provided.

Default: 0

completed_tracks
optionalinteger

No description provided.

Default: 0

total_files
optionalinteger

No description provided.

Default: 0

files_added
optionalinteger

No description provided.

Default: 0

files_skipped
optionalinteger

No description provided.

Default: 0

progress_percent
optionalinteger

No description provided.

Default: 0

progress_message
optionalstring | null

No description provided.

missing_items
optionalarray<object>

No description provided.

batch_id
optionalstring | null

No description provided.

error_message
optionalstring | null

No description provided.

modal_function_call_id
optionalstring | null

No description provided.

email_sent_at
optionalstring<date-time> | null

No description provided.

completed_at
optionalstring<date-time> | null

No description provided.

expires_at
optionalstring<date-time> | null

No description provided.

download_url
optionalstring | null

Persisted private archive URL, available until expires_at after completion.

Errors

A missing or invalid X-API-Key returns an authentication error. Validation errors use the declared 422 response below.
422Validation Error
detail
optionalarray<object>

No description provided.

Next steps