---
title: Audio Uploads
description: Mint a signed PUT URL and analyze with audio_upload_ref.
type: doc
icon: upload
sidebar:
  label: Audio Uploads
  order: 8
  icon: upload
search:
  tags: [api]
---
Use `POST /v2/audio/uploads` when you have a local audio file and do not want to send the bytes as `multipart/form-data` on the analyze call.

The flow is:

1. Call this endpoint to mint a scoped, write-once signed PUT URL.
2. `PUT` the audio bytes to `upload_url`. Send **every header** in `required_headers` (not only `Content-Type`). The PUT targets object storage, not `api.amplifierhealth.com`.
3. Call any analyze endpoint with form field `audio_upload_ref` set to the mint response's `upload_ref`.

Provide **either** `audio` **or** `audio_upload_ref` on analyze — not both. See [Model API](/reference/models#analyze-with-a-model), [Sign API](/reference/signs#analyze-with-a-sign), and [Longitudinal API](/reference/longitudinal-api).


## Mint an upload URL

`POST /v2/audio/uploads`

Mint a signed upload URL for a direct-to-storage PUT.

Requests use `Content-Type: application/json`.

### Request Body

| Field | Type | Required | Description |
|---|---|---|---|
| `content_type` | string | Yes | MIME type of the file you will PUT (for example `audio/wav`, `audio/flac`, `audio/mpeg`, `audio/mp4`). The `Content-Type` on the PUT must match this value and the `Content-Type` entry in `required_headers`. |
| `filename` | string or null | No | Original filename. Optional metadata for the upload; it is not a path on Amplifier. |

### Response

| Field | Type | Description |
|---|---|---|
| `upload_url` | string | Signed PUT URL. Write the audio bytes here once before `expires_at`. |
| `upload_ref` | string | Opaque reference. Pass this value as **`audio_upload_ref`** on the analyze request. |
| `expires_at` | string | ISO 8601 time after which `upload_url` is no longer valid. |
| `required_headers` | object | Headers that **must** be sent on the PUT, including `Content-Type` and `X-Goog-Content-Length-Range`. Copy them as returned. Omitting a signed header makes storage reject the PUT (`MalformedSecurityHeader`). |

The analyze form field is named `audio_upload_ref`. The mint field is named `upload_ref`. Use the mint value in the analyze field.

### Example response

```json
{
  "upload_url": "https://storage.googleapis.com/example-bucket/example-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-SignedHeaders=content-type%3Bhost%3Bx-goog-content-length-range&...",
  "upload_ref": "YjVkMmJiNTQ2YWZmNGE0ZGIzNGIxYzAwNmViMjA3ZjQ6d2F2",
  "expires_at": "2026-09-10T12:43:58.097178+00:00",
  "required_headers": {
    "Content-Type": "audio/wav",
    "X-Goog-Content-Length-Range": "0,104857600"
  }
}
```

`X-Goog-Content-Length-Range` is the byte range GCS will accept for this object (here `0`–`104857600`). Amplifier still enforces the [audio size and duration limits](/guides/audio-requirements#file-size) when the job is analyzed.

### Example

Mint, PUT, then analyze. Copy `upload_url` and `upload_ref` from the mint JSON. On the PUT, send every `required_headers` pair — for the example response above, that is `Content-Type` and `X-Goog-Content-Length-Range`.

<CodeGroup param="lang">

```bash cURL
curl -X POST https://api.amplifierhealth.com/v2/audio/uploads \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"content_type":"audio/wav","filename":"recording.wav"}'

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: audio/wav" \
  -H "x-goog-content-length-range: 0,104857600" \
  --data-binary @recording.wav

curl -X POST https://api.amplifierhealth.com/v2/models/pulse/analyze \
  -H "X-Account-ID: your-account-id" \
  -H "X-API-Key: your-api-key" \
  -F "audio_upload_ref=$UPLOAD_REF"
```

```javascript JavaScript
const fs = require("fs");

const mint = await fetch("https://api.amplifierhealth.com/v2/audio/uploads", {
  method: "POST",
  headers: {
    "X-Account-ID": process.env.AMPLIFIER_ACCOUNT_ID,
    "X-API-Key": process.env.AMPLIFIER_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content_type: "audio/wav",
    filename: "recording.wav",
  }),
});

const { upload_url, upload_ref, required_headers } = await mint.json();

await fetch(upload_url, {
  method: "PUT",
  headers: required_headers,
  body: fs.readFileSync("recording.wav"),
});

const form = new FormData();
form.append("audio_upload_ref", upload_ref);

const response = await fetch("https://api.amplifierhealth.com/v2/models/pulse/analyze", {
  method: "POST",
  headers: {
    "X-Account-ID": process.env.AMPLIFIER_ACCOUNT_ID,
    "X-API-Key": process.env.AMPLIFIER_API_KEY,
  },
  body: form,
});

const data = await response.json();
```

```python Python
# Requires httpx: pip install httpx
import os
import httpx

headers = {
    "X-Account-ID": os.environ["AMPLIFIER_ACCOUNT_ID"],
    "X-API-Key": os.environ["AMPLIFIER_API_KEY"],
}

mint = httpx.post(
    "https://api.amplifierhealth.com/v2/audio/uploads",
    headers=headers,
    json={"content_type": "audio/wav", "filename": "recording.wav"},
)
payload = mint.json()

with open("recording.wav", "rb") as f:
    httpx.put(
        payload["upload_url"],
        headers=payload["required_headers"],
        content=f.read(),
    )

response = httpx.post(
    "https://api.amplifierhealth.com/v2/models/pulse/analyze",
    headers=headers,
    data={"audio_upload_ref": payload["upload_ref"]},
)

data = response.json()
```

</CodeGroup>

If the PUT omits `X-Goog-Content-Length-Range` (or any other name listed in `required_headers` / `X-Goog-SignedHeaders`), storage returns XML `MalformedSecurityHeader` and analyze fails with `Invalid audio_upload_ref`.

### Errors

For 401 and other Amplifier codes, see [Errors](/reference/errors#error-codes). A `422` response is a request-validation error (for example a missing `content_type`). After a failed PUT, analyze returns `400` with `INVALID_REQUEST` / `Invalid audio_upload_ref`.

## Related pages

- [Model API](/reference/models) — analyze with `audio` or `audio_upload_ref`.
- [Sign API](/reference/signs) — same audio fields on a single sign.
- [Longitudinal API](/reference/longitudinal-api) — analyze into a group with `audio` or `audio_upload_ref`.
- [Audio Requirements](/guides/audio-requirements) — formats, size, and duration.
