View as Markdown

Hyperouter API

Version v1 · Updated October 25, 2026

These docs are available as Markdown for SI assistants. Start at /llms.txt, or fetch /llms-full.txt for every page in one file. This page is /docs.md.

Hyperouter is one HTTP API in front of many model vendors. You send a request that names a model. Hyperouter picks a provider that can serve it, forwards the request, and charges your balance at that provider’s list price.

BASEhttps://api.hyperouter.app/v1

Every endpoint follows OpenAI’s request and response shapes. The OpenAI SDKs work when you change the base URL and the API key. Hyperouter adds a few fields to OpenAI’s bodies. They are marked as additions in this reference.

EndpointPurposeAuth
GET /v1/modelsList the models your account can call.API key
POST /v1/chat/completionsChat completion, with optional streaming.API key
POST /v1/images/generationsText to image.API key
POST /v1/images/editsEdit pictures.API key
POST /v1/videosCreate a video from a prompt, with an optional picture.API key
POST /v1/videos/editsEdit or upscale a video.API key
POST /v1/videos/extensionsExtend a video.API key
POST /v1/videos/{id}/remixMake a new version of a video with a new prompt.API key
GET /v1/videos, GET /v1/videos/{id}, GET /v1/videos/{id}/content, DELETE /v1/videos/{id}List, read, download and delete videos.API key
GET /v1/generations/{id}Read the record of any request: status, usage and charge.API key
GET /v1/creditsYour balance, what open requests hold, and what is left to spend (balance_usd, held_usd, available_usd, decimal USD strings).API key

Sign in at dash.hyperouter.app with Google or GitHub to create an API key. GET /v1/models returns the models you can call at the time of the request.

Quickstart

  1. Sign in and create an API key. Keys start with sk-hr-v1- and are shown once.
  2. Store it in an environment variable, for example HYPEROUTER_API_KEY.
  3. Add credits: open a top-up request in the dashboard. Staff credit your balance when the payment is confirmed.
  4. Point any OpenAI SDK at the base URL above and pick a model.
curl https://api.hyperouter.app/v1/chat/completions \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "x-ai/grok-4.7",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Authentication

Send your API key as a bearer token on every request.

Authorization: Bearer sk-hr-v1-...

Keys carry access to your balance. Keep them on your server. Never put them in browser or mobile code. Hyperouter stores only a hash of each key, so a lost key cannot be shown again: create a new one.

In the dashboard you can name each key, give it a credit limit, and disable it. A disabled key stops working at once. A request that would take a key past its limit is refused with 402 insufficient_credits.

Authentication errors

StatustypecodeWhenmessage
401authentication_errorinvalid_api_keyThe header is missing, is not Bearer, or the key does not start with sk-hr-v1-.The API key is missing or malformed. Send Authorization: Bearer sk-hr-v1-…
401authentication_errorinvalid_api_keyThe key is unknown or disabled.The API key is not valid.
403permission_erroraccount_suspendedThe account is suspended. The check runs on every request.This account is suspended. Open a support ticket.
429rate_limit_errortoo_many_auth_failuresMore than 30 requests from your address in a minute used a missing or invalid key. Every /v1 request from the address is refused for 15 minutes, a valid key included. Retry-After gives the seconds left.Too many failed API key checks from this address. Try again after the time in Retry-After.
500server_errorinternalHyperouter could not check the key.An internal error occurred. Try again.
curl https://api.hyperouter.app/v1/generations/0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21 \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY"
{ "error": { "message": "The API key is not valid.", "type": "authentication_error", "code": "invalid_api_key", "param": null } }

Errors

Every error uses OpenAI’s error object and a matching HTTP status.

{
  "error": {
    "message": "n must be an integer from 1 to 128",
    "type": "invalid_request_error",
    "code": "invalid_body",
    "param": "n"
  }
}
FieldTypeDescription
messagestringA sentence that says what is wrong. Do not parse it.
typestringThe class of error. See the list below.
codestringA stable identifier for the cause. Match on this field.
paramstring | nullThe request field at fault, or null.

Types and statuses

StatustypeMeaning
400invalid_request_errorThe request is invalid. code names the cause and param the field.
401authentication_errorThe API key is missing or not valid.
402insufficient_creditsYour balance, or the key’s limit, cannot cover the most the request could cost.
403permission_errorThe account is suspended.
404not_foundNo such id, the id belongs to another account, or a video has passed its expires_at.
409conflictThe object is not in a state that allows the call (video_not_finished), or an Idempotency-Key conflict (see Retries and idempotency).
422invalid_request_errorThe request was valid but its answer was refused (expansion_refused). Not billed.
429rate_limit_errorEvery provider for the model is at capacity, your account is over its rate limits, or too many API key checks from your address failed. Retry after the time in the Retry-After header.
500, 501server_errorHyperouter failed, or the endpoint is not implemented. Not billed.
503upstream_errorA provider failed or did not answer in time: code says which (all_venues_failed, upstream_error or timeout). The header X-Upstream-Status carries 502 (failed) or 504 (timed out).

Hyperouter never answers 502 or 504: those would reach you as a bare page from our edge network, without the error body. Where a gateway would answer them, you get 503 with the body below and the gateway status in X-Upstream-Status.

Codes you can get

