Skip to navigation

Rate Limits & Concurrency

Concurrency limits for each plan, how they're counted, and how to see how much you're using.

Murf provides dedicated request capacity based on your plan. Each plan includes specific limits for concurrency and WebSocket connections. As your application scales, you can upgrade your plan to increase capacity.

Every Falcon 2.1 streaming response also tells you which region served it, that region’s limit, and how many slots you’re using right now. See Check your usage.

Limits by plan

Below is a summary of the concurrent request limits for each plan:

ModelFree tierPay as you goEnterprise
Falcon 2.15 for US-East;
2 for global and regional endpoints
5 for US-East;
2 for global and regional endpoints
Custom Support Up to 10,000 concurrent calls
Gen255Custom support
Speech to Text (Beta)520 by default; up to 150 on request (contact us)Custom (150+)

Speech to Text limits are separate from Text to Speech limits. Usage of one doesn’t count against the other.

How concurrency is counted

Concurrency for Streaming (Falcon 2.1)

Our TTS API supports streaming via both HTTP and WebSocket connections. Concurrency for streaming is the number of requests or contexts active at the same time, counted per region:

  1. HTTP streaming: each in-flight request uses one slot until its audio finishes.
  2. WebSocket streaming: each active context_id uses one slot until the context ends. More text sent to the same context_id doesn’t use another slot, because requests to the same context are processed in order. Text sent without a context_id goes to the default context, which also counts as one.

Usage in one region doesn’t count against another. If you go over the limit, see When you hit the limit.

Concurrency for Synthesize (Gen 2)

Concurrency refers to the maximum number of generation requests that can be processed simultaneously. For all non-streaming endpoints, this is defined as the number of active requests at any given time.

WebSocket Limits

Two limits apply to WebSocket usage. They are counted separately: open connections do not consume or increase streaming concurrency.

LimitWhat it countsYour planExample (N = 5)
Streaming concurrencyContext IDs actively generating audio at the same timeN5
WebSocket connectionsSockets open at the same time, whether idle or active10 × N50

An open socket does not consume concurrency. Concurrency is consumed only while audio is being generated on that socket.

The number of sessions that can generate audio at the same time is capped by your streaming concurrency—not by your connection count. The larger connection allowance lets you keep sockets open and avoid a new handshake for every call; it does not increase throughput.

  • A WebSocket connection closes automatically after 3 minutes of inactivity.
  • If you attempt to open a new WebSocket connection after exceeding your limit, an error will be returned.

Concurrency for Speech to Text

Speech to Text (Beta) streams over a WebSocket. Finch-Indic connects to wss://in.api.murf.ai/v1/transcription/stream-input. Finch-Global connects to wss://us-east.api.murf.ai/v1/transcription/stream-input. Concurrency is the number of Speech to Text connections open at the same time for your API key. Unlike Text to Speech, each open connection uses one slot until it closes, whether or not you’re sending audio.

The WebSocket limits above apply to Text to Speech only. If you’re already at your Speech to Text limit, a new connection is closed with code 4004. See Speech to Text: close code 4004.

Regions and the Global URL

Falcon 2.1 limits apply per region. Responses name the serving region with its cloud region code:

HostRegion value
us-east.api.murf.aius-east-2
us-west.api.murf.aius-west-2
in.api.murf.aiap-south-1
ca.api.murf.aica-central-1
kr.api.murf.aiap-northeast-2
jp.api.murf.aiap-northeast-1
au.api.murf.aiap-southeast-2
eu-central.api.murf.aieu-central-1
uk.api.murf.aieu-west-2
sa-east.api.murf.aisa-east-1

global.api.murf.ai sends each request to the nearest region, so your limit depends on where the request came from. A request from Bangalore lands in ap-south-1 (India, limit 2), not us-east-2 (limit 5). Responses always name the region that actually served the request, never global. To use a specific region’s limit, call that region’s host directly.

Check your usage

You get your usage in the responses you already receive. There’s no extra endpoint to call.

HTTP streaming headers

Every successful response and every 429 from POST /v1/speech/stream carries these headers. They arrive before the first audio byte.

HeaderExampleMeaning
X-Murf-Regionap-south-1Region that served the request
X-Murf-Concurrency-Limit2Your concurrency limit in that region
X-Murf-Concurrency-Used1Slots in use in that region, including this request
X-Murf-Concurrency-Remaining1limit minus used
# -D - prints the response headers; the audio goes to speech.wav
curl -sS -D - -o speech.wav -X POST https://global.api.murf.ai/v1/speech/stream \
-H "api-key: $MURF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Thanks for calling. How can I help you today?",
"voiceId": "Matthew",
"model": "falcon-2.1"
}'

Browser JavaScript can read these headers too; they’re listed in Access-Control-Expose-Headers. Don’t put your API key in browser code; see API key safety.

WebSocket messages

