amnt docs
Developers

Code examples

A typed TypeScript client, a Python helper, batching, a Next.js proxy route, and shell one-liners.

Copy-paste starting points. Each one handles the three things that actually bite: the timeout, the 429, and success: false.

A small TypeScript client

amnt.ts
const BASE = "https://www.amnt.io/api/v1";

export class AmntError extends Error {
  constructor(message: string, readonly status: number) {
    super(message);
  }
}

async function call(path: string, init: RequestInit = {}) {
  const res = await fetch(BASE + path, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.AMNT_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
    // Image agents can take well over a minute.
    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 call(path, init);
  }

  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new AmntError(body.error ?? `HTTP ${res.status}`, res.status);
  return body;
}

export const amnt = {
  run: async (agent: string, input: Record<string, unknown>) => {
    const body = await call("/run", { method: "POST", body: JSON.stringify({ agent, input }) });
    // A failed agent is a 200. This is the line everyone forgets.
    if (!body.success) throw new AmntError(body.error ?? "Agent run failed", 200);
    return body;
  },
  list: (params: Record<string, string> = {}) =>
    call("/agents?" + new URLSearchParams(params)),
  get: (agent: string) => call(`/agents/${agent}`),
  balance: () => call("/balance"),
};

Python

amnt.py
import os, time, requests

BASE = "https://www.amnt.io/api/v1"


class AmntError(RuntimeError):
    pass


def _session() -> requests.Session:
    s = requests.Session()
    s.headers.update({"Authorization": f"Bearer {os.environ['AMNT_API_KEY']}"})
    return s


def run(agent: str, payload: dict, retries: int = 2) -> dict:
    s = _session()
    for attempt in range(retries + 1):
        res = s.post(f"{BASE}/run", 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

        if res.status_code == 402:
            body = res.json()
            raise AmntError(
                f"Out of credits: need {body['credits_required']}, have {body['credits_remaining']}"
            )

        res.raise_for_status()
        body = res.json()

        if not body.get("success"):
            raise AmntError(body.get("error", "Agent run failed"))

        return body

    raise AmntError("Rate limited, gave up")

A Next.js route that proxies an agent

The pattern that keeps your key server-side: your front end calls your route, your route calls amnt.

app/api/describe/route.ts
export async function POST(req: Request) {
  const { product } = await req.json();

  const res = await fetch("https://www.amnt.io/api/v1/run", {
    method: "POST",
    headers: {
      // Server-side only. Never NEXT_PUBLIC_ anything.
      Authorization: `Bearer ${process.env.AMNT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      agent: "alice/product-description",
      input: { prompt: product },
    }),
  });

  const body = await res.json();

  if (!res.ok || !body.success) {
    // Do not leak our error text straight to the browser.
    return Response.json({ error: "Could not write that description." }, { status: 502 });
  }

  return Response.json({ text: body.output.text });
}

Running a batch without hitting the limit

One per second stays well inside 60/min
const products = ["walnut desk lamp", "brushed steel kettle", "linen throw"];
const results = [];

for (const product of products) {
  const { output, credits_remaining } = await amnt.run("alice/product-description", {
    prompt: product,
  });

  results.push(output.text);

  // Stop before the balance does, rather than after.
  if (credits_remaining < 10) {
    console.warn("Low balance - stopping at", results.length, "of", products.length);
    break;
  }

  await new Promise((r) => setTimeout(r, 1000));
}

Why sequential

Parallel calls are allowed, but a run holds a connection for as long as the agent takes. Ten at once means ten open sockets and a bill that arrives ten times faster. Sequential with a small delay is easier to reason about and easier to stop.

Shell

Run and read the text
curl -s -X POST https://www.amnt.io/api/v1/run \
  -H "Authorization: Bearer $AMNT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent":"alice/product-description","input":{"prompt":"a walnut desk lamp"}}' \
  | jq -r '.output.text'
Save the images
curl -s -X POST https://www.amnt.io/api/v1/run \
  -H "Authorization: Bearer $AMNT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent":"studio/logo-mark","input":{"prompt":"a fox, minimal"}}' \
  | jq -r '.output.images[]' \
  | xargs -n1 -I{} curl -sO {}
What can I afford?
curl -s https://www.amnt.io/api/v1/balance \
  -H "Authorization: Bearer $AMNT_API_KEY" | jq -r '.credits'

On this page