Skip to main content
Whisper-large transcription is live. Embeddings & image generation in private beta. See live jobs →
Jobs / Developers

An API you've already written against.

Acorn implements the OpenAI HTTP surface for every route we ship — request shape, response shape, error codes. Point your existing client at api.acorncompute.com and it works. The whole reference fits on one page.

Base URL
api.acorncompute.com
Auth
Bearer · ACORN_KEY
Default response
JSON · 2xx · 4xx · 5xx
Free tier
100 hrs / month
Quickstart

Three steps to your first transcript.

If you've never written a line of Acorn code, you'll be done by step three. If you're migrating from OpenAI, skip to step two.

01 — Account

Get your API key.

Sign up at app.acorncompute.com, head to Keys, and create a key scoped to the routes you'll use. New accounts land with $25 in credit and a 100-hour-a-month free tier on transcription.

~/.zshrc
# Acorn keys start with `acn_live_` or `acn_test_`
export ACORN_API_KEY="acn_live_8c1f...c93a"
02 — SDK

Install. Or keep your OpenAI client.

Acorn ships native SDKs for Python, Node, Go, and Ruby. If you have an OpenAI client wired up, you don't have to touch your import lines — change the base URL and the key, and the existing methods route to us unchanged.

pip · native SDK
pip install acorn

from acorn import Acorn
client = Acorn(api_key=os.environ["ACORN_API_KEY"])
npm · native SDK
npm install acorn

import { Acorn } from "acorn";
const client = new Acorn({ apiKey: process.env.ACORN_API_KEY });
go get
go get github.com/acorn-compute/acorn-go

import "github.com/acorn-compute/acorn-go"
client := acorn.NewClient(os.Getenv("ACORN_API_KEY"))
No install · raw HTTP
# All routes accept Bearer auth.
curl https://api.acorncompute.com/v1/audio/transcriptions \
  -H "Authorization: Bearer $ACORN_API_KEY" \
  -F file="@interview.mp3" \
  -F model="whisper-large-v3"
03 — Submit

Submit your first job.

Most routes work async-with-immediate-return: a single POST gives you the result back inline when the job finishes quickly, or a job_id you poll for longer work. The shape is the same.

submit.py
from acorn import Acorn

client = Acorn()  # reads ACORN_API_KEY from env

result = client.audio.transcriptions.create(
  model="whisper-large-v3",
  file=open("interview.mp3", "rb"),
  response_format="verbose_json",
  diarization=True,
)

print(result.text)
print("cost:", result.cost_usd, "USD")
submit.ts
import { Acorn } from "acorn";
import { createReadStream } from "node:fs";

const client = new Acorn();

const result = await client.audio.transcriptions.create({
  model: "whisper-large-v3",
  file: createReadStream("interview.mp3"),
  response_format: "verbose_json",
  diarization: true,
});

console.log(result.text);
submit.go
import "github.com/acorn-compute/acorn-go"

client := acorn.NewClient(os.Getenv("ACORN_API_KEY"))

result, err := client.Audio.Transcriptions.Create(ctx, acorn.TranscriptionRequest{
  Model:          "whisper-large-v3",
  File:           file,
  ResponseFormat: "verbose_json",
  Diarization:    true,
})
submit.sh
curl https://api.acorncompute.com/v1/audio/transcriptions \
  -H "Authorization: Bearer $ACORN_API_KEY" \
  -F file="@interview.mp3" \
  -F model="whisper-large-v3" \
  -F response_format="verbose_json" \
  -F diarization="true"
04 — Receive

Handle the webhook.

For longer batches and larger audio files we POST the result to a URL you register. Every payload is signed with HMAC-SHA256 — verify the header in one line with the SDK, then queue the work.

app.py · Flask
from acorn import verify_webhook

@app.post("/hooks/acorn")
def on_acorn_event():
    event = verify_webhook(
        body=request.get_data(),
        signature=request.headers["X-Acorn-Signature"],
        timestamp=request.headers["X-Acorn-Timestamp"],
        secret=os.environ["ACORN_WEBHOOK_SECRET"],
    )
    if event.type == "job.finished":
        enqueue(event.data.job_id, event.data.result_url)
    return "", 204
server.ts · Express
import { verifyWebhook } from "acorn";