codeStatusWhen
invalid_body400A body field or query parameter does not parse, has the wrong type, or is outside its allowed values. param names it.
unsupported_content_type400/v1/images/generations got a multipart/form-data body. Send JSON. param is null.
request_too_large413A multipart/form-data upload is over 50 MiB. Send large media as https URLs.
unknown_model400The model does not exist for this endpoint, or no provider can serve it with your provider preferences.
unsupported_parameter400A parameter that exists for other models is not available on this model. See Model-specific parameters.
invalid_combination400The vendor does not allow these parameters together, for example reference media with keyframes.
invalid_size400size is not WIDTHxHEIGHT, or is not allowed for the model.
too_many_images400n or the number of input pictures is over the limit.
invalid_image, invalid_video400A picture or video URL is not https to a public host: a data: URL, an http:// URL, an IP address or an internal name. param names the field.
image_required400The model needs an input picture and you sent none (or it only edits, and you called generations).
invalid_seconds400seconds is not allowed for the model, or is missing for a video given by URL.
unsupported_operation400The model does not support the operation (edit, extend or remix, or picture editing).
video_not_ready400The source video has not completed.
context_length_exceeded400The chat prompt and max_tokens do not fit in the model’s context.
content_rejected400The vendor’s safety filter refused the input.
vendor_rejected400The model refused the request as invalid. Check its parameters and inputs.
clip_too_long400A reference clip plus the requested seconds run past the model’s limit (30 s). Not billed.
prompt_rejected400The prompt asks the prompt expansion model for its instructions. Not billed. See Prompt expansion.
expansion_refused422The prompt expansion model's rewrite was refused or came back empty. Not billed. See Prompt expansion.
invalid_api_key401See Authentication.
insufficient_credits402See Billing & limits.
account_suspended403The account is suspended.
moderation_not_enabled403moderation: "low" is not enabled for the account. See Moderation.
feature_not_enabled403prompt_expand is not enabled for the account, on a model that offers it. Open a support ticket to request access. Nothing is held or sent to the provider. See Prompt expansion.
model_not_enabled403The model needs access granted to your account. Open a support ticket to request it. Nothing is held or sent to the provider.
not_found404No such object.
video_not_finished409You deleted a video that is still being made.
venue_busy429Every provider for the chat model is over its rate budget.
rate_limited429Your account is over its requests per minute or its concurrent requests. See Rate limits.
too_many_auth_failures429More than 30 requests from your address in a minute used an invalid API key. See Rate limits.
all_venues_failed503No provider is healthy, or all of them failed.
upstream_error503An image or video job failed at the model. You are not charged.
internal, server_error, no_price500Hyperouter failed (no_price: the model cannot be priced right now). Not billed.
not_implemented501This endpoint is not implemented yet.
invalid_idempotency_key400The Idempotency-Key header is longer than 255 characters or is not printable ASCII. See Retries and idempotency.
idempotency_key_reused409The Idempotency-Key was already used with a different request.
idempotency_key_in_use409A request with this Idempotency-Key is still being started. Retry with the same key.
idempotency_key_replayed409A chat completion with this Idempotency-Key already ran. error.generation_id is its id.
timeout503A sync image was not ready within 90 seconds. It is still being made: error.generation_id is the id to poll.

Retries and idempotency

A network error or a timeout does not tell you whether Hyperouter received the request. If you send it again, you may start a second job and pay twice. An Idempotency-Key makes a repeat safe: Hyperouter recognises the repeat, starts no second job, places no second hold, and answers from the first request.

Send one on every request that costs money. Generate a new random value, such as a UUID v4, for each new request. Keep it with the request, and send the same value every time you send that request again.

Sending a key

The header is accepted on these endpoints:

EndpointA repeat returns
POST /v1/images/generations, POST /v1/images/editsthe first request's image, or its 202 with the same id under Prefer: respond-async
POST /v1/videos, /v1/videos/edits, /v1/videos/extensions, /v1/videos/{id}/remixthe first request's Video object, as it is now
POST /v1/chat/completions409 idempotency_key_replayed with the first request's id (see Chat completions)

The OpenAI SDKs do not send this header on their own. Pass it as an extra header; the SDKs then re-send it unchanged on their automatic retries.

curl https://api.hyperouter.app/v1/images/generations \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -d '{"model": "x-ai/grok-imagine-image", "prompt": "A lighthouse at dusk", "n": 1}'

What a repeat returns

You sendHyperouter answers
A new keyRuns the request as usual.
The same key and the same request, after the first was acceptedThe first request's result as in the table above. No second job, no second hold, no second charge.
The same key and the same request, while the first is still being acceptedWaits for the first, then answers as above. If the first has not started its job within 60 seconds: 409 idempotency_key_in_use. Retry with the same key.
The same key and a different request409 idempotency_key_reused. Nothing runs. Use a new key.
The same key, after the first was refused before any job started (a 400, a 402, or a 5xx sent without x-should-retry: false)Runs the request as new, so you can correct it and send it again with the same key.
The same key, more than 30 minutes after the first requestRuns the request as new.
A key longer than 255 characters, or not printable ASCII400 invalid_idempotency_key. Nothing runs.

Rules

Chat completions

Chat replies are not replayed. A repeat with the same key answers 409 with code idempotency_key_replayed, and error.generation_id is the first request's id. The request is not run or billed again. Look the first request up with GET /v1/generations/{id} to see its status, usage and cost. If the first chat completion failed and was not billed, its key is freed, and a repeat runs as a new request.

{
  "error": {
    "message": "A request with this Idempotency-Key already ran as generation 019a1f3c-5b7e-7d21-9c4a-2e8f6b0d3a17. Chat replies are not replayed: GET /v1/generations/019a1f3c-5b7e-7d21-9c4a-2e8f6b0d3a17 for its record.",
    "type": "conflict",
    "code": "idempotency_key_replayed",
    "param": null,
    "generation_id": "019a1f3c-5b7e-7d21-9c4a-2e8f6b0d3a17"
  }
}

Automatic retries in the OpenAI SDKs

The OpenAI SDKs retry a 408, 409, 429 or 5xx answer twice by default, unless the answer carries the response header x-should-retry: false. Hyperouter sends x-should-retry: false on every answer where a retry would start a second job or a second charge:

It never sends it on a 429, on 409 idempotency_key_in_use, or on a 5xx from before any job exists. Those are safe to retry. With an Idempotency-Key, every retry is safe.

Models

GET/v1/models

Lists every model your account can call right now, with its prices and what it supports. A model with no enabled provider is not listed. Some models need access granted to your account: they are listed once it is. This endpoint needs an API key, and lists the models that key's account can call. The answer can be cached for 60 seconds, by your client only.

Parameters

None.

Response

