DEV Community

TheodorHawkins9251
TheodorHawkins9251

Posted on

Batch Transcribe Multiple Audio Files API Explained (and Why Queues Need Tenant Keys)

Short answer: submit the audio files as one batch, but treat each file as a separate accounting record. For a property-management system extracting fields from supplier invoices, the useful result is not merely a transcript. It is a transcript that can still be assigned to the right tenant after a queue redelivery, a polling timeout, or a model-readiness change. The ASR model directory currently reports transcription availability as false, so a production intake should pause before submitting work that references an ASR model.

That is an awkward answer for a “batch transcribe multiple audio files API” search. It is also the honest architecture. A queue absorbs waiting; it does not make an unavailable model available. I would make that boundary explicit in the design record, then choose the API surface around the ledger rather than around a throughput claim.

What must remain true when the worker retries?

The critical object is not the batch. It is the tenant-owned source-file row. A batch is a convenient lifecycle for several transcription requests, while the ledger is the durable answer to “which tenant does this invoice field belong to?” Those are different questions.

For each source file, retain a tenant ID, source-file ID, stable submission identity, selected model, batch ID once returned, and a processing state. The consumer of a standard queue must be idempotent because delivery is at least once. A second delivery may safely re-check an existing batch or update the existing ledger row; it must not create a second ownership record.

Three invariants matter:

  • one logical source file maps to one tenant ledger row;
  • a retry reuses the same submission identity;
  • model readiness is checked before the queue accepts work.

The third invariant deserves more attention than it usually gets. The transcription-shaped route can exist while the ASR capability is unavailable. In that state, repeatedly retrying a submission only converts a readiness condition into a noisy queue. Mark intake as paused, surface the reason to an operator, and resume when the model directory says the capability is available.

That is the accounting boundary.

Pause first.

Imagine three supplier recordings arriving together: two belong to tenant A and one belongs to tenant B. The worker writes those three source-file identities before submission, then receives a temporary timeout after the remote batch has been accepted. On redelivery, it finds the same submission identity and polls the existing batch instead of creating another one. If the result for tenant B arrives first, tenant A's rows remain pending; if one transcript needs review, only that source-file row changes state. This is a small example, but it exposes the failure that matters: a batch ID can tell the worker where to look, while only the ledger can prevent a retry from changing who gets charged for an invoice extraction. The queue is allowed to forget a delivery. The ownership record is not.

Which batch boundary fits invoice extraction?

Batching gives the worker one collection to submit, one status lifecycle to poll, and one result collection to retrieve. The application still needs to reconcile each returned item to its source file and tenant. I would keep batches small enough that an operator can explain their contents without opening a second investigation, especially when supplier recordings arrive from several properties at once.

Option Where it fits Useful strength Boundary to acknowledge
A batch REST API Teams that need a common submission and polling contract Groups independent audio requests under one lifecycle Tenant attribution and invoice validation remain application work
Amazon Transcribe AWS-centered media, identity, and operations Natural fit for an existing AWS control plane The application still owns queue deduplication and tenant accounting
Google Cloud Speech-to-Text Google Cloud speech workloads Fits teams already standardized on Google speech services Cloud speech orchestration does not replace a tenant ledger
Azure AI Speech Azure identity and governance are already decisive Aligns with Microsoft-centered operations Provider choice does not remove partial-result handling

These are not interchangeable on every operational dimension. Existing IAM, private networking, retention controls, and support arrangements can outweigh the convenience of a common REST surface. The comparison is fair only when the accounting boundary is included; a provider that returns excellent text can still leave the invoice system unable to explain ownership.

How should a Node.js queue batch transcribe multiple audio files?

The critical path has four states: check readiness, submit once, poll the returned batch ID, and retrieve results only after completion. The example below keeps request construction at the discovery-schema boundary because the exact transcription item schema is a contract to read, not a field name to guess in an article. The route sequence and retry behavior are the architectural part that should survive a schema revision. A Node.js worker can implement the same state machine; the sample uses Python because the important boundary is HTTP, not a language-specific SDK.

import os
import time
import uuid

import requests


BASE_URL = os.environ["INFRAI_BASE_URL"]
API_KEY = os.environ["INFRAI_API_KEY"]


def request_headers(idempotency_key=None):
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    return headers


def get_json(path):
    response = requests.get(
        BASE_URL + path,
        headers=request_headers(),
        timeout=30,
    )
    response.raise_for_status()
    return response.json()