On every final message. When a context finishes, its final message carries a concurrency object. used counts the active contexts in that region, including the one that just finished. Clients that ignore unknown fields need no change.

{
"context_id": "turn_1",
"concurrency": { "region": "ap-south-1", "limit": 2, "used": 1 },
"final": true
}

At the start of each context (opt-in). Add include_usage=true to the connection URL to also get a session_info message when Murf starts processing each context, before its first audio chunk:

wss://global.api.murf.ai/v1/speech/stream-input?api-key=YOUR_API_KEY&model=falcon-2.1&include_usage=true
{
"type": "session_info",
"region": "ap-south-1",
"concurrency": { "limit": 2, "used": 1 }
}

session_info has no audio field, so it’s off by default: clients that assume every non-final message carries audio keep working. Turn it on only when your message handler checks for it:

msg = json.loads(await ws.recv())
if msg.get("type") == "session_info":
usage = msg["concurrency"]
print(f'Context started in {msg["region"]}: {usage["used"]}/{usage["limit"]}')
elif "audio" in msg:
... # play or save the chunk
elif msg.get("final"):
usage = msg.get("concurrency")
...

On connect. The handshake response carries the same four X-Murf-* headers. used is the number of contexts you already have active in that region; opening a connection doesn’t use a slot. Server-side clients can read these headers. Browsers don’t let JavaScript read handshake headers, so browser apps should use session_info or final.

async with websockets.connect(url) as ws:
headers = ws.response.headers # websockets 14+; older versions: ws.response_headers
print(headers.get("X-Murf-Region"), headers.get("X-Murf-Concurrency-Used"))

Python SDK support for reading usage is coming soon. Until then, use the HTTP or WebSocket examples above.

When you hit the limit

HTTP streaming: 429

A request over the limit gets HTTP 429 with the same four headers, a Retry-After header in seconds, and this body:

{
"error_code": 429,
"error_message": "Concurrency limit reached, please upgrade your plan or contact sales.",
"error": "concurrency_limit_exceeded",
"region": "ap-south-1",
"limit": 2,
"message": "Concurrency limit reached in region 'ap-south-1'. Retry shortly or request a higher limit."
}

Branch on error, not on the message text. To handle it:

  • Wait Retry-After seconds, then retry. Add jitter if many workers retry at once.
  • Cap your in-flight requests at the limit for the region in X-Murf-Region.
  • If you hit 429 often, call us-east.api.murf.ai directly (it has the highest default limit), or contact sales for a higher limit.
import time
response = requests.post(URL, headers=HEADERS, json=BODY, stream=True)
if response.status_code == 429:
error = response.json()
wait = int(response.headers.get("Retry-After", "1"))
print(f"Limit {error['limit']} reached in {error['region']}; retrying in {wait}s")
time.sleep(wait)
response = requests.post(URL, headers=HEADERS, json=BODY, stream=True)

WebSockets: over the limit

If a new context_id would go over the limit, you get an ACTIVE_CONTEXT_LIMIT_EXCEEDED error frame for that context:

{
"error": "Exceeded Active context limit",
"context_id": "turn_3",
"trace_id": "f26546c5-6874-43ef-b764-f0ae2be02737",
"error_code": "ACTIVE_CONTEXT_LIMIT_EXCEEDED",
"fatal": true,
"concurrency": { "region": "ap-south-1", "limit": 2, "used": 2 }
}

The connection is then closed with code 4029, which stops every context on it, including ones that were within the limit. Keep the number of active contexts below limit, close finished turns with {"end": true}, and handle close code 4029 by reconnecting.

A context’s slot is freed shortly after its final message, not at the same moment. If you run at your limit, wait for final plus a short margin before starting the next context.

See Errors & Warnings for every error and close code.

Speech to Text: close code 4004

If you open a new Speech to Text connection while you’re at your limit, the server sends an Error message and closes the connection with code 4004. To handle it:

  • Retry with exponential backoff.
  • Keep one connection per call, and use Finalize between turns instead of reconnecting.
  • On pay as you go, contact us to raise your limit from 20 to up to 150. For more than 150, ask about Enterprise.

See Speech to Text errors for every close code.

FAQs

The Global URL routes each request to the nearest region, and limits are per region. Check X-Murf-Region to see which region served a request. To use one region’s limit consistently, call that region’s host directly.

No. Only active contexts use slots. Connections have their own, larger allowance; see WebSocket Limits.

Not yet. Usage comes back on the requests you make.

A new context went over your concurrency limit in that region. The error frame before the close names the region and limit. Reconnect, and keep fewer contexts active at once.

Why These Limits Matter

The limits are designed to maintain system performance and ensure a consistent experience for all users. By adhering to the limits and following best practices, you can integrate the Murf API smoothly and efficiently into your applications. If you have additional questions or need guidance on managing API limits, please drop a message in our discord channel or contact our support team.