{
  "data": [{
    "id": "x-ai/grok-4.7",
    "name": "Grok 4.7",
    "kind": "chat",
    "context_length": 500000,
    "architecture": { "input_modalities": ["text", "image"], "output_modalities": ["text"] },
    "pricing": {
      "prompt": "0.000002", "completion": "0.000006", "input_cache_read": "0.0000005",
      "request": "0", "image": "0", "video_second": "0"
    },
    "top_provider": { "context_length": 500000, "max_completion_tokens": 32768 },
    "response_modes": ["sync", "stream"],
    "supported_parameters": ["max_tokens", "temperature", "top_p", "stop", "stream", "tools", "tool_choice", "response_format", "seed", "frequency_penalty", "presence_penalty"],
    "extra_parameters": {}
  }]
}
FieldTypeDescription
idstringThe model id, <provider>/<name>, for example x-ai/grok-4.7 or qwen/qwen-image-2.0. Send it as model. A renamed model keeps answering to its old id.
response_modesstring[]How the model answers: sync (one held answer), async (send Prefer: respond-async, get 202, and poll) and stream ("stream": true). A model that lists only async needs the Prefer header, and stream works only where it is listed. Video is always asynchronous through the Videos API, with or without the header.
namestringDisplay name.
kindstringchat, image or video.
context_lengthintegerContext window in tokens. 0 for image and video models.
architectureobjectinput_modalities and output_modalities. Output is ["text"], ["image"] or ["video"].
pricingobject of stringsUSD per unit, as decimal strings, at the base tier: prompt and completion (per token), input_cache_read (per cached token), request, image (per image), video_second (per second of video). A video priced by resolution shows the lowest price. A unit that does not apply is "0".
top_providerobjectcontext_length and max_completion_tokens.
supported_parametersstring[]For chat, the OpenAI parameters the model takes. For image and video, the shared features it takes (resolution, ratio, audio, seed, negative_prompt, first_frame, last_frame, reference_images, reference_videos, prompt_extend).
operationsstring[]Video models only: edit, extend and remix, for each one the model supports. Absent when the model supports none.
extra_parametersobjectThe model-specific top-level fields, by name. Each has type, values (allowed values; empty means any), price_condition (the field changes the price) and description. See Model-specific parameters.
curl https://api.hyperouter.app/v1/models \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY"

Errors

None specific to this endpoint.

Chat models

Prices per 1M tokens: input / cached input / output.

ModelContextPrice
x-ai/grok-4.7500K$2.00 / $0.50 / $6.00
x-ai/grok-4.6500K$2.00 / $0.50 / $6.00
x-ai/grok-4.5500K$2.00 / $0.30 / $6.00
x-ai/grok-4.31M$1.25 / $0.20 / $2.50
x-ai/grok-build-0.1256K$1.00 / $0.20 / $2.00
deepseek/deepseek-v4.1-flash1M$0.15 / $0.003 / $0.60
deepseek/deepseek-v4.1-flash-10171M$0.15 / $0.003 / $0.60
openai/gpt-6-astra272K$10.00 / $1.00 / $50.00
openai/gpt-6.1-sol272K$2.00 / $0.10 / $10.00
z-ai/glm-5.31M$1.40 / $0.26 / $4.40
lumina/face-detect4K$0.0013 per request

On the x-ai/ models, a request with 200,000 prompt tokens or more is billed entirely at the long-context rates, which are twice these. The deepseek/ models have one rate at every prompt length, reason before they answer by default (reasoning_effort: "none" turns it off), and count their reasoning in max_tokens, at most 384,000. z-ai/glm-5.3 always reasons: reasoning_effort is low, high or max, and none is refused. The openai/ models reason too and take prompts of at most 272,000 tokens.

Chat completions

POST/v1/chat/completions

Creates a model response for a conversation. The body is OpenAI’s Chat Completions request. Hyperouter adds models, provider and moderation.

Parameters

NameTypeRequiredAllowed valuesDefaultDescription
modelstringYesA chat model id from GET /v1/modelsThe model to use, for example x-ai/grok-4.7.
messagesobject[]YesNon-empty. Roles system, user, assistant, toolThe conversation so far. Pictures (image_url parts) are accepted where the model takes images.
modelsstring[]NoAt most 5 model idsAddition. Fallback models, tried in order after every provider of model fails.
providerobjectNoSee belowAddition. Routing preferences.
moderationstringNoauto, lowautoAddition. See Moderation.
streambooleanNofalseReturn server-sent events.
stream_optionsobjectNo{"include_usage": true}With include_usage, the stream ends with a chunk that carries usage.
max_tokens, max_completion_tokensintegerNo1 or moreThe model’s maximum outputUpper bound on generated tokens. It also sets the most a request can cost, which Hyperouter holds before the call (see Billing).
nintegerNo1 to 1281Number of choices.
temperature, top_p, seed, frequency_penalty, presence_penalty, logit_biasnumber / integer / objectNoAs OpenAISampling controls, passed to the provider.
stopstring | string[]NoSequences that end generation.
tools, tool_choice, parallel_tool_callsobject[], string | object, booleanNoOpenAI function-calling formatFunction calling. Only tools of type function: any other type is refused with 400 unsupported_parameter.
response_formatobjectNo{"type":"json_object"} or a JSON schemaStructured output.
reasoning_effort, logprobs, top_logprobs, useras OpenAINoPassed to the provider where the model takes them.

Other fields are ignored. supported_parameters of each model in GET /v1/models lists exactly what it takes. Grok 4.20 and newer ignore logprobs and top_logprobs, so they are not listed.

The provider object

By default Hyperouter sends each chat request to the healthy provider with the lowest price for that model, and breaks ties by the lowest recent time to first token. A provider that errors repeatedly is skipped until it recovers. The provider object follows OpenRouter’s request shape.

NameTypeRequiredAllowed valuesDefaultDescription
sortstringNoprice, latency, throughputpriceHow to rank the providers.
orderstring[]NoProvider namesProviders to try first, in order, for example ["xai"]. A provider name is its public name in any case (xAI, Qwen) or the first part of its model ids (x-ai, qwen).
onlystring[]NoProvider namesRoute only to these providers.
ignorestring[]NoProvider namesNever route to these providers.

Response

OpenAI’s chat completion. The id is the request id: look it up with GET /v1/generations/{id}. Hyperouter adds provider (the public name of the provider that filled the request, for example xAI) and usage.cost (the charge, in USD, as a JSON number).

{
  "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21",
  "object": "chat.completion",
  "model": "x-ai/grok-4.7",
  "provider": "xAI",
  "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,
    "prompt_tokens_details": { "cached_tokens": 0 },
    "cost": 0.000066
  }
}

A prompt-rewrite model answers with one choice, a usage of zero tokens and a fixed cost per request.

Streaming

With "stream": true the response is a stream of server-sent events in the OpenAI chunk format. It ends with data: [DONE]. When you set stream_options.include_usage, the last chunk before [DONE] has an empty choices array and a usage object that includes cost, and a provider field. Usage is always collected for billing, whether or not you ask for the chunk.