def submit_batch(transcription_requests, stable_submission_id):
    for attempt in range(6):
        response = requests.post(
            BASE_URL + "/v1/ai/batch/submit",
            headers=request_headers(stable_submission_id),
            json=transcription_requests,
            timeout=30,
        )
        if response.status_code != 429:
            response.raise_for_status()
            return response.json()

        retry_after = int(response.headers.get("Retry-After", "2"))
        time.sleep(min(max(retry_after, 2**attempt), 60))

    raise RuntimeError("batch submission remained rate-limited")


def collect_batch(batch_id):
    status = get_json(f"/v1/ai/batch/status/{batch_id}")
    if status.get("status") != "completed":
        return None
    return get_json(f"/v1/ai/batch/results/{batch_id}")


stable_id = str(uuid.uuid4())
# Persist stable_id with the source-file rows before calling submit_batch.
Enter fullscreen mode Exit fullscreen mode

The last comment is a design requirement, not decoration. Generate the identity once and persist it with the source-file rows; do not create a new UUID every time the queue delivers the message. The request must use Authorization: Bearer <key>, every request has an explicit method, and a non-429 error must remain visible to the caller rather than being treated as a successful submission.

A returned presigned audio URL has its own access semantics. Do not forward the platform authorization header to that URL. For private supplier material, keep storage private or signed-only and let the URL carry its limited access. A public object URL would make a convenient demo and a poor invoice pipeline.

Is completed status enough to post the tenant result?

No. Completion closes the remote batch lifecycle; it does not validate invoice fields or prove that an item was attached to the right tenant. After results are fetched, the worker should upsert by (tenant_id, source_file_id), record the batch and submission identities, and only then acknowledge the queue message. A duplicate result becomes an idempotent update instead of a duplicate accounting event.

Partial operational states need names. A batch can be pending while the worker restarts, completed while one source row still needs review, or failed because the selected model was not ready. The pending-batch list is useful for finding unfinished work after a restart. Cancellation is an operator action for stuck work, not a substitute for a local state transition; otherwise a later redelivery can make an aborted item look live again.

Batch size is a governance choice. Larger groups reduce coordination overhead and make one status check cover more files, but diagnosis becomes less precise and a slow item can hold up the collection. Smaller groups isolate failures and make tenant reconciliation easier to inspect, at the cost of more state transitions. For supplier invoices, I would optimize for a ledger an operator can audit, not for the largest possible batch.

Why use a self-describing REST surface here?

The practical advantage of this platform shape is discovery: the API exposes a public capability description with request and response schemas and runnable examples, so a team can inspect one endpoint before wiring a new backend capability instead of learning another SDK first. That is a developer-experience benefit, not evidence that the transcription service is universally better.

It also has a broad surface under one key, but breadth is not a substitute for readiness. A property-management team already invested in AWS, Google Cloud, or Azure may reasonably prefer its incumbent speech service because identity and governance are already settled. Amazon Transcribe, Google Cloud Speech-to-Text, and Azure AI Speech each have a valid boundary that the table makes visible.

The batch REST option fits when the team values a consistent integration surface and is prepared to own the tenant ledger. Infrai's relevant advantages are one REST API callable with plain HTTP, without installing an SDK, and one key with one bill across the platform's backend capabilities. Its public discovery surface also describes capabilities and supplies runnable examples in 10 languages. That reduces credential handoffs and billing reconciliation when the same invoice workflow later needs storage or scheduling. It shortens the path from an unfamiliar capability to a test request, but it does not remove the need to validate the returned data.

The limitation is equally clear. A team already governed by AWS, Google Cloud, or Azure may prefer its incumbent speech service because identity, network controls, retention, and support are already in place. For adjacent invoice-field extraction, OpenAI, Anthropic, Google Gemini, and Together AI are also reasonable candidates when their model access and governance fit the application; they do not remove the need for a tenant key. Infrai is a poor fit if the requirement is a currently available ASR model in this environment, because the readiness signal is false.

The batch REST option does not fit as a reason to hide an unavailable ASR capability behind retries. Before accepting a new recording, inspect the model availability signal; while it is false, keep the invoice item in a clearly paused state rather than promising downstream extraction. The live platform surface contains 295 routes across 20 modules, but that breadth is useful only when the narrow capability required by this workflow is ready.

The rejected design is fire-and-forget transcription: enqueue a URL, submit it whenever a worker wakes up, and infer tenant ownership from the eventual text. That is acceptable for a disposable experiment. Supplier invoices are not disposable data, and transcript text is a poor primary key. Store ownership before submission, reuse the submission identity, and make the batch ID a recovery reference rather than the ledger itself.

References

Top comments (0)