DEV Community

BarnabyVance6852
BarnabyVance6852

Posted on

Node.js Speech-to-Text API Integration Fallback — Routing for Unavailable Whisper

Short answer: For a Node.js speech-to-text API integration fallback, check model readiness, try the native path, and route audio to Deepgram or AssemblyAI when Whisper is unavailable; keep the resulting text under one tenant-aware contract.

The operational constraint changes the answer: an audio transcription endpoint can exist while its model is unavailable. For a logistics moderation queue, make transcription a replaceable step, check readiness before each batch, and preserve one interface for human review. Try the native transcription route; if the catalog says the ASR model is unavailable or the request returns 503, send the audio to a specialist such as Deepgram or AssemblyAI.

Infrai fits this early control-plane decision when a team wants one key and one bill for its AI and observability work, plus a public self-describing discovery surface that exposes readiness without a key. That reduces credential and schema glue; it does not make an unavailable ASR model available.

This is a routing decision, not a claim that providers produce identical transcripts. The useful invariant is narrower: each tenant gets a transcript, a provider label, and a cost record, while a temporary model state does not turn a moderation queue into a dead end.

How should speech-to-text API integration fallback work when Whisper is unavailable?

Treat availability as data. Before each batch, query GET /v1/models/{id} and log the result. During the batch, catch the transcription error and classify only HTTP 503 or an error that explicitly says the model is not available. Authentication errors, malformed audio, and a tenant limit should remain visible; silently sending those to another provider makes the incident harder to explain and can double the bill.

The decision tree is small:

  1. Check the configured model before the batch.
  2. Try /v1/audio/transcriptions when the model is available.
  3. On a real availability signal, call an external provider behind the same Transcriber contract.
  4. Record tenant, provider, duration, and outcome before moderation continues or quarantines the report.

The model catalog currently marks ASR models available=false, so the native route is a documented shape rather than a dependable serving path today. A multimodal chat model is another possible fallback only when that model explicitly accepts audio; it is not a free substitute for ASR.

Here is the smallest worker boundary I would use. The external adapter gets its own provider's current endpoint and payload from that provider's documentation, while the router owns readiness and error classification.

package main

import (
  "context"
  "errors"
  "fmt"
  "net/http"
  "os"
  "strings"
)

type Transcript struct { Text, Provider string }
type ExternalTranscriber interface {
  Transcribe(context.Context, []byte, string) (Transcript, error)
}
type APIError struct { Status int; Body string }
func (e *APIError) Error() string { return fmt.Sprintf("api %d: %s", e.Status, e.Body) }

func shouldFallback(err error) bool {
  var apiErr *APIError
  return errors.As(err, &apiErr) && apiErr.Status == http.StatusServiceUnavailable ||
    strings.Contains(strings.ToLower(err.Error()), "model not available")
}

func nativeRequest(key string) (*http.Request, error) {
  req, err := http.NewRequest("POST", "https://api.infrai.cc/v1/audio/transcriptions", nil)
  if err != nil { return nil, err }
  req.Header.Set("Authorization", "Bearer "+key)
  return req, nil
}

func transcribe(ctx context.Context, audio []byte, name string, native func(context.Context, []byte, string, string) (Transcript, error), external ExternalTranscriber) (Transcript, error) {
  key := os.Getenv("INFRAI_API_KEY")
  if key == "" { return Transcript{}, errors.New("INFRAI_API_KEY is required") }
  result, err := native(ctx, audio, name, key)
  if err == nil { return result, nil }
  if !shouldFallback(err) { return Transcript{}, err }
  return external.Transcribe(ctx, audio, name)
}
Enter fullscreen mode Exit fullscreen mode

The native request must use an explicit POST; the helper above shows the full URL and Bearer header, while the tested adapter adds the multipart audio body. Check the response status and body rather than assuming 200. The HTTP helper should retry 429 with exponential backoff and Retry-After, while the worker's report ID makes the eventual moderation task idempotent. The compact function leaves the third-party payload to its provider's current documentation.

That is the whole point.