app.post("/hooks/acorn", express.raw({ type: "*/*" }), (req, res) => {
  const event = verifyWebhook({
    body: req.body,
    signature: req.headers["x-acorn-signature"],
    timestamp: req.headers["x-acorn-timestamp"],
    secret: process.env.ACORN_WEBHOOK_SECRET,
  });
  if (event.type === "job.finished") enqueue(event.data.job_id);
  res.status(204).end();
});
Register a webhook URL
curl https://api.acorncompute.com/v1/webhooks \
  -H "Authorization: Bearer $ACORN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":   "https://yourapp.com/hooks/acorn",
    "events":["job.finished","job.failed"]
  }'
SDKs

Four native clients. Five OpenAI ones.

Native SDKs unlock the Acorn-specific bits — webhook verification, job-id helpers, region pinning. The official OpenAI SDKs work too; you just don't get the extras.

Reference

Every endpoint, every status.

The full surface area Acorn exposes today. Beta routes will keep the same request and response shape at GA — only the pricing locks.

POST /v1/audio/transcriptions Transcribe an audio file. Returns text + segments inline. GA
POST /v1/audio/translations Transcribe and translate to English in one pass. GA
POST /v1/embeddings Batch text → vectors. Up to 512 inputs per request. Beta
POST /v1/images/generations Async image generation queue. Webhook on completion. Beta
POST /v1/chat/completions Async chat completion. Non-streaming. OpenAI-compatible shape. Beta
GET /v1/jobs/{job_id} Fetch status & result of any submitted job. GA
GET /v1/jobs List recent jobs · paginated · filterable by route & state. GA
POST /v1/webhooks Register a callback URL for job-finished events. GA
POST /v1/batch Submit a multi-job manifest with BYO MLX checkpoint. Planned · Q4
OpenAI compatibility

Two lines of diff, then you're routing to Acorn.

Your existing OpenAI client works as-is. Change the base URL and the key, and every openai.audio.transcriptions.create() call routes to us instead.

Before · OpenAI
from openai import OpenAI

client = OpenAI(
  api_key=os.environ["OPENAI_API_KEY"],
)
After · Acorn
from openai import OpenAI

client = OpenAI(
  api_key=os.environ["ACORN_API_KEY"],
  base_url="https://api.acorncompute.com/v1",
)
openai-python ≥ 1.30 openai-node ≥ 4.50 openai-go ≥ 0.8 openai-ruby ≥ 0.4 LangChain / LlamaIndex via OpenAI provider
Webhooks

Notify, don't poll.

For longer jobs — batches, BYO models, large audio — register a webhook and we POST the result the moment a worker hands it back.

HMAC-signed, retried, replayable.

  • Signed. Every payload carries X-Acorn-Signature and X-Acorn-Timestamp headers — HMAC-SHA256 over timestamp.body. Reject timestamps outside a five-minute window before comparing the signature.
  • At-least-once. We retry with exponential backoff for up to 24 hours, then drop to the dead-letter queue. Use the job_id as your idempotency key.
  • Filtered. Subscribe per route, per region, per environment. Test webhooks land at a separate URL of your choice.
  • Replayable. The last 30 days of events are queryable from the dashboard if your endpoint was down.
POST https://yourapp.com/hooks/acorn · job.finished
{
  "event": "job.finished",
  "event_id": "evt_8c1f",
  "created": 1747824312,
  "data": {
    "job_id": "job_3uA9k2",
    "route": "/v1/audio/transcriptions",
    "model": "whisper-large-v3",
    "status": "succeeded",
    "duration_sec": 2.1,
    "cost_usd": 0.0569,
    "result_url": "https://res.acorncompute.com/job_3uA9k2",
    "worker": "node_quiet-cedar-4419"
  }
}
Errors & retries

Errors look like errors.

OpenAI-shape error objects, so your existing handlers keep working. The error.acorn block carries retry hints and a worker fingerprint for the support ticket you'll never have to open.

HTTP 503 · worker_unavailable · auto-retried 3×
{
  "error": {
    "code": "worker_unavailable",
    "message": "All nearby nodes were busy. Job was rescheduled.",
    "param": null,
    "type": "server_error",
    "acorn": {
      "retried": 3,
      "workers_tried": ["node_quiet-cedar-4419", "node_brave-otter-1102"],
      "retry_after_sec": 2,
      "job_id": "job_3uA9k2",
      "billed": false
    }
  }
}

The docs are above. Or just sign up.

The free tier covers a real integration before you commit. No card, no demo call — your key works the moment you finish sign-up.