Pick candidates from OpenRouter's public catalogue by what they read, what they cost and which parameters they take, read a model's endpoints, route it with provider_options, handle rate limits per model, and prove the choice with a live probe.
When you need this
Section titled “When you need this”Read this before you add or change an agent whose model starts with openrouter:, before an experiment that
compares agents, and when a step fails with MODEL_FEATURE_UNSUPPORTED, runs into 429 or is slow. The answer to
“can this model do it” comes from OpenRouter’s catalogue and from a live probe, not from a model’s name or from
memory: AQVEN keeps no table of what each model can read or produce.
-
List candidates from the catalogue.
GET https://openrouter.ai/api/v1/modelsis public: no key needed. It takes filters as query parameters, among theminput_modalities,output_modalitiesandsupported_parameters(comma-separated). This lists the models that read images and return structured output, cheapest input first, with prices in USD per million tokens:Terminal curl -s 'https://openrouter.ai/api/v1/models?input_modalities=image&supported_parameters=structured_outputs' \| jq -r '.data[] | [.id, (.pricing.prompt | tonumber * 1000000), (.pricing.completion | tonumber * 1000000), .context_length] | @tsv' \| sort -t$'\t' -k2 -gPick at least five candidates from at least three model families, so a result does not rest on one family.
Field of a model What it tells you idthe name after openrouter:in the agent’smodel, for examplegoogle/gemini-2.5-flash-litearchitecture.input_modalitieswhat the model reads: text,image,file,audio,videoarchitecture.output_modalitieswhat it produces; imagemeans it can answer with an imagepricing.prompt,pricing.completionUSD per input and per output token, as strings pricing.image,pricing.internal_reasoning,pricing.input_cache_read, …other prices, only where they apply context_lengththe context window in tokens top_provider.max_completion_tokensthe longest answer of the main provider supported_parametersrequest parameters some provider of the model accepts: structured_outputs,response_format,tools,reasoning,max_tokens,seed, … -
Read the endpoints of each candidate.
GET https://openrouter.ai/api/v1/models/<author>/<slug>/endpointslists every provider that serves the model. The same model can differ between them, so choose the providers here, not from the model entry:Terminal curl -s https://openrouter.ai/api/v1/models/google/gemini-2.5-flash-lite/endpoints \| jq -r '.data.endpoints[] | [.tag, .provider_name, .quantization, .max_completion_tokens, .uptime_last_30m] | @tsv'Field of an endpoint What it tells you tagthe provider slug to write in order,onlyorignore, for examplegoogle-vertexorgoogle-vertex/euprovider_namewho serves this endpoint quantizationfp8,bf16and so on;unknownis normal for closed modelscontext_length,max_prompt_tokens,max_completion_tokensthe limits of this endpoint pricingthe prices of this endpoint supported_parameterswhat this endpoint accepts; structured output or tools may be missing on one provider only status,uptime_last_5m,uptime_last_30m,uptime_last_1dwhether it is up latency_last_30m,throughput_last_30mhow fast it has been lately -
Route the model in the agent file. Everything under
settings.provider_optionsgoes into the request body as is, so OpenRouter’sproviderobject goes there:Key of providerEffect orderprovider slugs to try first, in this order; a slug without a region matches all its regions allow_fallbackstrueby default: other providers of the same model may answer when these failrequire_parametersonly providers that accept every parameter of the request; set it for structured output and tools only,ignoreprovider slugs to allow or to skip quantizationsallowed quantizations, for example ["fp8", "bf16"]data_collection"deny"skips providers that may store your datazdrtruekeeps to zero-data-retention endpointssort,max_pricesort providers by price, throughput or latency; cap the price A pinned
orderwithallow_fallbacks: falseleaves a rate-limited provider no way out: keep fallbacks on, or give the agentfallback_models.Keep these keys in the agent, not in
aqven.yaml. When the OpenRouter provider inaqven.yamldeclaresrouting(data_collection,zdr), AQVEN sends it as theproviderobject of every request, and it replaces the agent’s ownproviderobject:order,require_parametersand the rest are dropped. Putdata_collectionandzdrinto each agent’sproviderobject instead. -
Choose how the model’s rate limits are handled. OpenRouter puts no request limit on paid models; the providers behind a model do, and that limit is per model and shared with everyone who calls it. Before a
429reaches you, OpenRouter tries other providers of the same model when fallbacks are allowed. Free variants (:free) have their own small limits per minute and per day. In AQVEN everyopenrouter:<model>is a lane of its own: a429pauses that model only, andon_rate_limiton the provider inaqven.yamlsays what happens next (auto,fixedorfail). How to connect a model provider has the table. Do not lowerlimits.rpmagainst these429s:rpmspaces your own requests to the whole provider and never sees another customer’s load. -
Prove the choice with a live probe.
models check --livesends one small request per output mode and shows which modes work, so you can pinoutput.mode. It does not read the agent’sprovider_options: pass the sameproviderobject with--provider-optionsto probe the routing the agent runs with.Terminal uv run aqven models check reader --project . --live \--provider-options '{"provider": {"order": ["google-vertex", "google-ai-studio"], "allow_fallbacks": true, "require_parameters": true, "data_collection": "deny"}}'For an output with nested objects or long lists,
uv run aqven models shapes reader --project . --livefinds the model’s structural limits (billed as well). Whether a model reads images is proven by one real run on a case with an image: a provider that refuses images fails the step withMODEL_FEATURE_UNSUPPORTED. Then compare the candidates on your own labelled cases with an experiment that changes the agent.
Example
Section titled “Example”The reader agent of the tested snippets project reads listing photos. Gemini 2.5 Flash-Lite
takes image input and structured_outputs; its endpoints are served by google-vertex and google-ai-studio,
both with structured_outputs in their supported_parameters. The agent tries Google’s endpoints in that order,
lets OpenRouter fall back to other providers, keeps to providers that accept every parameter and do not store
data, and moves to Gemma 3 27B when the request to the model fails:
apiVersion: "aqven/v1"kind: "Agent"description: "Reads listing photos and text: Gemini Flash-Lite through OpenRouter, Google endpoints first"model: "openrouter:google/gemini-2.5-flash-lite"fallback_models:- "openrouter:google/gemma-3-27b-it"settings: max_tokens: 2000 provider_options: provider: order: - "google-vertex" - "google-ai-studio" allow_fallbacks: true require_parameters: true data_collection: "deny"output: mode: "tool" on_error: "retry" on_refusal: "fallback" on_truncated: "retry"limits: seconds: 90Its provider in aqven.yaml declares no routing, starts each model at four parallel calls and keeps the default
on_rate_limit: "auto".
Under the hood
Section titled “Under the hood”The openrouter provider is Pydantic AI’s OpenRouter model: settings.provider_options becomes the request’s
extra body, and a project’s routing becomes OpenRouter’s provider object. See
What this is built on.
See also
Section titled “See also”- How to connect a model provider — declaring a provider, credentials, rate-limit strategies per model.
- How to check your model providers are configured — everything
models checkreports. - How to find a model’s real structural limits —
models shapesin detail. - How to prepare images for a flow — what an image model receives.
- OpenRouter’s own reference: list models, list a model’s endpoints, provider routing, rate limits.