API Reference
Every route on the platform, with its parameters, a runnable request and what comes back.
Every route, in one page. The endpoint headers elsewhere in these docs and the blocks below read from the same definition, so a route cannot be described as live on one page and planned on another. The cURL, JavaScript and Python snippets are generated from the sample body shown beneath each route. They are the request, not a paraphrase of it.
Conventions
| ASPECT | VALUE |
|---|---|
| Base URL | https://api.routehook.ai/v1 |
| Authentication | Authorization: Bearer <key> |
| Anthropic route | x-api-key is accepted on POST /v1/messages |
| Request encoding | application/json |
| Response encoding | application/json, or text/event-stream when streaming |
| Idempotency | Idempotency-Key on a queue submit returns the existing job |
| Errors | { error: { code, message, request_id } } |
GET/v1/keyAvailable
Describe the key making the call: its label, spend cap, lifetime usage and rate limit.
The route describes the key that called it. There is no way to read another key with this one. limit_reset, is_management_key and byok_usage are published as explicit constants rather than omitted. A client reading them gets a definite answer instead of a missing key it cannot tell from a typo. key_id is the key's own id, unprefixed; it is the id the dashboard shows and the one to quote in a support report.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/key \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": {
"label": "Production",
"limit": null,
"usage": 128.4419,
"limit_remaining": null,
"is_provisioning_key": false,
"is_free_tier": false,
"rate_limit": { "requests": 600, "interval": "60s" },
"limit_reset": null,
"is_management_key": false,
"byok_usage": 0,
"key_id": "3xK9mQ2p7vL4nR8t",
"account_id": "acct_9f2c41be",
"created_at": "2026-05-01T09:14:22Z",
"last_used_at": "2026-08-23T11:02:47Z",
"expires_at": null
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/creditsAvailable
Read the account balance, the amount held by requests in flight and what is left to spend.
Figures here are JSON numbers, rounded to two decimal places. Per-token *rates* elsewhere are strings. See /docs/pricing for why.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/credits \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": {
"total_credits": 250.0,
"total_usage": 128.44,
"balance": 121.56,
"held": 0.42,
"available": 121.14,
"credit_limit": 0,
"currency": "USD"
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/generationAvailable
Read what one request cost and how long it took.
reference_value is what the same call costs at the vendor's list price. It is the only number here you have to compare against; what the platform pays upstream is never published.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| id | string | required | - | The request id returned in the X-Routehook-Request-Id header, or on an error body. |
Request
curl https://api.routehook.ai/v1/generation?id=req_7c41d9be \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": {
"id": "req_7c41d9be",
"model": "openai/gpt-4o-mini",
"streamed": false,
"generation_time": 812,
"created_at": "2026-08-23T11:02:47Z",
"tokens_prompt": 9,
"tokens_completion": 8,
"native_tokens_prompt": 9,
"native_tokens_completion": 8,
"num_media_prompt": 0,
"num_media_completion": 0,
"origin": "api",
"usage": 0.000103,
"total_cost": 0.000103,
"cache_discount": 0,
"finish_reason": "stop",
"latency": 812,
"moderation_latency": 0,
"request_id": "req_7c41d9be",
"status": "succeeded",
"error_code": null,
"attempts": 1,
"fallback_used": false,
"reference_value": 0.000245
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
POST/v1/chat/completionsAvailable
Chat completions in OpenAI's shape, streamed or whole, with automatic failover.
Unknown request fields are forwarded upstream rather than refused, so a vendor parameter this gateway has never heard of still works.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| messages | array | required | - | Ordered turns, each with a role of system, user or assistant, and content. |
| stream | boolean | optional | false | Emit server-sent events instead of one body. See /docs/streaming. |
| max_tokens | integer | optional | model default | Ceiling on generated tokens. Caps the output side of the bill. |
| temperature | number | optional | model default | Sampling randomness. Forwarded upstream unchanged. |
Request
curl -X POST https://api.routehook.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}'
Response
{
"id": "chatcmpl_5f81c0",
"object": "chat.completion",
"created": 1786312455,
"model": "openai/gpt-4o-mini",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hello. How can I help?" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 8,
"total_tokens": 17,
"cost": "0.000103"
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/completionsAvailable
OpenAI's legacy prompt completion, translated onto the same routing as chat.
Almost nothing upstream still serves a true completion endpoint. The prompt is wrapped as one user message on the way out and the message content unwrapped back to text on the way in.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| prompt | string | string[] | required | - | The prompt, or a batch of them. A batch answers with one choice per prompt, in order. |
| max_tokens | integer | optional | model default | Ceiling on generated tokens. |
| stream | boolean | optional | false | Emit server-sent events instead of one body. |
| temperature | number | optional | model default | Sampling randomness. Forwarded upstream unchanged. |
Request
curl -X POST https://api.routehook.ai/v1/completions \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"prompt": "Write one sentence about harbour fog.",
"max_tokens": 64
}'
Response
{
"id": "cmpl_5f81c0",
"object": "text_completion",
"created": 1786312455,
"model": "openai/gpt-4o-mini",
"choices": [
{
"index": 0,
"text": "Fog rolled off the water and swallowed the cranes one by one.",
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 8,
"completion_tokens": 14,
"total_tokens": 22,
"cost": "0.0000038"
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/messagesAvailable
Anthropic's Messages API, so an Anthropic client works against this base URL unchanged.
The key may arrive as x-api-key as well as a bearer token, so an Anthropic client needs no header rewriting. model on the way back is the gateway slug, never the upstream's.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| messages | array | required | - | Anthropic's turns. Role user or assistant, and content. |
| max_tokens | integer | required | - | Required by the Messages API, unlike chat completions. There is no default to fall back on. |
| system | string | optional | - | System prompt, sent apart from the turns. |
| stream | boolean | optional | false | Emit Anthropic's event framing: message_start through message_stop. |
| stop_sequences | string[] | optional | - | Strings that end generation when produced. |
| tools | array | optional | - | Tool definitions, in Anthropic's schema. |
Request
curl -X POST https://api.routehook.ai/v1/messages \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4",
"max_tokens": 256,
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}'
Response
{
"id": "msg_5f81c0",
"type": "message",
"role": "assistant",
"model": "anthropic/claude-sonnet-4",
"content": [{ "type": "text", "text": "Hello. How can I help?" }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": { "input_tokens": 9, "output_tokens": 8 }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/images/generationsAvailable
Generate images and return their URLs. The connection is held until the images exist.
The connection is held for the 20–60 seconds a generation takes. Video cannot work this way. Use the queue. Batching with n is a latency optimisation, not a discount.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| prompt | string | required | - | What to generate. Up to 32,000 characters. |
| n | integer | optional | 1 | Images per request, 1 to 10. Each bills at the unit rate. |
| size | string | optional | model default | Dimensions (1024x1024) or an aspect ratio (16:9). Advisory. Translated to whatever the model expects. |
| reference_urls | string[] | optional | - | Publicly reachable input images, for models that take references. Ignored by models that do not. |
Request
curl -X POST https://api.routehook.ai/v1/images/generations \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/nano-banana-2",
"prompt": "isometric factory floor, technical illustration",
"size": "1024x1024",
"n": 2
}'
Response
{
"created": 1786312455,
"model": "google/nano-banana-2",
"data": [
{ "url": "https://cdn.routehook.ai/img/9d21f4-0.png" },
{ "url": "https://cdn.routehook.ai/img/9d21f4-1.png" }
]
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/imagesAvailable
The same image generation under OpenRouter's path. One handler, one bill.
The same handler as POST /v1/images/generations, at OpenRouter's spelling of the path. One implementation, one bill, one response shape. Which of the two you call is a matter of which client library you already have.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| prompt | string | required | - | What to generate. Up to 32,000 characters. |
| n | integer | optional | 1 | Images per request, 1 to 10. Each bills at the unit rate. |
| size | string | optional | model default | Dimensions (1024x1024) or an aspect ratio (16:9). Advisory. Translated to whatever the model expects. |
| reference_urls | string[] | optional | - | Publicly reachable input images, for models that take references. Ignored by models that do not. |
Request
curl -X POST https://api.routehook.ai/v1/images \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/nano-banana-2",
"prompt": "isometric factory floor, technical illustration",
"size": "1024x1024",
"n": 2
}'
Response
{
"created": 1786312455,
"model": "google/nano-banana-2",
"data": [
{ "url": "https://cdn.routehook.ai/img/9d21f4-0.png" },
{ "url": "https://cdn.routehook.ai/img/9d21f4-1.png" }
]
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/embeddingsAvailable
Turn text into vectors for search, clustering and retrieval.
Only models in the embedding category are accepted; anything else is refused with invalid_request before a hold is taken. Vectors are truncated in this example. Real ones are the model's full width.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| input | string | string[] | number[][] | required | - | One string, a batch of strings, or pre-tokenised input to embed in a single call. |
| encoding_format | float | base64 | optional | float | How each vector is encoded in the response. |
| dimensions | integer | optional | model default | Truncate vectors to this width, where the model supports it. |
| user | string | optional | - | Opaque end-user identifier, forwarded upstream. |
Request
curl -X POST https://api.routehook.ai/v1/embeddings \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/text-embedding-3-small",
"input": [
"first document",
"second document"
]
}'
Response
{
"object": "list",
"model": "openai/text-embedding-3-small",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0021, -0.0138, 0.0074] },
{ "object": "embedding", "index": 1, "embedding": [0.0019, -0.0142, 0.0069] }
],
"usage": { "prompt_tokens": 6, "total_tokens": 6 }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/responsesAvailable
OpenAI's Responses API, served by every text model on the platform rather than one vendor's.
Translated onto a chat completion rather than forwarded, so every text model in the catalogue answers here, not only the one vendor that implements this route. previous_response_id is refused rather than ignored: this gateway stores no conversations, and a reply that had silently forgotten the earlier turns would be worse than an error saying so.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| input | string | object[] | required | - | A prompt, or the conversation so far as a list of role/content items. |
| instructions | string | optional | - | The system prompt. Sent as its own turn, not folded into the first user message. |
| max_output_tokens | integer | optional | model default | Ceiling on the answer. |
| stream | boolean | optional | false | Emit the answer as server-sent events. |
| tools | object[] | optional | - | Function definitions, in the same shape chat takes. |
| tool_choice | string | object | optional | - | auto, none, required, or a named function. |
| parallel_tool_calls | boolean | optional | - | Allow more than one tool call in a single turn. |
| temperature | number | optional | - | 0 to 2. Forwarded unchanged. |
| top_p | number | optional | - | 0 to 1. Forwarded unchanged. |
| metadata | object | optional | - | Echoed back on the response object rather than forwarded upstream. It changes neither routing nor price. |
| store | boolean | optional | - | Accepted and ignored. This gateway stores no conversations, so there is nothing for it to switch on. |
| previous_response_id | string | optional | - | Refused with 400 rather than ignored, for the reason in the note below. |
| user | string | optional | - | An opaque end-user id, forwarded upstream. |
Request
curl -X POST https://api.routehook.ai/v1/responses \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"input": "Say exactly: parity works",
"max_output_tokens": 20
}'
Response
{
"id": "resp_A81FB265",
"object": "response",
"created_at": 1787654321,
"status": "completed",
"error": null,
"incomplete_details": null,
"model": "openai/gpt-4o-mini",
"output": [
{
"type": "message",
"role": "assistant",
"content": [{ "type": "output_text", "text": "parity works", "annotations": [] }]
}
],
"output_text": "parity works",
"instructions": null,
"max_output_tokens": 20,
"temperature": null,
"top_p": null,
"parallel_tool_calls": true,
"tool_choice": "auto",
"tools": [],
"metadata": {},
"usage": { "input_tokens": 12, "output_tokens": 3, "total_tokens": 15 }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/audio/speechAvailable
Turn text into spoken audio and stream the bytes back.
The response is audio, not JSON. The bytes are streamed rather than buffered, so a long document does not sit in memory on either side. Billing is per character of input, and the charge is on X-Routehook-Cost because there is no body to put it in.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| input | string | required | - | The text to speak. Capped at 4096 characters, which is also the axis this route is billed on. |
| voice | string | optional | alloy | Vendor voice id. Not an enum here. A new voice should not need a release from us to be usable. |
| response_format | mp3 | opus | aac | flac | wav | pcm | optional | mp3 | Container for the returned audio. |
| speed | number | optional | 1.0 | Playback rate, 0.25 to 4.0. |
Request
curl -X POST https://api.routehook.ai/v1/audio/speech \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/tts-1",
"input": "Routehook audio is live.",
"voice": "alloy"
}'
Response
HTTP/1.1 200 OK
Content-Type: audio/mpeg
X-Routehook-Cost: 0.00045
<binary audio>
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/audio/transcriptionsAvailable
Transcribe audio to text, with optional word and segment timestamps.
Billed per second of audio. Where a duration is reported that number is used; where it is not, the upload is measured and the request row records that the figure was estimated rather than metered.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| input_audio | object | required | - | `{ data, format }`: base64 audio, with or without a data: prefix, and the container it is in. |
| language | string | optional | - | ISO-639-1 hint. Improves both accuracy and latency when it is known. |
| response_format | json | text | srt | verbose_json | vtt | optional | json | Shape of the transcript. Non-JSON formats are returned verbatim. |
| timestamp_granularities | string[] | optional | - | `word` and/or `segment`. Only meaningful with verbose_json. |
Request
curl -X POST https://api.routehook.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-transcribe",
"input_audio": {
"data": "UklGRiQAAABXQVZF...",
"format": "mp3"
}
}'
Response
{
"text": "Routehook audio is live."
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/rerankAvailable
Score a set of documents against a query and return them in relevance order.
index is the position in the request's document list, not in this response, so a reordered result still points at what you sent. Billed on the query plus the documents.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Model slug, as returned by GET /v1/models. |
| query | string | required | - | What the documents are being scored against. |
| documents | string[] | object[] | required | - | Up to 1000 documents, as plain strings or as objects carrying a `text` field. |
| top_n | integer | optional | all | Return only the highest-scoring N. |
| return_documents | boolean | optional | false | Echo each document back beside its score. |
Request
curl -X POST https://api.routehook.ai/v1/rerank \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "cohere/rerank-v3.5",
"query": "how do holds work",
"documents": [
"A hold reserves the ceiling before the call.",
"Invoices are issued monthly."
],
"top_n": 2
}'
Response
{
"id": "req_9f2c41be",
"model": "cohere/rerank-v3.5",
"results": [
{ "index": 0, "relevance_score": 0.97 },
{ "index": 1, "relevance_score": 0.04 }
],
"usage": { "total_tokens": 24 }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/queue/{model}Available
Submit a job and return immediately with the id and the URLs that follow it.
Everything in the body other than the fields above is the model's own input and is passed through. Money is held at submit and settled when the job reaches a terminal state.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Path parameter, and a wildcard. The whole slug, slashes included. |
| webhook_url | string | optional | - | https URL to POST the terminal state to. `?fal_webhook=` is accepted for the same purpose. |
| prompt | string | optional | - | Up to 32,000 characters. Read by the gateway for the request record, and forwarded. |
| n | integer | optional | - | 1 to 10. Multiplies the reservation and the bill. |
| size | string | optional | - | Vendor size string, forwarded as sent. |
| duration | number | optional | - | Seconds, 1 to 600. The axis a video model is billed on, so it also sizes the hold. |
| Idempotency-Key | header | optional | - | Repeating a submit with the same key returns the existing job instead of opening a second one. |
Request
curl -X POST https://api.routehook.ai/v1/queue/fal/veo3 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "drone shot over a harbour at dawn",
"duration": 5,
"webhook_url": "https://your.app/hooks/routehook"
}'
Response
{
"request_id": "job_ab12cd34",
"status": "IN_QUEUE",
"queue_position": 3,
"status_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/status",
"response_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34",
"cancel_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/cancel"
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
GET/v1/queue/requests/{job_id}/statusAvailable
Poll a job: queue position, terminal outcome, timings and optional logs.
status carries fal's three values only (IN_QUEUE, IN_PROGRESS, COMPLETED), so a client polling until COMPLETED terminates. A job that failed or was cancelled is COMPLETED here and says which in outcome. queue_position is null once the job has left the queue rather than 0, because a running job reporting 0 reads as about to start. metrics is in seconds. Fal's unit, and the one place this API does not suffix a duration _ms. GET /v1/queue/{model}/requests/{job_id}/status is the same handler.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
| logs | 0 | 1 | optional | 0 | Include the job's log lines in the response. |
Request
curl https://api.routehook.ai/v1/queue/requests/job_ab12cd34/status?logs=1 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"status": "IN_PROGRESS",
"outcome": null,
"queue_position": null,
"logs": [
{ "message": "sampling frame 12/120", "level": "INFO", "timestamp": "2026-08-23T11:02:47Z" }
],
"metrics": { "inference_time": null, "queue_time": 4.1 },
"error": null
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/queue/requests/{job_id}Available
Fetch the output of a finished job.
Check the status code. While the job is still running this route answers 202 with the status body — request_id, status, queue_position — not with the result above; 200 means finished. A client that parses the body without looking at the code reads a running job as a result with no payload. payload is the upstream's own result body, forwarded verbatim rather than normalised, so there is one shape per model. A job that failed answers with the standard error envelope and the failure's status code, not with a 200 carrying an error field. GET /v1/queue/{model}/requests/{job_id} is the same handler.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl https://api.routehook.ai/v1/queue/requests/job_ab12cd34 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"model": "fal/veo3",
"created": 1786312455,
"outcome": "succeeded",
"payload": {
"video": { "url": "https://cdn.routehook.ai/vid/ab12cd34.mp4" }
},
"gateway_request_id": "req_7c41d9be",
"cost": "0.7500",
"reference_value": "1.5000",
"usage": { "video_seconds": "5" }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
PUT/v1/queue/requests/{job_id}/cancelAvailable
Cancel a job that has not finished and release its hold.
Cancelling releases the hold. The call is idempotent and answers 200 even for a job that had already finished: canceled is false there, and exists for the audit trail rather than for control flow. outcome is null in the window between the cancellation being recorded and the worker abandoning an attempt already in flight upstream.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl -X PUT https://api.routehook.ai/v1/queue/requests/job_ab12cd34/cancel \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"status": "COMPLETED",
"outcome": "canceled",
"canceled": true
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/queue/requests/{job_id}/streamAvailable
Server-sent stream of a job's status transitions, instead of polling.
The response is text/event-stream; the object above is one event's data: payload. The stream ends when the job reaches a terminal state. Use it instead of a polling loop, not alongside one.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl https://api.routehook.ai/v1/queue/requests/job_ab12cd34/stream \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"status": "IN_PROGRESS",
"outcome": null,
"queue_position": 0
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/queue/{model}/requests/{job_id}/statusAvailable
The same poll, at fal's URL shape. The model segment is ignored.
fal's path shape for GET /v1/queue/requests/{job_id}/status, reaching the same handler. It exists so a client that kept fal's URLs works without rewriting them; a new integration should use the short form.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Path parameter, and a wildcard. Matched and then ignored — the job id alone identifies the job, and any slug here is accepted. |
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
| logs | 0 | 1 | optional | 0 | Include the job's log lines in the response. |
Request
curl https://api.routehook.ai/v1/queue/fal/veo3/requests/job_ab12cd34/status?logs=1 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"status": "IN_PROGRESS",
"outcome": null,
"queue_position": null,
"logs": [
{ "message": "sampling frame 12/120", "level": "INFO", "timestamp": "2026-08-23T11:02:47Z" }
],
"metrics": { "inference_time": null, "queue_time": 4.1 },
"error": null
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/queue/{model}/requests/{job_id}Available
The same result, at fal's URL shape.
fal's path shape for GET /v1/queue/requests/{job_id}, reaching the same handler — including the 202 while the job is still running. It exists so a client that kept fal's URLs works without rewriting them; a new integration should use the short form.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Path parameter, and a wildcard. Matched and then ignored — the job id alone identifies the job, and any slug here is accepted. |
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl https://api.routehook.ai/v1/queue/fal/veo3/requests/job_ab12cd34 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"model": "fal/veo3",
"created": 1786312455,
"outcome": "succeeded",
"payload": {
"video": { "url": "https://cdn.routehook.ai/vid/ab12cd34.mp4" }
},
"gateway_request_id": "req_7c41d9be",
"cost": "0.7500",
"reference_value": "1.5000",
"usage": { "video_seconds": "5" }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
PUT/v1/queue/{model}/requests/{job_id}/cancelAvailable
The same cancel, at fal's URL shape.
fal's path shape for PUT /v1/queue/requests/{job_id}/cancel, reaching the same handler. It exists so a client that kept fal's URLs works without rewriting them; a new integration should use the short form.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Path parameter, and a wildcard. Matched and then ignored — the job id alone identifies the job, and any slug here is accepted. |
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl -X PUT https://api.routehook.ai/v1/queue/fal/veo3/requests/job_ab12cd34/cancel \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"status": "COMPLETED",
"outcome": "canceled",
"canceled": true
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
POST/v1/run/{model}Available
Submit a job and hold the connection until it finishes or the wait ceiling is reached.
Identical to submitting and polling, with the wait done for you. Past the platform's wait ceiling the call returns the job so you can poll it. The work is not thrown away.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Path parameter, and a wildcard. The whole slug, slashes included. |
Request
curl -X POST https://api.routehook.ai/v1/run/fal/veo3 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "drone shot over a harbour at dawn",
"duration": 5
}'
Response
{
"request_id": "job_ab12cd34",
"model": "fal/veo3",
"created": 1786312455,
"outcome": "succeeded",
"payload": {
"video": { "url": "https://cdn.routehook.ai/vid/ab12cd34.mp4" }
},
"gateway_request_id": "req_7c41d9be",
"cost": "0.7500",
"reference_value": "1.5000",
"usage": { "video_seconds": "5" }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/videos/generationsAvailable
Submit a video generation and get a job id back. Answers 202: the work runs for minutes.
202, not 200. The name looks synchronous and the behaviour is not: video generation runs for minutes and no connection is held for it. Poll status_url, or GET /v1/videos/{job_id}, which is the same job under a shorter path. Everything in the body other than the fields above is the model's own input and is forwarded untouched.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Body field, not a path segment. This route has none to carry it, unlike POST /v1/queue/{model}. |
| prompt | string | optional | - | Up to 32,000 characters. Read by the gateway for the request record, and forwarded. |
| duration | number | optional | - | Seconds, 1 to 600. The axis a video model is billed on, so it also sizes the hold. |
| size | string | optional | - | Vendor size string, forwarded as sent. |
| n | integer | optional | - | 1 to 10. Multiplies the reservation and the bill. |
| webhook_url | string | optional | - | https URL to POST the terminal state to. `?fal_webhook=` is accepted for the same purpose. |
| Idempotency-Key | header | optional | - | Repeating a submit with the same key returns the existing job instead of opening a second one. |
Request
curl -X POST https://api.routehook.ai/v1/videos/generations \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "fal/veo3",
"prompt": "drone shot over a harbour at dawn",
"duration": 5
}'
Response
{
"request_id": "job_ab12cd34",
"status": "IN_QUEUE",
"queue_position": 3,
"status_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/status",
"response_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34",
"cancel_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/cancel"
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
POST/v1/videosAvailable
The same submit under OpenRouter's path.
The same handler as POST /v1/videos/generations, at OpenRouter's spelling of the path. 202, not 200 — poll status_url or GET /v1/videos/{job_id}.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| model | string | required | - | Body field, not a path segment. This route has none to carry it, unlike POST /v1/queue/{model}. |
| prompt | string | optional | - | Up to 32,000 characters. Read by the gateway for the request record, and forwarded. |
| duration | number | optional | - | Seconds, 1 to 600. The axis a video model is billed on, so it also sizes the hold. |
| size | string | optional | - | Vendor size string, forwarded as sent. |
| n | integer | optional | - | 1 to 10. Multiplies the reservation and the bill. |
| webhook_url | string | optional | - | https URL to POST the terminal state to. `?fal_webhook=` is accepted for the same purpose. |
| Idempotency-Key | header | optional | - | Repeating a submit with the same key returns the existing job instead of opening a second one. |
Request
curl -X POST https://api.routehook.ai/v1/videos \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "fal/veo3",
"prompt": "drone shot over a harbour at dawn",
"duration": 5
}'
Response
{
"request_id": "job_ab12cd34",
"status": "IN_QUEUE",
"queue_position": 3,
"status_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/status",
"response_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34",
"cancel_url": "https://api.routehook.ai/v1/queue/requests/job_ab12cd34/cancel"
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 409 | model_unavailable | Model is listed but has no live endpoint to route to. |
GET/v1/videos/{job_id}Available
Poll a video job: its status while it runs, its result once it finishes.
The queue's result route under a shorter path, and the same handler. While the job is running it answers 202 with the status body — request_id, status, queue_position — and once it finishes, 200 with the result above. Poll it until the status code changes. The finished video's bytes are at GET /v1/videos/{job_id}/content.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| job_id | string | required | - | Path parameter. The request_id returned by the submit call. |
Request
curl https://api.routehook.ai/v1/videos/job_ab12cd34 \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"request_id": "job_ab12cd34",
"model": "fal/veo3",
"created": 1786312455,
"outcome": "succeeded",
"payload": {
"video": { "url": "https://cdn.routehook.ai/vid/ab12cd34.mp4" }
},
"gateway_request_id": "req_7c41d9be",
"cost": "0.7500",
"reference_value": "1.5000",
"usage": { "video_seconds": "5" }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/videos/{job_id}/contentAvailable
Stream the finished video's bytes.
The same proxy as GET /v1/files/{id}, reached by job id instead of by media reference. A job that has not finished answers 400 rather than 404: the bytes are on their way, and a client told 'not found' would stop polling.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/videos/job_ab12cd34/content \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
HTTP/1.1 200 OK
Content-Type: video/mp4
Accept-Ranges: bytes
<binary media>
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | not_found | No such id, or it belongs to another account. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 410 | gone | The reference expired. Permanent. Do not retry. |
GET/v1/files/{id}Available
Fetch generated media by reference. Proxied at request time, never redirected upstream.
The origin copy is fetched at request time and streamed through. It never redirects: a 302 would publish the upstream URL, which is the one thing the indirection exists to hide. References expire in minutes, and an expired one answers 410 rather than a retryable code.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/files/file_9f2c41be \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
HTTP/1.1 200 OK
Content-Type: video/mp4
Accept-Ranges: bytes
Cache-Control: private, max-age=280, must-revalidate
<binary media>
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 404 | not_found | No such id, or it belongs to another account. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 410 | gone | The reference expired. Permanent. Do not retry. |
GET/v1/jobsAvailable
List this account's jobs, newest first, keyset-paged.
This route is ours rather than fal's (fal has no job list), so it is the one place to find a job whose id was lost. A row carries no payload: fifty jobs' worth of upstream JSON is a megabyte of response to render a table, so the output stays behind response_url. cost is null until the hold is settled. An open job has been reserved against, not charged.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| status | string | optional | all | Filter to queued, running, succeeded, failed or canceled. The stored vocabulary, not the three wire statuses. |
| model | string | optional | all | Filter to one model slug, matched exactly. |
| category | string | optional | all | Filter to one modality: text, image, video, audio, embedding or rerank. |
| cursor | string | optional | - | Keyset cursor. The `next_cursor` value from the previous page. |
| limit | integer | optional | 50 | Rows per page, 1 to 200. |
Request
curl https://api.routehook.ai/v1/jobs?limit=50&status=running \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"request_id": "job_ab12cd34",
"model": "fal/veo3",
"category": "video",
"status": "IN_PROGRESS",
"outcome": null,
"queue_position": null,
"gateway_request_id": "req_7c41d9be",
"cost": null,
"error_code": null,
"created_at": "2026-08-23T11:02:47Z",
"started_at": "2026-08-23T11:02:51Z",
"completed_at": null
}
],
"next_cursor": null
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/modelsAvailable
Every model with its category, status, published price and per-token rates.
Anonymous and cached, no key needed. Per-token rates are strings, because a client that sums a million float64 rates drifts. The headline price stays a number.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/models \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "openai/gpt-4o-mini",
"canonical_slug": "openai/gpt-4o-mini",
"name": "GPT-4o mini",
"category": "text",
"status": "available",
"unit": "per 1M tokens",
"price": 0.15,
"reference_price": 0.30,
"context_length": 128000,
"pricing": {
"prompt": "0.00000015",
"completion": "0.0000006",
"request": null,
"image": null
},
"supported_parameters": ["tools", "temperature", "max_tokens"]
}
]
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/models/countAvailable
How many models are servable right now.
Counts models whose status is available. Everything listed but not yet servable — coming_soon — is excluded, so this is the number of slugs a request can actually route to rather than the length of GET /v1/models.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/models/count \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": { "count": 214 }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/models/userAvailable
The catalogue as this caller sees it.
Returns the same catalogue as GET /v1/models. OpenRouter narrows this route by an account's provider preferences; there is no such per-account filtering here — every published model is available to every key — so the honest answer is the whole catalogue rather than a stub. The route exists so a client that calls it gets a catalogue instead of a 404, and it will narrow on its own the day per-account preferences do.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/models/user \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "openai/gpt-4o-mini",
"canonical_slug": "openai/gpt-4o-mini",
"name": "GPT-4o mini",
"category": "text",
"status": "available",
"unit": "per 1M tokens",
"price": 0.15,
"reference_price": 0.30,
"context_length": 128000,
"pricing": {
"prompt": "0.00000015",
"completion": "0.0000006",
"request": null,
"image": null
},
"supported_parameters": ["tools", "temperature", "max_tokens"]
}
]
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/model/{author}/{slug}Available
One model by slug. Singular 'model'. OpenRouter's spelling for this route.
Singular model, which is OpenRouter's spelling for this route and not a typo for the plural one. An unknown slug answers 404 with model_unavailable rather than not_found — note the code, and see the header of this file for why the two differ.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| author/slug | string | required | - | Path parameter, and two segments. Catalogue slugs are namespaced, so `openai/gpt-4o-mini` is the whole of it. |
Request
curl https://api.routehook.ai/v1/model/openai/gpt-4o-mini \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": {
"id": "openai/gpt-4o-mini",
"canonical_slug": "openai/gpt-4o-mini",
"name": "GPT-4o mini",
"category": "text",
"status": "available",
"unit": "per 1M tokens",
"price": 0.15,
"reference_price": 0.30,
"context_length": 128000,
"architecture": {
"modality": "text->text",
"input_modalities": ["text"],
"output_modalities": ["text"],
"tokenizer": null,
"instruct_type": null
},
"pricing": {
"prompt": "0.00000015",
"completion": "0.0000006",
"request": null,
"image": null
},
"supported_parameters": ["tools", "temperature", "max_tokens"],
"alias_target": null,
"reasoning": { "supported": false },
"discount": null
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
| 404 | model_unavailable | No such id, or it belongs to another account. Note the code. This route does not answer not_found. |
GET/v1/embeddings/modelsAvailable
The catalogue narrowed to embedding models, with a total count.
Note the envelope: total_count sits beside data here, and does not on GET /v1/models. That is OpenRouter's shape for the narrowed listings and ours matches it per route, so a client written against theirs parses this without a branch. Anonymous and cached.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/embeddings/models \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "openai/text-embedding-3-small",
"name": "Text Embedding 3 Small",
"category": "embedding",
"status": "available",
"unit": "per 1M tokens",
"price": 0.02,
"reference_price": 0.02,
"pricing": { "prompt": "0.00000002", "completion": null, "request": null, "image": null },
"supported_parameters": ["dimensions", "encoding_format"]
}
],
"total_count": 6
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/images/modelsAvailable
The catalogue narrowed to image models.
Note the envelope: total_count sits beside data here, and does not on GET /v1/models. That is OpenRouter's shape for the narrowed listings and ours matches it per route, so a client written against theirs parses this without a branch. Anonymous and cached.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/images/models \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "google/nano-banana-2",
"name": "Nano Banana 2",
"category": "image",
"status": "available",
"unit": "per image, 1024x1024",
"price": 0.039,
"reference_price": 0.04,
"pricing": { "prompt": null, "completion": null, "request": null, "image": "0.039" },
"supported_parameters": ["size", "n", "reference_urls"]
}
],
"total_count": 24
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/videos/modelsAvailable
The catalogue narrowed to video models.
Note the envelope: total_count sits beside data here, and does not on GET /v1/models. That is OpenRouter's shape for the narrowed listings and ours matches it per route, so a client written against theirs parses this without a branch. Anonymous and cached. Every model listed here is served through the queue, not through a synchronous route — see /docs/queue.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/videos/models \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "fal/veo3",
"name": "Veo 3",
"category": "video",
"status": "available",
"unit": "per 5s clip, 720p",
"price": 0.75,
"reference_price": 1.5,
"pricing": { "prompt": null, "completion": null, "request": "0.75", "image": null },
"supported_parameters": ["duration", "size", "prompt"]
}
],
"total_count": 18
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/catalogAvailable
The discovery catalogue behind the public site: names, blurbs, families and headline rates.
camelCase, unlike every other /v1 route, because this one feeds the public site rather than an OpenAI-shaped client. Build against /v1/models instead unless you are rendering a catalogue.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/catalog \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"data": [
{
"id": "openai/gpt-4o-mini",
"slug": "openai-gpt-4o-mini",
"name": "GPT-4o mini",
"description": "Small, fast general-purpose text model.",
"category": "text",
"status": "available",
"unit": "per 1M tokens",
"price": 0.15,
"reference": 0.30,
"speed": "fast",
"featured": true,
"capabilities": ["tools", "vision"],
"contextLength": 128000,
"addedAt": "2026-05-01T09:14:22Z"
}
],
"generatedAt": "2026-08-23T11:02:47Z"
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/statsAvailable
Platform totals: accounts, tokens billed over the trailing 30 days, providers and live models.
Four counts and nothing derived from them. tokens is input plus output over the trailing thirty days; cached-input and reasoning tokens are excluded on purpose, so the figure matches what an account would recognise from its own usage page. Cached for five minutes.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/stats \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"accounts": 1284,
"tokens": 91744812,
"providers": 17,
"models": 214,
"generatedAt": "2026-08-23T11:02:47Z"
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |
POST/v1/mcpAvailable
JSON-RPC 2.0 over Streamable HTTP: initialise a session and call the tools.
The initialize reply carries an Mcp-Session-Id header. Send it on every later call. notifications/initialized answers 202 with no body. Tools that spend money say so in their own description.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| jsonrpc | string | required | - | Always "2.0". |
| method | string | required | - | initialize, tools/list, tools/call, resources/list, resources/read or prompts/get. |
| params | object | optional | - | Method arguments, per the MCP specification. |
| MCP-Protocol-Version | header | optional | 2025-06-18 | Negotiated protocol version. 2025-06-18, falling back to 2025-03-26. |
Request
curl -X POST https://api.routehook.ai/v1/mcp \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "your-client",
"version": "1.0.0"
}
}
}'
Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": { "listChanged": false },
"resources": { "subscribe": false, "listChanged": false },
"prompts": {}
},
"serverInfo": { "name": "routehook", "version": "1.0.0" }
}
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter outside its bounds. |
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 402 | insufficient_credits | Top up the balance before retrying. |
| 429 | rate_limited | Back off using the Retry-After header. |
| 503 | upstream_unavailable | Upstream outage. Safe to retry. |
| 200 | -32601 | JSON-RPC error, not an HTTP one: unknown method. Transport errors stay HTTP. |
GET/v1/mcpAvailable
Open the server-sent stream for server-initiated MCP messages.
text/event-stream; the object above is one event's data: payload. Only server-initiated messages arrive here. Tool calls go over POST. A session id this server does not know answers 404 carrying a JSON-RPC -32600, not the gateway's { error: { code } } envelope: an MCP client is a JSON-RPC client, and the specification makes a 404 on a request bearing a session id mean "that session is gone, call initialize again".
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| Mcp-Session-Id | header | required | - | The session id returned by initialize. |
Request
curl https://api.routehook.ai/v1/mcp \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"jsonrpc": "2.0",
"method": "notifications/message",
"params": { "level": "info", "data": "job_ab12cd34 finished" }
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 429 | rate_limited | Back off using the Retry-After header. |
DELETE/v1/mcpAvailable
End an MCP session.
There is no response body. Ending a session drops its stream; the API key itself is untouched. An unknown session id answers 404 with a JSON-RPC -32600 rather than the gateway envelope, exactly as on the stream above.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| Mcp-Session-Id | header | required | - | The session to end. |
Request
curl -X DELETE https://api.routehook.ai/v1/mcp \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
204 No Content
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 401 | invalid_api_key | Key missing, malformed or revoked. |
| 429 | rate_limited | Back off using the Retry-After header. |
GET/v1/statusAvailable
Platform health: component states, current incidents and recent uptime.
camelCase, unlike the OpenAI-shaped routes. overall and each component's status are upper case (OPERATIONAL, DEGRADED, DOWN or UNCONFIGURED), while a history day's state is lower case: operational, degraded, down or none. They are different vocabularies and a client that lower-cases one to match the other will be wrong about the day it did it to. There are four components, keyed api, providers, storage and payments, and history carries one entry per day for the whole 90-day window, oldest first (trimmed to one above). monitored is false when no probe has ever recorded a sample, which is a different claim from "everything is fine" and must not render the same way. uptime is a 0–1 fraction or null, never a percentage. The route deliberately keeps answering during maintenance (a status page that 503s while the platform is down is worse than none), and publishes measurements only: no vendor names, no topology.
Parameters
| PARAM | TYPE | REQUIRED | DEFAULT | DESCRIPTION |
|---|
Request
curl https://api.routehook.ai/v1/status \
-H "Authorization: Bearer $ROUTEHOOK_API_KEY"
Response
{
"overall": "OPERATIONAL",
"monitored": true,
"windowDays": 90,
"generatedAt": "2026-08-23T11:02:47Z",
"components": [
{
"key": "api",
"name": "API",
"description": "The gateway and the dashboards behind it.",
"status": "OPERATIONAL",
"uptime": 0.9997,
"latencyMs": 84,
"latencyMeaning": "Median round trip to the database.",
"sampleCount": 129600,
"checkedAt": "2026-08-23T11:02:41Z",
"history": [
{ "date": "2026-05-26", "state": "operational", "uptime": 1, "sampleCount": 1440 }
]
}
]
}
Errors
| STATUS | CODE | MEANING |
|---|---|---|
| 429 | rate_limited | Back off using the Retry-After header. |