Skip to main content
Acorn API / Docs

Transcription from $0.05 an audio-hour.

Priced off idle Apple Silicon, not rented GPUs. Acorn speaks the OpenAI transcription API, so an existing client needs a one-line base-URL change.

Base URL
api.acorncompute.com
Auth
Bearer · ACORN_API_KEY
Default response
JSON · 2xx · 4xx · 5xx
Access
Pilot · by request
Quickstart

Four steps to your first transcript.

If you've never written a line of Acorn code, you'll be done by step four.

01 — Account

Request API access.

Submit a pilot access request at acorncompute.com/signup. The team reviews every request, approves access and provides an API key. Set that key as ACORN_API_KEY in your environment.

Request pilot access →

02 — Connect

Call the API over plain HTTPS.

Every route takes your key as a Bearer token. Python needs requests (pip install requests); Node 18+ has fetch built in. Run the selected examples in order in one Python file or one Node .mjs file. Migrating an OpenAI client? whisper-1 maps to the same model; see the compatibility pilot for setup and limits.

Python · connect
import os, time
import requests

API = "https://api.acorncompute.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ACORN_API_KEY']}"}
Node · connect
const API = "https://api.acorncompute.com/api/v1";
const headers = {
  Authorization: `Bearer ${process.env.ACORN_API_KEY}`,
  "Content-Type": "application/json",
};
No install · raw HTTP
# Every route accepts Bearer auth.
curl https://api.acorncompute.com/api/v1/models \
  -H "Authorization: Bearer $ACORN_API_KEY"
03 — Submit

Submit your first job.

Use any audio file you already have. Put it at a URL Acorn can fetch (a presigned S3 or R2 link works) and submit it to POST /api/v1/jobs. You get 202 and a job_id right away while the job runs. The curl example needs jq.

Python · submit
resp = requests.post(f"{API}/jobs", headers=HEADERS, json={
  "audio_url": "https://your-bucket.example/interview.mp3",  # your file
  "model": "whisper-large-v3-turbo-q4",
})
resp.raise_for_status()
job = resp.json()
Node · submit
const submitted = await fetch(`${API}/jobs`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    audio_url: "https://your-bucket.example/interview.mp3", // your file
    model: "whisper-large-v3-turbo-q4",
  }),
});
if (!submitted.ok) throw new Error(await submitted.text());
const job = await submitted.json();
curl · native async job
AUDIO_URL="https://your-bucket.example/interview.mp3"  # your file
ACORN_JOB_ID=$(curl -sf https://api.acorncompute.com/api/v1/jobs \
  -H "Authorization: Bearer $ACORN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"audio_url":"'"$AUDIO_URL"'","model":"whisper-large-v3-turbo-q4"}' \
  | jq -r .job_id)
[ -n "$ACORN_JOB_ID" ] || echo "submit failed: check your key and URL"
04 — Receive

Read your transcript.

/result answers 202 until the job finishes, so poll it, then print the transcript. If it stays pending for long, GET /api/v1/jobs/{id} shows how much work is ahead of it. To be notified instead of polling, add a callback_url when you submit; see webhooks.

Python · wait, then read
while (r := requests.get(f"{API}/jobs/{job['job_id']}/result", headers=HEADERS)).status_code == 202:
  time.sleep(10)
body = r.json()
print((body.get("result") or {}).get("transcript") or body.get("error"))
Node · wait, then read
let r;
while ((r = await fetch(`${API}/jobs/${job.job_id}/result`, { headers })).status === 202) {
  await new Promise((done) => setTimeout(done, 10_000));
}
const body = await r.json();
console.log(body.result?.transcript ?? body.error);
curl · wait, then read
until [ "$(curl -s -o result.json -w '%{http_code}' \
    https://api.acorncompute.com/api/v1/jobs/$ACORN_JOB_ID/result \
    -H "Authorization: Bearer $ACORN_API_KEY")" != 202 ]; do sleep 10; done
jq -r '.result.transcript // .error' result.json

The full native result looks like this (abridged). Pass ?format=srt or ?format=vtt for captions.

GET /api/v1/jobs/{id}/result
{
  "job_id": "job_…",
  "status": "completed",
  "result": {
    "transcript": "Thanks for joining us today…",
    "language": "en",
    "duration_seconds": 183.4,
    "word_count": 412,
    "segments": [ … ]
  },
  "served_by_model": { "model": "whisper-large-v3-turbo-q4", … }
}

What you're billed for: the audio's measured duration, at your account's per-audio-hour rate, locked when a worker picks up the job. GET /api/v1/usage/summary shows audio processed and spend.

Concepts

Know what is live before you build.

POST /v1/audio/transcriptions is a supported pilot path. It is synchronous: the request can remain open for up to 600 seconds. No webhooks on this path; use the native jobs API for async work.

● Sync only: use POST /api/v1/jobs with callback_url for async work● Failures: deadline_exceeded keeps the backend job alive; multipart setup can return local_audio_not_enabled● Example: {"audio_url":"https://example.com/audio.mp3","callback_url":"https://yourapp.com/hooks/acorn"}

Pilot See compatibility limits and error details, including {"code":"invalid_audio_duration","request_id":"…"}.

Keep the details open in another tab.

Endpoints, job states, webhooks, compatibility limits, and errors now have a dedicated reference.