Hosted tiers
Set llm to a tier name and leave out llm_api_key. Browsa runs the model and bills it in credits, so there is no separate model bill and no provider account to set up. Each tier names a first model and the models it moves to if that one fails.
"browsa"5 credits per taskUse it for: The default. A good first choice for most tasks.
- Runs
- Claude Opus 4.8
- Falls back to
- Claude Sonnet 4.6
- Reasoning
- Low
"browsa-fast"5 credits per taskUse it for: The fastest and lightest tier, for short and simple tasks.
- Runs
- Gemini 3.1 Flash Lite
- Falls back to
- Claude Sonnet 4.6, then Claude Opus 4.8
- Reasoning
- The model has no such setting
"browsa-smart"5 credits per taskUse it for: Harder tasks that need more thought per step.
- Runs
- Claude Opus 4.8
- Falls back to
- Claude Sonnet 4.6
- Reasoning
- High
The credits on each card are added once per task, on top of the normal task credits (browser time, 1 base credit and 1 credit per 5 steps: see pricing). They are a server setting and can change for each tier. The cards show today's values; GET /v1/models always returns the live ones.
from browsa import Client
c = Client(api_key="agt_live_...")
job = c.run_task(
task="Go to example.com and report the H1.",
llm="browsa-fast", # or "browsa", "browsa-smart"
)
print(job.final_result)import { Client } from "browsa";
const c = new Client({ apiKey: "agt_live_..." });
const job = await c.runTask({
task: "Go to example.com and report the H1.",
llm: "browsa-fast", // or "browsa", "browsa-smart"
});
console.log(job.finalResult);curl https://browsa.io/v1/tasks \
-H "X-API-Key: agt_live_..." \
-H "Content-Type: application/json" \
-d '{
"task": "Go to example.com and report the H1.",
"llm": "browsa-fast"
}'When a hosted model fails
If the model a tier is running fails during a task, the run switches to the next model in that tier's list and carries on from where it was. Steps that were lost to the model failing are given back, so you do not pay step credits for them. A job that moved to another model carries the signal llm_fallback_used (see Outcome and signals). The credits per task stay those of the tier you asked for.
List the tiers: GET /v1/models
Read-only and free. It returns each hosted tier with the model it runs, its fallback models, its default reasoning level and its credits per task, and the providers you can use with your own key. Read the credits from here instead of writing them into your code.
from browsa import Client
c = Client(api_key="agt_live_...")
models = c.models()
for tier in models.hosted:
print(tier.id, tier.model, tier.credits_per_task)import { Client } from "browsa";
const c = new Client({ apiKey: "agt_live_..." });
const models = await c.models();
for (const tier of models.hosted) {
console.log(tier.id, tier.model, tier.creditsPerTask);
}curl https://browsa.io/v1/models \
-H "X-API-Key: agt_live_..."{
"hosted": [
{ "id": "browsa", "model": "claude-opus-4-8", "fallback_models": ["claude-sonnet-4-6"],
"reasoning": "low", "credits_per_task": 5, "default": true },
{ "id": "browsa-fast", "model": "gemini-3.1-flash-lite", "fallback_models": ["claude-sonnet-4-6", "claude-opus-4-8"],
"reasoning": null, "credits_per_task": 5 },
{ "id": "browsa-smart", "model": "claude-opus-4-8", "fallback_models": ["claude-sonnet-4-6"],
"reasoning": "high", "credits_per_task": 5 }
],
"own_key": {
"providers": ["anthropic", "openai", "google", "openai_compatible"],
"default_models": { "anthropic": "...", "openai": "...", "google": "..." }
}
}reasoning is the level a tier uses when the task does not set one; null means the tier's model has no such setting. default_models is the model used when a task relies on a saved key and names no model.
Your own provider key
Use any model your Anthropic, OpenAI or Google account can call. Your provider bills you for the tokens. Browsa adds no markup and no hosted charge: the task costs its normal task credits only. There are two ways to hand over the key.
- Per task. Send
llm_api_keywith the task. It is held encrypted for the run and deleted when the run ends. - Saved once. Save the key in your workspace. A task then names it with
llm_key_id, or sends no key at all and uses your default saved key for the model's provider. The API never returns a saved key.
from browsa import Client
c = Client(api_key="agt_live_...")
# A key for this task only
job = c.run_task(
task="Go to news.ycombinator.com and return the top story title.",
llm="anthropic:claude-sonnet-5-5",
llm_api_key="sk-ant-...",
)
# A key saved in the workspace, by id
job = c.run_task(
task="Go to news.ycombinator.com and return the top story title.",
llm="anthropic:claude-sonnet-5-5",
llm_key_id="3f6c2a1e-8b4d-4c1f-9a7e-2d5b6c7e8f90",
)import { Client } from "browsa";
const c = new Client({ apiKey: "agt_live_..." });
// A key for this task only
let job = await c.runTask({
task: "Go to news.ycombinator.com and return the top story title.",
llm: "anthropic:claude-sonnet-5-5",
llmApiKey: "sk-ant-...",
});
// A key saved in the workspace, by id
job = await c.runTask({
task: "Go to news.ycombinator.com and return the top story title.",
llm: "anthropic:claude-sonnet-5-5",
llmKeyId: "3f6c2a1e-8b4d-4c1f-9a7e-2d5b6c7e8f90",
});curl https://browsa.io/v1/tasks \
-H "X-API-Key: agt_live_..." \
-H "Content-Type: application/json" \
-d '{
"task": "Go to news.ycombinator.com and return the top story title.",
"llm": "anthropic:claude-sonnet-5-5",
"llm_api_key": "sk-ant-..."
}'If you send a key and no llm, Browsa picks a default model for that key's provider. Which key wins when several apply, the default model for each provider and the errors a key can return are in the docs: Use a saved key in tasks and How Browsa handles your LLM keys.
OpenAI-compatible servers
To run a task on a gateway such as OpenRouter or LiteLLM, or on a model you serve yourself, add llm_base_url. Browsa sends the model requests to that address with your key. The same field accepts an Anthropic-compatible endpoint.
- The address must be a public
httpsURL. - It needs your own key:
llm_api_keyon the task, or a saved key of the provideropenai_compatible, which stores its address with the key. A hosted tier always uses Browsa's own endpoint. - Set
llmto a model name your server accepts. There is no default model for a compatible server. - The model must support tool calling (also called function calling). Agents act through tool calls, and this is checked before a browser starts.
from browsa import Client
c = Client(api_key="agt_live_...")
job = c.run_task(
task="Go to example.com and report the H1.",
llm="openai:my-model",
llm_api_key="sk-...",
llm_base_url="https://llm.example.com/v1",
)
print(job.final_result)import { Client } from "browsa";
const c = new Client({ apiKey: "agt_live_..." });
const job = await c.runTask({
task: "Go to example.com and report the H1.",
llm: "openai:my-model",
llmApiKey: "sk-...",
llmBaseUrl: "https://llm.example.com/v1",
});
console.log(job.finalResult);curl https://browsa.io/v1/tasks \
-H "X-API-Key: agt_live_..." \
-H "Content-Type: application/json" \
-d '{
"task": "Go to example.com and report the H1.",
"llm": "openai:my-model",
"llm_api_key": "sk-...",
"llm_base_url": "https://llm.example.com/v1"
}'Model names
With your own key, llm is the provider's model name. Three forms are accepted and mean the same thing.
| Form | Example | Notes |
|---|---|---|
| Bare name | "claude-sonnet-5-5" | The provider is read from the name: claude- names go to Anthropic. Fine for well-known model families. |
provider:model | "anthropic:claude-sonnet-5-5" | Says which provider to call. The clearest form, and the one these docs use. |
provider/model | "anthropic/claude-sonnet-5-5" | The same, in the slash style many gateways and tools use. |
The provider part is removed before the request is sent, so your provider sees the plain model name. The three tier names browsa, browsa-fast and browsa-smart are not provider models: they select a hosted tier.
Reasoning
reasoning is an optional field that asks the model to think less or more before each step. It takes "low", "medium" or "high", and it works on a task, on a session message and on a schedule.
| You send | What happens |
|---|---|
| Nothing | A hosted tier uses its own level (the Reasoning line of each tier above). With your own key, the provider's default applies. |
"low", "medium" or "high" | That level is used for the run, on a hosted tier or on your own key. |
| A level, on a model that has no such setting | The field is ignored, the task runs, and result.warnings says so. |
| Any other value | 400 BAD_REQUEST. Nothing is started or charged. |
from browsa import Client
c = Client(api_key="agt_live_...")
job = c.run_task(
task="Compare the three cheapest plans on example.com/pricing and say which fits a team of 5.",
llm="browsa-smart",
reasoning="medium",
)
print(job.final_result)import { Client } from "browsa";
const c = new Client({ apiKey: "agt_live_..." });
const job = await c.runTask({
task: "Compare the three cheapest plans on example.com/pricing and say which fits a team of 5.",
llm: "browsa-smart",
reasoning: "medium",
});
console.log(job.finalResult);curl https://browsa.io/v1/tasks \
-H "X-API-Key: agt_live_..." \
-H "Content-Type: application/json" \
-d '{
"task": "Compare the three cheapest plans on example.com/pricing and say which fits a team of 5.",
"llm": "browsa-smart",
"reasoning": "medium"
}'A higher level makes the model produce more tokens per step. On your own key that is more on your provider's bill. On a hosted tier the credits per task do not change with the level.
Usage and cost
A finished task reports what the model used. The fields are in the job's result, from GET /v1/jobs/{id}. All of them are optional: jobs that ran before these fields existed have none, so read them defensively.
| Field | What it holds |
|---|---|
usage | Token counts for the whole run: input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, and requests, the number of calls made to the model. |
llm_cost_usd | An estimate of what the model cost in US dollars, worked out from the provider's public list prices. null when the price of the model is not known, and always null on a hosted tier, which is paid in credits. |
llm_tier | The hosted tier the run used. Present on hosted runs only. |
reasoning | The reasoning level that was applied. |
{
"status": "completed",
"credits_charged": 4,
"result": {
"final_result": "Top story: ...",
"usage": {
"input_tokens": 48210,
"output_tokens": 1904,
"cache_read_tokens": 31500,
"cache_write_tokens": 6200,
"requests": 9
},
"llm_cost_usd": 0.0612,
"reasoning": "medium"
}
}The cost is an estimate. It uses list prices, so a negotiated rate, a gateway's own pricing or a batch discount will not show in it. Your provider's invoice is the real figure.
Checks before a browser starts
Before Browsa opens a browser for a task, it makes one small call to the model to see that the task can run on it. If that call fails, the job ends at once with a code that says why. No browser was opened, so nothing is charged.
| Job code | What is wrong | What to do |
|---|---|---|
LLM_KEY_REJECTED | The provider refuses your key: it is invalid, revoked, out of credit, or belongs to another provider than the model. | Check the key in your provider's console and that it matches the model. A saved key is marked invalid until you rotate it. |
MODEL_NO_TOOL_USE | The model answers without a tool call. Agents need tool calling. | Use a model, and an endpoint, that support tool or function calling. |
MODEL_RATE_LIMITED | The model is rate limited on every try. On a hosted tier this means every model in the tier is busy. | Run the task again in a few minutes. With your own key, raise the limit with your provider. On a hosted tier you can also switch to your own key. |
A problem with the request itself is caught earlier still, when you submit the task: a missing key returns 400 LLM_KEY_REQUIRED, and a reasoning value outside the three levels returns 400. The full list is in Job failure codes and LLM key errors.