If your app already calls audio.transcriptions.create through the official OpenAI SDK, you do not need a new client library, a new response parser, or a rewrite of the upload path. SpeechWeave supports the same /v1/audio/transcriptions endpoint. All you need to change is two config values, everything else works the same.
That swap is the whole first step, but it is also where the limitations of the OpenAI SDK kick in. The drop-in path is a blocking multipart POST, always billed on the standard queue, and it is capped at 90 MB because the request still travels through a standard HTTP body limit. OpenAI’s own Whisper cap is 25 MB, so you still get a much larger file in a single call. For files above 90 MB, deferred pricing (50% cheaper), and webhooks, you’ll use the native SpeechWeave SDK instead.
Save up to 87.5% compared to OpenAI Whisper. Our Core rates are just $0.0015/min for standard and $0.00075/min for deferred (Whisper charges $0.006/min). Check speechweave.com/pricing for the latest pricing.
1. Point the official OpenAI client at SpeechWeave
Create an API key in the dashboard and put it in SPEECHWEAVE_API_KEY. The requests 402 if the workspace has no credit in their prepaid wallet.
Node.js (openai package)
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.SPEECHWEAVE_API_KEY,
baseURL: "https://api.speechweave.com/v1",
});
const result = await client.audio.transcriptions.create({
file: fs.createReadStream("meeting.mp3"),
model: "core",
});
console.log(result.text);
Pass a stream (or an open file handle in Python). Do not readFileSync a 90 MB buffer into the process if you can avoid it.
Python (openai package)
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["SPEECHWEAVE_API_KEY"],
base_url="https://api.speechweave.com/v1",
)
with open("meeting.mp3", "rb") as audio:
result = client.audio.transcriptions.create(
model="core",
file=audio,
)
print(result.text)
Anything that already accepts an OpenAI client or a base_url (LangChain, LlamaIndex, a thin wrapper you wrote) takes the same two-line change.
client.audio.translations.create({ file, model: "core" }) works the same way for English translation. OpenAI’s translations endpoint has no language parameter, the source language is always auto-detected.
2. What actually maps, and what does not
| Field | Behavior |
|---|---|
file |
Multipart upload. Typical self-serve cap on this path is 90 MB (proxy_max_bytes from GET /v1/limits). OpenAI’s 25 MB Whisper limit does not apply. |
model |
Prefer "core" or "max". "whisper-1", "gpt-4o-transcribe", and "gpt-4o-mini-transcribe" all alias to core. |
prompt |
Vocabulary / spelling hint. Truncated at 2000 characters. Only reliably influences the first ~30 seconds of audio, not a full glossary. |
response_format |
json (default), verbose_json, text, srt, vtt. text / srt / vtt come back as text/plain. |
temperature |
Accepted, clamped to [0, 1]. |
language |
ISO-639-1 (en, es, …). Ignored on the translations endpoint. |
timestamp_granularities |
segment and/or word. word only appears on verbose_json. |
service_mode |
Not a field on this path. The OpenAI-compatible route always uses the standard queue. Deferred pricing requires the native SDK. |
webhook_url |
Not accepted, here or on POST /v1/jobs. Webhooks are workspace endpoints in the dashboard. |
Default json responses include text plus task, duration, and language. That is a superset of OpenAI’s { text } object. Code that only reads .text keeps working.
Exact byte ceilings for your key:
curl -sS https://api.speechweave.com/v1/limits \
-H "Authorization: Bearer $SPEECHWEAVE_API_KEY"
You get max_input_bytes, sync_max_bytes, and proxy_max_bytes. Do not hard-code 90 MB or 250 MB in production as those are base limits and can be raised at the account level.
3. Long jobs return 504 with a job_id
The drop-in route blocks until the transcript is ready. If it does not finish before the response deadline (a little over a minute), you get HTTP 504 and a JSON body with job_id. The job has not failed and is still running. The official OpenAI SDK will throw on that status. Read job_id from the error body and poll:
curl -sS "https://api.speechweave.com/v1/jobs/$JOB_ID" \
-H "Authorization: Bearer $SPEECHWEAVE_API_KEY"
Raising the OpenAI client’s timeout only helps if the server wait is raised to match. It does not turn this into a deferred job, and it does not attach a webhook. If 504s are a regular occurrence, the native SDK offers better workflows for this.
A 413 on this path means the file is over the proxy cap. Switch to the native SDK (next section). A 402 means the wallet is empty or a spend cap was hit.
4. When to leave the OpenAI SDK
Stay on the official OpenAI client while files stay under the proxy cap and a blocking call is acceptable.
Move to @speechweave/node or speechweave when you want any of:
- Files up to the account cap, typically 250 MB self-serve. The SDK uploads straight to storage, so the bytes never enter the 90 MB proxy.
service_mode: "deferred"at half the standard price.- Workspace webhooks instead of holding an HTTP connection open.
| Integration Type | What you call | File Limit | Execution Mode | Response & Handling |
|---|---|---|---|---|
| OpenAI SDK drop-in | client.audio.transcriptions.create |
Max ~90 MB | Standard queue (always) | Wait inline for { text }. Returns 504 + job_id if it runs long. |
| Native SDK (same call shape) | sw.audio.transcriptions.create |
Up to account cap | Standard queue (waits for .text) |
Same { text }. The SDK handles presign, PUT, job create, and poll. |
| Native SDK (jobs) | sw.jobs.create + waitForJob |
Up to account cap | Standard by default. Pass service_mode: "deferred" for the discounted background queue. |
Poll GET /v1/jobs/:id or use a workspace webhook. |
The middle row is the same call shape as OpenAI. It still bills on the standard queue because it waits for .text in-process. Deferred pricing is jobs.create with service_mode: "deferred" below.
import fs from "node:fs";
import { SpeechWeave } from "@speechweave/node";
const sw = new SpeechWeave({ api_key: process.env.SPEECHWEAVE_API_KEY! });
const out = await sw.audio.transcriptions.create({
file: fs.createReadStream("podcast.mp3"),
filename: "podcast.mp3",
model: "core",
});
console.log(out.text);
import os
from speechweave import SpeechWeave
client = SpeechWeave(api_key=os.environ["SPEECHWEAVE_API_KEY"])
with open("podcast.mp3", "rb") as audio:
result = client.audio.transcriptions.create(
file=audio,
filename="podcast.mp3",
model="core",
)
print(result["text"])
The native Python helper returns a dict, not an object. Use result["text"].
For deferred jobs, pass the file to jobs.create. The SDK still does the storage upload and you get a job back instead of { text }:
import { SpeechWeave, waitForJob } from "@speechweave/node";
const sw = new SpeechWeave({ api_key: process.env.SPEECHWEAVE_API_KEY! });
const job = await sw.jobs.create({
file: "./podcast.mp3",
model: "core",
service_mode: "deferred",
});
const done = await waitForJob(sw, job.id);
console.log(done.transcript);
Or skip polling and register an HTTPS endpoint under Dashboard → Webhooks. There is no per-job callbackUrl. See the SDK docs.
5. Core vs Max after the swap
core is the default. Use it for podcasts, clean meetings, voicemail, and anything you previously sent to whisper-1.
max costs more and is slower. Use it for noise, overlapping speakers, heavy accents, or legal or medical vocabulary you cannot afford to mangle. You can keep core on the OpenAI client for the migration and route a subset of traffic to max later without changing the rest of the stack.
Deferred vs standard is independent of the model. Both models offer the 50% deferred discount. The OpenAI drop-in cannot select deferred. Only sw.jobs.create (or POST /v1/jobs) can.
Checklist
- Create a key, fund the wallet, verify the account email (uploads 403 with
EMAIL_UNVERIFIEDuntil you do). - Set
baseURL/base_urltohttps://api.speechweave.com/v1and the API key toSPEECHWEAVE_API_KEY. - Pass
model: "core"(or leavewhisper-1, it aliases). - Confirm a short clip returns
.text. - Hit
GET /v1/limitsbefore you assume a file will fit the proxy path. - When you outgrow 90 MB, blocking calls, or sync pricing, install the native SDK.
More detail lives in the OpenAI migration doc. Deepgram and AssemblyAI have the same idea with their own helpers.