Until the first token arrives, the stream sends the comment line : processing every 15 seconds to keep the connection open. SSE clients ignore comments.

Fallback to another provider happens only before the first token is sent. If a provider fails mid-stream, the stream ends with data: {"error": {...}} followed by data: [DONE], and you are billed for the tokens already produced.

You are billed for the tokens the provider produces, whether or not you read them. A stream you abort, or a reply you stop waiting for, is not stopped at the provider, which goes on and bills its whole answer: Hyperouter reads it to its end (for at most 5 more minutes) and bills that.

A provider that has accepted a request may be running it. If it then stops answering (5 minutes with no token on a stream, or longer than a reply of max_tokens takes) or its answer breaks off before the first token, the request is not sent to another provider: it answers 503 with code timeout or upstream_error with error.generation_id, and is billed on its hold, with usage_estimated: true on its record.

Example

curl https://api.hyperouter.app/v1/chat/completions \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "x-ai/grok-4.7",
    "models": ["x-ai/grok-4.6"],
    "provider": { "sort": "latency" },
    "messages": [{ "role": "user", "content": "Hello" }]
  }'

Errors

StatustypecodeparamWhen
400invalid_request_errorinvalid_bodythe fieldThe body is not valid JSON or is over 32 MiB; model is missing; models has more than 5 entries; n is outside 1 to 128; max_tokens is not positive; provider.sort is not allowed; moderation is not auto or low (moderation must be auto or low.); messages is empty. Also when every provider refused the request as invalid.
400invalid_request_errorunknown_modelmodel or modelsThe model is not a chat model, or it has no enabled provider.
400invalid_request_errorunsupported_parametertools, response_format, messages or max_tokensThe model does not take tools, a response format, or pictures; a tool is not of type function; or no provider of the model can bound its output.
400invalid_request_errorcontext_length_exceededmessagesThe prompt and max_tokens exceed the model’s context.
402insufficient_creditsinsufficient_creditsmax_tokensThe balance or key limit cannot cover the most the request could cost. The message says how many tokens you can afford.
429rate_limit_errorvenue_busyEvery provider for the model is over its rate budget.
429rate_limit_errorrate_limitedYour account is over its requests per minute or concurrent requests (see Rate limits). Nothing is held or forwarded.
409conflictidempotency_key_replayed, idempotency_key_reused, idempotency_key_in_useSee Retries and idempotency.
503upstream_errorall_venues_failedNo provider is healthy, or all of them failed. Not billed.
503upstream_errorupstream_errorThe provider accepted the request and its answer broke off before the first token. Billed on the hold (see Streaming).
503upstream_errortimeoutThe provider accepted the request and did not finish it in time. Billed on the hold (see Streaming).
401, 403, 500See Authentication.

Images

POST/v1/images/generations
POST/v1/images/edits

Both follow OpenAI’s Images API and take a JSON body. /v1/images/edits takes OpenAI’s JSON form: the pictures are images, 1 to 5 https URLs. The OpenAI SDKs' images.edit sends picture files as multipart/form-data, and that works too: Hyperouter's edge stores each file (PNG, JPEG or WebP, 20 MiB each, 50 MiB a request) for 3 days at an unguessable https://api.hyperouter.app/u/... URL that the provider fetches, then runs the request as JSON. A video file is refused (400 invalid_video): send a video as an https URL. Both take the shared features below and any model-specific parameters as top-level fields.

Parameters

NameTypeRequiredAllowed valuesDefaultDescription
modelstringYesAn image model idThe model to use.
promptstringGenerations: yes. Edits: as the model needsNon-blank textWhat to make.
imagesobject[]Edits: yes1 to 5 objects {"image_url": "https://..."}, as many as the model takes. Each URL is https to a public host, never a data: URL, an IP address or an internal name. file_id is not accepted.The pictures to edit. Each URL is passed to the provider as it is, so the provider must be able to fetch it; one it cannot fetch or read fails the job with vendor_rejected, and nothing is charged. A model that takes them in a set order says so in its notes in the model reference.
nintegerNo1 to 10, and at most the model’s limit1Number of images.
sizestringNoWIDTHxHEIGHT, for example 1024x1024; the model may allow fewer sizes1024x1024Output size.
qualitystringNoAs the model allowsPassed to the model.
response_formatstringNourlurlHow the image comes back: always a URL. b64_json is refused with 400 unsupported_parameter.
seedintegerNoIf the model lists seedSeed for repeatable output.
negative_promptstringNoIf the model lists itWhat to avoid.
prompt_extendbooleanNoIf the model lists itOff unless you askLet the model expand the prompt.
prompt_expandbooleanNoIf the model lists itfalseAddition. Hyperouter rewrites the prompt for the model before it renders. See Prompt expansion.
prompt_expand_modelstringNoA chat model slug your account can callx-ai/grok-4.6Addition. With prompt_expand: the model that writes the rewrite. Without prompt_expand: true it is 400 invalid_body. See Prompt expansion.
localestringNoA BCP 47 tag, for example zh-CNEnglishAddition. With prompt_expand: the language spoken lines are written in, where the model speaks it.
ratiostringNoIf the model lists itAspect ratio.
resolutionstringNo480p, 720p, 1080p, 2K, 4K, as the model allowsOutput resolution.
modelsstring[]NoImage model idsAddition. Fallback models.
providerobjectNoonly, ignore, order (arrays of provider names)Addition. Routing preferences.
moderationstringNoauto, lowautoAddition. See Moderation.
model-specific fieldsas declaredAs declaredAs declaredSee Model-specific parameters.

A shared feature that the model does not list, such as seed on a model without it, is refused with 400 unsupported_parameter. It is never dropped silently, because it would change the output and the price.

Response

{
  "created": 1730000000,
  "data": [{ "url": "https://..." }],
  "usage": { "cost": 0.02 },
  "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21"
}
FieldTypeDescription
createdintegerUnix time of the request.
data[].urlstringWith response_format: "url": a signed link, valid for 24 hours. GET /v1/generations/{id} signs a fresh one.
usage.costnumberThe charge in USD, as a JSON number.
idstringThe request id. Use it with GET /v1/generations/{id}.
expanded_promptstring | nullAddition. With prompt_expand: the prompt that rendered. null when nothing was expanded.

