Skip to content

Pre-recorded (async): result_url returned by create() is malformed when region is set — following it fails with 400 #45

Description

@oz-casavi

Environment

  • gladiaio-sdk (Python) 1.0.3 and 1.0.4 (latest) — both affected
  • Python 3.13
  • Region: eu-west

Summary

When a GladiaClient is constructed with region=..., the pre-recorded (async) flow via create() + result_url is broken: the result_url returned by POST /v2/pre-recorded is malformed, and following it fails with 400 Bad Request.

Cause chain:

  1. pre_recorded_v2() builds an HttpClient whose default query params include region, which is attached to every request — including POST /v2/pre-recorded (v2/prerecorded/client.py, network/http_client.py).
  2. The API appears to construct result_url from the request URL, so the job id is appended after the query string:
    https://api.gladia.io/v2/pre-recorded?region=eu-west/{id} — malformed.
  3. Any consumer that follows result_url (the documented REST contract for async jobs) gets 400.

The SDK's own poll()/transcribe() mask the bug because they rebuild the poll URL from the job id ({base}/v2/pre-recorded/{id}) instead of using result_url — so this bites exactly the users driving the async flow manually via create() + result_url (e.g. storing result_url and polling from another worker).

Steps to reproduce

import os
import httpx
import gladiaio_sdk as gladia

API_KEY = os.environ["GLADIA_API_KEY"]
AUDIO = "http://files.gladia.io/example/audio-transcription/split_infinity.wav"

print("=== WITH region='eu-west' ===")
client = gladia.GladiaClient(api_key=API_KEY, region="eu-west")
prerecorded = client.pre_recorded_v2()
init = prerecorded.create({"audio_url": AUDIO})
print("result_url:", init.result_url)
resp = httpx.get(init.result_url, headers={"x-gladia-key": API_KEY})
print("GET result_url ->", resp.status_code)

print("=== CONTROL: no region ===")
client2 = gladia.GladiaClient(api_key=API_KEY)
init2 = client2.pre_recorded_v2().create({"audio_url": AUDIO})
print("result_url:", init2.result_url)
resp2 = httpx.get(init2.result_url, headers={"x-gladia-key": API_KEY})
print("GET result_url ->", resp2.status_code)

Actual output (live, 2026-07-16, sdk 1.0.3):

=== WITH region='eu-west' ===
result_url: https://api.gladia.io/v2/pre-recorded?region=eu-west/f4c62e54-d1cd-4a65-bb87-0c1681a36391
GET result_url -> 400
=== CONTROL: no region ===
result_url: https://api.gladia.io/v2/pre-recorded/1c574c82-ba8c-4ebb-8de2-56d0d3fd18a9
GET result_url -> 200

Expected

result_url should be a valid, pollable URL regardless of region, e.g.
https://api.gladia.io/v2/pre-recorded/{id}?region=eu-west (or without the param).

Impact / workaround

  • Consumers following result_url from the create response fail with 400 whenever region is set.
  • Workaround: ignore result_url and poll {base}/v2/pre-recorded/{id} built from id (what the SDK's poll() does internally).
  • Fix could be server-side (build result_url from the path only, placing query params after the id) and/or SDK-side (don't attach the default region query param in a way that ends up reflected into result_url).

Related note: silent auth behavior change 0.4.0 → 1.0.x

In 0.4.0, GladiaClient(api_key=...).options.http_headers contained the auth header, and downstream code could reuse it for raw HTTP calls. Since 1.0.x it is always {}x-gladia-key is injected only per-request inside _merge_options(). Code that read options.http_headers now silently sends unauthenticated requests (401). If this is intended, a changelog note would help; we lost a few hours to the combination of that 401 and the malformed result_url above.

Happy to provide more details or test a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions