Run an agent
POST /api/v1/run — the endpoint you will use most. Parameters, output shapes, status codes, refunds and timeouts.
POST https://www.amnt.io/api/v1/runOne POST runs any agent on the marketplace, charges your credits, pays the creator and returns the result.
Why one endpoint
The agent is named in the body, not the URL. Switching agents is a config change rather than a code change, and one rate limit and one retry policy covers everything you call.
Request body
| Field | Type | Description | |
|---|---|---|---|
agent | string | required | The agent to run, as handle/slug — the same two parts as its page URL. Case-insensitive |
input | object | required | The agent's inputs, keyed by field name. Read the exact fields from the detail endpoint |
{
"agent": "alice/product-description",
"input": {
"prompt": "a walnut desk lamp",
"tone": "warm"
}
}Unknown keys are ignored, missing ones are not
Input is validated against the agent's published fields. A missing required
field is a 400; a value that breaks a rule (too long, not one of the
allowed choices) is a 422. Keys the creator never declared are dropped
rather than passed through — an agent cannot be steered by a field that is
not on its published list, including a raw prompt.
Most agents also accept extraInstructions: a free-text note appended to
the creator's recipe. It is the one place to add something the fields do not
cover.
Response
| Field | Type | Description |
|---|---|---|
success | boolean | Check this, not the status code. A failed agent returns 200 with success: false and a refund already applied |
output | object | The result. Shape depends on the agent — see below |
credits_used | integer | What this run cost. 0 on failure and on free agents |
credits_remaining | integer | Your balance after the run |
receipt | object | null | Present only for agents that write to Hedera. Carries the transaction identifiers |
error | string | Present when success is false. Written to be shown to a person |
Output shapes
An agent's connector decides the shape. Branch on what is present rather than assuming — a creator can republish an agent on a different connector.
{
"success": true,
"output": { "kind": "text", "text": "Warm walnut, brushed brass..." },
"credits_used": 3,
"credits_remaining": 497
}{
"success": true,
"output": {
"images": ["https://...supabase.co/storage/v1/object/public/..."]
},
"credits_used": 12,
"credits_remaining": 485
}{
"success": true,
"output": { "kind": "json", "data": { "score": 82, "flags": [] } },
"credits_used": 2,
"credits_remaining": 483
}{
"success": true,
"output": { "kind": "receipt", "status": "SUCCESS", "transactionId": "0.0.123@176..." },
"credits_used": 5,
"credits_remaining": 478,
"receipt": { "topicId": "0.0.4567", "sequence": 42 }
}{
"success": false,
"error": "The model refused this request.",
"credits_used": 0,
"credits_remaining": 497
}Image URLs
Images are stored by amnt and served over HTTPS — they are not temporary provider links that expire in an hour. Still, download anything you need to keep: an agent's gallery can be moderated by its owner.
Status codes
| Code | What it means | What to do |
|---|---|---|
200 | Request handled. Read success to know whether the agent worked | Branch on success |
400 | Bad JSON, missing agent, missing input, or a required field absent | Fix the payload. Nothing was charged |
401 | Missing, malformed, invalid or revoked key | See Authentication |
402 | Not enough credits | Top up. credits_required says how many the run needs |
404 | No such agent, or it is not live | Check handle/slug against the list endpoint |
422 | A value broke one of the agent's declared rules | The message names the field. Nothing was charged |
429 | Rate limited | Wait Retry-After seconds. See Rate limits |
500 | The agent is misconfigured, or something broke on our side | Retry once. Nothing was charged |
503 | The agent's owner could not be verified, so it will not run | Temporary. This fails closed on purpose rather than paying the wrong account |
Timeouts and long runs
- Runs are synchronous — the connection stays open until the agent finishes. An image agent can take 30 to 90 seconds.
- Set a client timeout of at least 180 seconds. Node's
fetchhas no default timeout at all and Python'srequestshas none either; neither default is what you want here. - If your client gives up, the run continues server-side and the charge stands. That is the one case where you can pay for a result you never read. More in Long runs and retries.
Retries are not free
There is no idempotency key yet. A retry is a second run and a second
charge. Retry on 429, on 503, and on a network error — never blindly on
success: false, which usually means the input was wrong and will fail
again.
A complete client
type RunResult =
| { success: true; output: unknown; credits_used: number; credits_remaining: number }
| { success: false; error: string; credits_remaining: number };
export async function runAgent(
agent: string,
input: Record<string, unknown>,
): Promise<RunResult> {
const res = await fetch("https://www.amnt.io/api/v1/run", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AMNT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent, input }),
// Image agents routinely need a minute or more.
signal: AbortSignal.timeout(180_000),
});
if (res.status === 429) {
const wait = Number(res.headers.get("retry-after") ?? 60);
await new Promise((r) => setTimeout(r, wait * 1000));
return runAgent(agent, input);
}
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error ?? `amnt returned ${res.status}`);
}
return res.json();
}import os, time, requests
BASE = "https://www.amnt.io/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['AMNT_API_KEY']}"}
def run_agent(agent: str, payload: dict, retries: int = 2):
for attempt in range(retries + 1):
res = requests.post(
f"{BASE}/run",
headers=HEADERS,
json={"agent": agent, "input": payload},
timeout=180,
)
if res.status_code == 429 and attempt < retries:
time.sleep(int(res.headers.get("Retry-After", 60)))
continue
res.raise_for_status()
data = res.json()
if not data.get("success"):
raise RuntimeError(data.get("error", "Agent run failed"))
return data["output"]
raise RuntimeError("Rate limited, gave up")