Hyperouter stores every result for 30 days, so a link never points at a vendor. After 30 days the record stays but its outputs is empty.

Waiting for the result

Examples

curl https://api.hyperouter.app/v1/images/generations \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "x-ai/grok-imagine-image", "prompt": "A lighthouse at dusk", "n": 1}'
curl https://api.hyperouter.app/v1/images/edits \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "x-ai/grok-imagine-image", "prompt": "make it night",
       "images": [{"image_url": "https://example.com/photo.png"}]}'

Errors

StatuscodeparamWhen
400unsupported_content_typeThe body is multipart/form-data: Send a JSON body; pictures and videos are https URLs.
400invalid_bodythe fieldThe body is not valid JSON; images is not an array of objects with image_url; prompt is missing on generations; n is below 1; response_format is not url (response_format must be url.; b64_json is unsupported_parameter); resolution is not allowed; a typed field has the wrong type; moderation is not auto or low.
400unknown_modelmodelThe model does not exist for images, or no provider matches your provider preferences.
400invalid_sizesizesize does not look like 1024x1024, or the model does not allow it.
400too_many_imagesn or imagesn is over 10 or over the model’s limit; more than 5 pictures; more pictures than the model takes.
400image_requiredimagesAn edit has fewer pictures than the model needs, or you called generations on a model that only edits (use /v1/images/edits).
400unsupported_operationimagesThe model does not edit pictures.
400unsupported_parameterthe fieldThe model does not support the field; images[i].file_id.
400invalid_combinationthe fieldThe vendor does not allow these fields together.
400async_requiredThe model answers asynchronously only: send Prefer: respond-async.
400unsupported_parameterstream"stream": true on a model whose response_modes do not include stream.
400invalid_imageimages[i].image_urlThe URL is not https to a public host: images[i].image_url must be an https URL. for a data: or http:// URL.
400content_rejectedThe vendor’s safety filter refused the input: The request was rejected by a content safety filter.
400vendor_rejectedThe request was refused by the model. Check its parameters and inputs.
400invalid_idempotency_keySee Retries and idempotency.
402insufficient_creditsThe balance or key limit cannot cover the most the request could cost.
409idempotency_key_reused, idempotency_key_in_useSee Retries and idempotency.
429rate_limit_errorEvery provider is at capacity (no code), or your account is over its requests per minute or concurrent requests (code: rate_limited, see Rate limits).
503upstream_errorThe model failed to produce a result. You were not charged.
503provider_busyThe provider stayed at capacity for about two minutes and did not take the job. Retry later. You were not charged.
500server_error, no_priceHyperouter failed, or the model cannot be priced right now. A 500 after the image was made carries x-should-retry: false.
503timeoutThe image was not ready within 90 seconds. It is still being made: error.generation_id is the id to poll. Carries x-should-retry: false.

A failed job’s message ends with the provider’s own reason when it gave one, after Provider:, so a refused prompt, a busy provider and an outage read apart. URLs in it are replaced by <url>.

Image models

ModelDoesPrice
x-ai/grok-imagine-imageGenerate and edit. Up to 10 images, up to 5 input images.$0.02 per image
x-ai/grok-imagine-image-2.0Generate and edit, as above.From $0.04 per image
qwen/qwen-image-2.0Generate and edit (1 to 3 input images). n 1 to 6.$0.035 per image
bytedance/seedream-5.0-flashGenerate and edit (1 to 10 input images). One 2K image per request, no watermark.$0.018 per image
bytedance/seedream-5.0-liteGenerate and edit, as above.$0.035 per image

Some models need access granted to your account. The model reference in the dashboard lists every model your account can call, with its parameters, prices and notes.

Videos

Video follows OpenAI’s Videos API. A video is a job. A create, edit, extension or remix call answers 200 at once with the Video object in status queued. You poll GET /v1/videos/{id} until the status is completed, or receive a webhook. Prefer changes nothing here: videos are always asynchronous.

EndpointDoes
POST /v1/videosCreate a video from a prompt, with an optional input_reference.
POST /v1/videos/editsEdit or upscale a completed video. Only models that list edit in operations.
POST /v1/videos/extensionsExtend a completed video. Only models that list extend.
POST /v1/videos/{id}/remixNew prompt on a completed video, same model. Only models that list remix.
GET /v1/videosList your videos.
GET /v1/videos/{id}The Video object.
GET /v1/videos/{id}/contentThe MP4 or the thumbnail.
DELETE /v1/videos/{id}Delete a video.

Create a video

POST/v1/videos

The body is JSON, on every video endpoint; the OpenAI SDKs' videos.create with an input_reference picture file is also accepted, the file handled as for image edits. Every picture and video is an https URL to a public host, which is passed to the provider as it is: the provider must be able to fetch it, and one it cannot fetch or read fails the video with vendor_rejected, and nothing is charged. A data: URL, an http:// URL, an IP address or an internal name is refused with 400 invalid_image or invalid_video. A multipart/form-data body is refused with 400 unsupported_content_type.

NameTypeRequiredAllowed valuesDefaultDescription
modelstringYesA video model idThe model to use.
promptstringYesNon-blank textWhat to make.
secondsstring | integerNoAs the model allowsThe model’s first allowed value, else 4 s (or the model’s maximum if lower)Length of the video. "8" and 8 are the same.
sizestringNoWIDTHxHEIGHTThe height picks the resolution unless you send resolution: up to 480 is 480p, up to 720 is 720p, up to 1080 is 1080p, up to 1440 is 2K, above is 4K. The model must allow that resolution.
input_referenceobjectIf the model needs a picture{"image_url": "https://..."}, an https URLThe first picture of the video. file_id is not accepted.
resolutionstringNo480p, 720p, 1080p, 2K, 4K, as the model allowsThe model’s first resolutionOutput resolution. It changes the price.
ratiostringNoIf the model lists itAspect ratio.
audiobooleanNoIf the model makes soundfalseMake sound.
seedintegerNoIf the model lists itSeed for repeatable output.
negative_promptstringNoIf the model lists itWhat to avoid.
prompt_extendbooleanNoIf the model lists itOff unless you askLet the model expand the prompt.
prompt_expand, localeboolean, stringNoAs for imagesfalseAddition. See Prompt expansion.
prompt_expand_modelstringNoAs for imagesx-ai/grok-4.6Addition. With prompt_expand: the model that writes the rewrite. See Prompt expansion.
first_frame, last_framestringNoAn https URL of a pictureKeyframes.
reference_images, reference_videosstring[]Nohttps URLsReference media. They cannot be combined with keyframes, as the vendor sets.
models, providerstring[], objectNoAs for imagesAddition. Fallback models and routing preferences.
moderationstringNoauto, lowautoAddition. See Moderation.
model-specific fieldsas declaredAs declaredAs declaredSee Model-specific parameters.