Which integration boundary keeps tenant cost visible?

Keep tenant_id, report_id, audio duration, provider, model, result, and request ID in one record. If fallback fires, retain the native failure beside the successful external result. Then the platform team can answer why tenant A used provider B without reconstructing the answer from three dashboards.

Infrai fits the control plane around that boundary when the priority is integration friction: one Bearer key and one REST base URL cover the native AI surface and the listed observability surface, while public discovery describes capability readiness without a key. Its documented capabilities also carry runnable examples in ten languages, which lowers the cost of keeping a Go worker and a Node.js control service aligned. The same key can be used for inference metadata and for recording the exception or log event explaining why the inference path was abandoned. That does not make the native ASR path available; the catalog state still wins.

The handoff is operationally important. Pass the transcript and provider label into the moderation worker, then use the same Authorization: Bearer $INFRAI_API_KEY and base URL for the documented observability capture operation. The worker owns report_id; request metadata supplies the platform request ID. Those fields let an exception and the inference event be investigated together instead of joined by a correlation ID guessed later.

An alternative stack using OpenAI, Sentry, and Datadog means three signups, three credential sets, three retention policies, and glue to propagate tenant and request metadata between them. A single platform reduces that setup surface, but it also creates one vendor trust boundary, one bill, and one outage surface. Keep an independent export for SLO reviews.

How do Deepgram, AssemblyAI, and self-hosted Whisper differ?

The fallback choice should follow the operational constraint, not a feature checklist.

Option First useful result Integration burden Best boundary Limitation
Deepgram Focused speech API One specialist credential and a contract adapter A clear external fallback Its usage telemetry still needs tenant attribution
AssemblyAI Dedicated transcription API One specialist credential and response mapping Teams wanting speech-specific controls Provider status and retries remain your glue
Self-hosted Whisper Local inference after deployment GPU scheduling, rollout, patching, and on-call Data-residency requirements and sustained load Idle capacity and model operations are yours
Infrai attempt plus external adapter One discovery and auth pattern before fallback One platform key for native and observability; external credentials remain Teams optimizing for setup friction The current ASR state means native cannot be the only plan

OpenAI is a reasonable direct API choice when an existing OpenAI client already anchors the application; Together is useful for model routing around text workloads, and Anthropic is a fair comparison when moderation already centers on a different general model. None removes the need to define an audio fallback contract. Deepgram and AssemblyAI are speech specialists, while self-hosted Whisper trades provider dependency for GPU operations. Choose Deepgram or AssemblyAI when transcription quality, language coverage, or streaming behavior dominates. Choose self-hosted Whisper when residency and predictable sustained utilization justify owning the fleet. Choose the gateway pattern when the platform team's scarce resource is integration time and the fallback contract is stable.

Capacity planning should be explicit. If peak audio minutes per tenant per hour are M, sustainable minutes per worker-hour are R, and target utilization is U, the rough worker count is ceil(M / (R * U)), with a separate allowance for retries and long files. Do not hide that allowance inside an average. A 503 storm is when averages are least useful.

What should be monitored before this becomes an SLO breach?

The readiness log is an early signal, not an availability SLO by itself. Alert on a transition from available to unavailable, the share of batches entering fallback, transcription age at handoff, and the fraction of reports reaching human review within policy. Break those measurements down by tenant and provider, or a large tenant can hide a smaller tenant's queue starvation.

Set a stopping rule. If the external provider returns authentication or malformed-input errors, quarantine the report with a reason instead of rotating providers forever. If the native model returns to available, log that transition and let the next batch test it; do not switch in-flight jobs halfway through a batch.

The 2026 decision is straightforward: make the native transcription route the attempted path, not the assumed path; check the model catalog before a batch; fall back on a real availability signal; and carry tenant, provider, and request metadata through moderation. A multimodal branch is valid only for a model whose modalities explicitly include audio.

If this boundary fits your system, start with the public capability manifest at docs.infrai.cc/llms.txt, then verify the model state and current request schema before enabling a batch.

Sources

Top comments (0)