Edit, extend and remix

EndpointBody (JSON)
POST /v1/videos/editsmodel, video (required), prompt, resolution and the shared features. video is {"id": "<your video id>"} (a completed video of yours) or {"url": "https://..."}. model defaults to the source video’s model when you send an id.
POST /v1/videos/extensionsmodel, video (required), prompt, seconds. The new seconds follow the same rules as a create.
POST /v1/videos/{id}/remixprompt (required). The model is the source video’s.

The source video must have completed. A video given by URL is an https URL, passed to the provider as it is, like a picture. If you give a video by URL, seconds is needed on models that have no fixed maximum.

List, read, download, delete

EndpointParametersAnswer
GET /v1/videosQuery: after (a video id), limit (1 to 100, default 20), order (desc default, or asc){"object":"list","data":[Video],"first_id","last_id","has_more"}. Videos past their expires_at are left out.
GET /v1/videos/{id}The Video object.
GET /v1/videos/{id}/contentQuery: variant (video default, or thumbnail)302 Found to a signed link to the bytes (video/mp4, or the thumbnail’s type), valid for 15 minutes. HTTP clients and the OpenAI SDKs follow it. 404 not_found before completion or when there is no thumbnail.
DELETE /v1/videos/{id}{"id","object":"video.deleted","deleted":true}. Answers 409 video_not_finished while the job runs. A deleted video answers 404.

The Video object

{
  "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21",
  "object": "video",
  "model": "x-ai/grok-imagine-video",
  "status": "completed",
  "progress": 100,
  "prompt": null,
  "seconds": "8",
  "size": "",
  "created_at": 1730000000,
  "completed_at": 1730000137,
  "expires_at": 1732592000,
  "error": null,
  "remixed_from_video_id": null,
  "usage": { "cost": 0.4, "seconds": 8 }
}
FieldTypeDescription
statusstringqueued, in_progress, completed or failed.
progressinteger0 to 100.
promptstring | nullThe prompt is returned only while the job runs. The finished object returns null.
secondsstringLength of the video, as a string, as OpenAI’s.
sizestringThe size you sent, if any.
expires_atintegerUnix time: created_at plus 30 days. After it, GET /v1/videos/{id} and /content answer 404 not_found and the video is left out of the list. Download what you want to keep before then.
errorobject | nullOn a failed job: {"code", "message"}, for example content_rejected.
remixed_from_video_idstring | nullThe source video of an edit, extension or remix.
usageobject | nullSet once the job is settled: cost (USD, a JSON number) and seconds (integer).
expanded_promptstring | nullAddition. With prompt_expand: the prompt that rendered, once the job is submitted. Kept 30 days, then null.

A video is billed on the seconds you request. The amount is held up front, and released in full if the job fails.

Example: x-ai/grok-imagine-video

Model x-ai/grok-imagine-video makes a video from a prompt, with an optional picture to start from. seconds is 1 to 15. Price: $0.050 per second.

curl https://api.hyperouter.app/v1/videos \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "x-ai/grok-imagine-video",
    "prompt": "She turns to the camera and smiles",
    "input_reference": { "image_url": "https://example.com/portrait.png" },
    "seconds": 8
  }'
{
  "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21",
  "object": "video",
  "model": "x-ai/grok-imagine-video",
  "status": "queued",
  "progress": 0,
  "prompt": "She turns to the camera and smiles",
  "seconds": "8",
  "size": "",
  "created_at": 1730000000,
  "completed_at": null,
  "expires_at": 1732592000,
  "error": null,
  "remixed_from_video_id": null,
  "usage": null
}

This job is charged 8 × $0.050 = $0.40, so the settled object shows "usage": {"cost": 0.4, "seconds": 8}.

Errors

StatuscodeparamWhen
400unsupported_content_typeThe body is multipart/form-data: Send a JSON body; pictures and videos are https URLs.
400invalid_bodythe fieldThe body is not valid JSON; prompt is blank; video is not an object with id or url; input_reference is not an object with image_url; limit, after or variant is not valid; a typed field has the wrong type; resolution is not allowed; moderation is not auto or low.
400unknown_modelmodelThe model does not exist for videos.
400invalid_secondssecondsseconds is not allowed for the model (the message lists the allowed values), or is needed for a video given by URL.
400invalid_sizesizesize does not look like 1280x720.
400unsupported_parameterthe fieldThe model does not support the field; the resolution is not available (resolution or size); audio on a model without sound; input_reference.file_id.
400unsupported_operationmodelThe model does not list the operation (edit, extend or remix).
400invalid_combinationthe fieldReference media together with keyframes, as the vendor sets.
400image_requiredinput_referenceThis model needs an input_reference picture.
400video_not_readyvideoThe source video has not completed.
400invalid_image, invalid_videothe fieldA picture URL (invalid_image) or video URL (invalid_video: reference_videos[i], video.url) is not https to a public host, for example a data: or http:// URL: <field> must be an https URL. param is the field, for example input_reference.image_url, first_frame or reference_images[1]. A URL is passed to the provider as it is: one the provider cannot fetch or read fails the video with vendor_rejected, and nothing is charged.
400content_rejected, vendor_rejectedThe job failed at the vendor. It shows in error.code of the Video object.
400clip_too_longThe reference clip plus the requested seconds run past the model’s limit: The reference clip plus the requested seconds run past the model's limit of 30 s. Send a shorter clip or fewer seconds. You were not charged. It shows in error.code of the Video object.
402insufficient_creditsThe balance or key limit cannot cover the held amount.
404not_foundNo such video, it is not yours, it was deleted, or it is past expires_at; or the content is not ready.
409video_not_finishedYou deleted a video that is still being made.
409idempotency_key_reused, idempotency_key_in_useSee Retries and idempotency.
429, 503, 500As for images. A 500 after the video was created carries x-should-retry: false.

Video models

ModelPrice per second of videoNotes
x-ai/grok-imagine-videoFrom $0.050Text, image or video input. 15 s at most. Supports edit.
x-ai/grok-imagine-video-1.5From $0.080Text, image or reference input. 15 s at most.
x-ai/grok-imagine-video-1.5-liteFrom $0.020As above, smaller and cheaper.
bytedance/seedance-2.0From $0.0431892Text, first and last frame, or reference image input, optional audio. 4 to 15 s. 480p, 720p, 1080p or 4K; the rate shown is 480p's.
bytedance/seedance-2.0-fastFrom $0.0331452Text, first and last frame, or reference image input, optional audio. 4 to 15 s. 480p or 720p; the rate shown is 480p's.
bytedance/seedance-2.0-miniFrom $0.0210924Text, first and last frame, or reference image input, optional audio. 4 to 15 s. 480p or 720p; the rate shown is 480p's.
bytedance/seedance-2.5From $0.0642816Text, first and last frame, or reference image input, optional audio. 4 to 15 s. 480p, 720p or 1080p; the rate shown is 480p's.
lumina/berry-1.0From $0.050Text, image or reference input, optional audio. 30 s at most. 480p, 720p or 1080p; the rate shown is 480p's.
minimax/minimax-h3From $0.080Text, first and last frame, or reference image input, no audio. 4 to 15 s, at 768p or 2K.
alibaba/wan3.0From $0.050Text, image or reference input, optional audio. 30 s at most. 480p, 720p or 1080p; the rate shown is 480p's.

The model reference in the dashboard lists every video model your account can call.

Generations

GET/v1/generations/{id}

Returns the record of one of your requests, of any kind: its status, usage, charge, timing and, for images and videos, signed links to the outputs. Use it to poll an image request you sent with Prefer: respond-async and to reconcile spend. Prompt and completion text are never in the record. Hyperouter keeps them encrypted for 180 days only to meet legal obligations, and no API or dashboard page returns them (Privacy Policy).

Parameters

NameInTypeRequiredDescription
idpathstring (UUID)YesThe id of a chat completion, image answer or Video object.

Response

{
  "data": {
    "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21",
    "kind": "chat",
    "model": "x-ai/grok-4.7",
    "provider_name": "xAI",
    "status": "done",
    "created_at": "2026-10-24T08:12:00Z",
    "finished_at": "2026-10-24T08:12:02Z",
    "usage": {
      "input_tokens": 120, "output_tokens": 300, "cache_read_tokens": 0,
      "image": 0, "video_seconds": 0, "cost": 0.00204
    },
    "usage_estimated": false,
    "ttft_ms": 320,
    "latency_ms": 2100,
    "error": null,
    "outputs": []
  }
}
FieldTypeDescription
kindstringchat, image or video.
provider_namestring | nullThe provider that served a finished chat request. null for images and videos.
statusstringqueued, submitting, running, done or failed.
created_at, finished_atstring | nullRFC 3339 UTC. finished_at is null until the request ends.
usageobjectinput_tokens, output_tokens, cache_read_tokens, image, video_seconds and cost. cost is the charge in USD, a JSON number. Counts are 0 until the request is done.
usage_estimatedbooleantrue when the provider sent no token counts and Hyperouter counted them.
ttft_ms, latency_msinteger | nullTime to first token and total time, in milliseconds.
errorobject | null{"code", "message"} when the request failed.
outputsobject[]For images and videos: [{"url", "expires_at"}], signed links valid for 24 hours. Available for 30 days after the request; after that outputs is empty and the record stays. Always empty for chat.
expanded_promptstring | nullFor images and videos sent with prompt_expand: the prompt that rendered. Kept 30 days after the request, then null.
curl https://api.hyperouter.app/v1/generations/0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21 \
  -H "Authorization: Bearer $HYPEROUTER_API_KEY"

Errors

StatustypecodeparamWhen
404not_foundnot_foundidNo generation with that id. The id is not a UUID, does not exist, belongs to another account, or was deleted.
500server_errorserver_errorHyperouter could not read or sign the record.
401, 403See Authentication.

Model-specific parameters

Some models have fields that other models do not. These are extra parameters. You send them as top-level fields of the request body, like any other field. Every OpenAI SDK can send them (extra_body in Python, extra properties in JavaScript).

GET /v1/models lists each model’s extra parameters in extra_parameters. Each entry has:

FieldTypeDescription
typestringOne of string, integer, number, boolean, array, object.
valuesarrayThe allowed values. Empty means any value of the type.
price_conditionbooleantrue when the field changes the price.
descriptionstringWhat the field does.

The model reference in the dashboard describes the extra parameters of each model your account can call.

Rules

Shared features

These fields have the same name on every model that has them. A model lists the ones it takes in supported_parameters.

FieldMeaning
resolution480p, 720p, 1080p, 2K or 4K, as the model allows.
ratio, seed, negative_prompt, prompt_extend, audioAs named. audio and prompt_extend are off unless you ask.
first_frame, last_frameKeyframes for video.
reference_images, reference_videosReference media for video and image edits.

Prompt expansion

With "prompt_expand": true, Hyperouter rewrites your prompt for the model before it renders: a fuller description for a picture, a second-by-second script for a video. It is off by default, so a prompt goes as written. Only a model whose supported_parameters lists prompt_expand takes it, and only to create an image or a video; on any other model, and on a video edit, extension or remix, it is refused with 400 unsupported_parameter, and it cannot be combined with prompt_extend: true (400 invalid_body).

Prompt expansion is available to an account once Hyperouter has enabled it; open a support ticket to request access. Until then GET /v1/models does not list prompt_expand for any model, and a request with prompt_expand: true on a model that offers it answers 403 permission_error, code feature_not_enabled, param prompt_expand, with nothing held or sent to the provider.

The rewrite as a chat model

An account with prompt expansion also gets a chat model that answers the rewrite instead of rendering it; your model reference names it. Call it on POST /v1/chat/completions with exactly one user message: its text is the prompt, and its image_url parts (https URLs, at most 5) are the pictures; a data: or http:// URL is 400 invalid_image with param messages. target_model is required: a model that lists prompt_expand. seconds, locale and negative_prompt are optional. A video target with a picture is written from that first frame. It does not stream (stream: true is 400 unsupported_parameter), and any other message, a system message included, is 400 invalid_body.

This chat model always uses x-ai/grok-4.6; it does not take prompt_expand_model. choices[0].message.content is a JSON string, {"prompt": "...", "negative_prompt": "..."}, plus "beats" (one line per second) for a video target. It is billed at x-ai/grok-4.6's token prices, shown in usage. A prompt that asks for the model's instructions answers 400 prompt_rejected; a rewrite that is refused or comes back empty answers 422 expansion_refused, and one that fails 503 upstream_error. None of these is charged.

Moderation

The moderation field is OpenAI’s. It works on /v1/chat/completions, /v1/images/generations, /v1/images/edits and /v1/videos with its edit, extension and remix calls.

ValueMeaning
auto (default)The vendor’s own screening applies. Hyperouter sends nothing extra.
lowHyperouter sends the provider’s opt-out from its screening, on models where the provider has one. On other models low is ignored without an error, as OpenAI does. Each model’s entry in your model reference says whether it has one. Send moderation in the body: a provider’s own screening header or field is not forwarded on these endpoints.

Any other value is refused: 400 invalid_request_error, code invalid_body, param moderation, message moderation must be auto or low.

low is available to an account once Hyperouter has enabled it; open a support ticket to request it. Until then a request with low answers 403 permission_error, code moderation_not_enabled, param moderation, and is not sent to the provider.

{
  "model": "qwen/qwen-image-2.0",
  "prompt": "...",
  "moderation": "low"
}

The response does not say whether the opt-out was used. The policies in the Terms of Service still apply. A request that the vendor’s filter refuses fails with 400 content_rejected and is not charged.

Webhooks

Instead of polling, an endpoint of yours can receive a request when a job ends. Add endpoints in the dashboard. The shape is OpenAI’s webhooks, signed with the Standard Webhooks scheme, so the OpenAI SDKs verify them.

EventSent when
video.completed, video.failedA video job ends.
image.completed, image.failedAn image job that you sent with Prefer: respond-async ends.
raw.completed, raw.failedA raw call’s task ends (accounts with raw calls): charged, or its hold released. A raw call answered inline sends none.

The payload carries an id, never a result, a prompt or a charge. Fetch the object with your API key: data.id is the id that GET /v1/videos/{id} and GET /v1/generations/{id} take. For a raw task it is the X-Generation-Id that the create answered.

{
  "id": "evt_0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21",
  "object": "event",
  "type": "video.completed",
  "created_at": 1761300000,
  "data": { "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21" }
}

Headers and verification

Each delivery is a POST with content-type: application/json and these headers:

The signing secret starts with whsec_. It is shown once, when you create the endpoint and when you rotate it. Verify the raw request body, before you parse it:

// Node, with the OpenAI SDK
const event = client.webhooks.unwrap(rawBody, req.headers, process.env.HYPEROUTER_WEBHOOK_SECRET);
if (event.type === "video.completed") {
  const video = await client.videos.retrieve(event.data.id);
}

unwrap throws when the signature does not verify; answer such a request with an error status. The follow-up call uses the same base URL as the rest of this API.

Endpoints, retries and limits

MCP server

POSThttps://api.hyperouter.app/mcp

An SI assistant can work with your Hyperouter account through the Model Context Protocol (MCP). The server manages the dashboard: API keys, activity, credits, tickets, webhooks and the model list. It does not run models. Model calls still go to /v1 with an API key.

The server speaks Streamable HTTP and JSON-RPC 2.0. It supports MCP revisions 2025-11-25 and 2025-06-18. It is stateless: there are no sessions, and GET /mcp answers 405.

Access tokens

An MCP client authenticates with a dashboard access token, not with an API key.

ScopeTools the token can call
readTools that only read data.
writeEvery tool that the user has permission to use.

Send the token as a bearer token:

Authorization: Bearer hr_dt_...

Protocol

MethodAnswer
initializeThe protocol version (the client's, if supported; else 2025-11-25), the tools capability and server information.
pingAn empty result.
tools/listThe tools that the token's user and scope allow.
tools/callThe result of one tool.

A notification, or a response to the server, answers 202 with no body. A JSON-RPC batch is refused with error -32600. A message must not exceed 1 MiB. An unknown MCP-Protocol-Version header answers 400. A browser request must come from https://dash.hyperouter.app; a client that sends no Origin header is not affected.

curl https://api.hyperouter.app/mcp \
  -H "Authorization: Bearer $HYPEROUTER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

Tools

tools/list returns the current tool set. Each tool maps to one dashboard route and carries a JSON Schema for its arguments. The tools cover:

A tool answers with one text content item that holds the JSON body of the route. When the route fails, the result has isError: true and the text <message> (<code>, HTTP <status>). An unknown tool, or one that your token cannot see, is JSON-RPC error -32602.

Approvals

A sensitive write, such as crediting an account, is not run at once. The tool does not execute it. It answers with status set to approval_required and an approval_url on the dashboard. The user opens the link, confirms with a second factor and approves the action. An approval expires after 24 hours. The user can also reject it.

Billing & limits

Rate limits

Each account has a limit on requests per minute and on requests in flight at once. Both count every API key of the account together, and apply to every request that starts work: chat completions, image and video creates, and raw creates. Reading a result (GET /v1/generations/{id}, GET /v1/videos…, a raw task poll) does not count; raw polls have their own count, per provider, with the same requests-per-minute limit.

Your tier follows the total you have topped up, before the top-up fee:

TierTopped upRequests per minuteConcurrent requests
1Under $106010
2$10 or more30030
3$100 or more1,200100
4$1,000 or more6,000300

Your current limits are on the Credits page. These are the defaults; staff can change them for the platform or for one account, and a change applies within a minute.

Over either limit, the request is refused before anything is held or forwarded, with 429, type: rate_limit_error, code: rate_limited and a Retry-After header in seconds. The message names the limit you reached. A request counts toward the minute even when it is refused. A request in flight, a stream included, counts until it ends.

As a guard against key guessing, Hyperouter counts failed API key checks per client address. More than 30 in a minute blocks that address from every /v1 route for 15 minutes: 429, rate_limit_error, too_many_auth_failures, with Retry-After. A valid key does not lift the block.

Support

Open a ticket in the dashboard for billing, top-ups and API questions; staff reply in the same thread. You can also write to [email protected]. Please include the id from the response when you report a specific request.