# API

> Call Frames F1 with any OpenAI SDK: keys, endpoints, runs, streaming and budgets.

The Frames API lets your code use Frames F1, or pull data from the catalog directly. It works with OpenAI's SDKs, so most apps only need a new base URL, an API key and the model name `frames-f1`.

## Basics

| | |
| --- | --- |
| Base URL | `https://api.frames.ag/v1` |
| Authentication | `Authorization: Bearer <your key>`. Create keys in the dashboard under [Settings → API Access](https://frames.ag/settings/keys); each key is shown once. |
| Model | `frames-f1` |
| Budget | `frames.budget_usd` per request, capped at your plan's per-run cap. Without it, F1 uses a default budget within that cap. |

## Endpoints

| Method and path | What it does |
| --- | --- |
| `POST /v1/chat/completions` | Ask F1 in the OpenAI Chat Completions format |
| `POST /v1/responses` | The same in the OpenAI Responses format, with `previous_response_id` for follow-ups |
| `GET /v1/models` | Lists model ids, no key needed. Use `frames-f1`. |
| `POST /v1/runs` | Starts a run in Frames' own format |
| `GET /v1/runs/{id}` | Gets a run's status, result and receipts |
| `POST /v1/runs/{id}/approve` | Resumes a run paused for approval, with a `max_usd` |
| `POST /v1/runs/{id}/cancel` | Stops a run |
| `GET /v1/runs/{id}/tool-results` | Reads the full responses of a run's paid calls |
| `/v1/tools/*` | Search, probe and invoke tools directly. See [Tool endpoints](https://frames.ag/docs/tool-endpoints.md). |

## First request

Put your key in the `FRAMES_API_KEY` environment variable, then send a question. `budget_usd` is in dollars, so `1` lets this request spend up to 1,000 credits.

```bash title="curl"
curl https://api.frames.ag/v1/chat/completions \
  -H "Authorization: Bearer $FRAMES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "frames-f1",
    "messages": [{ "role": "user", "content": "Which fintech startups raised a Series A in the last 90 days? Include sources." }],
    "frames": { "budget_usd": 1 }
  }'
```

```python title="Python"
# export FRAMES_API_KEY=<your key>
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.frames.ag/v1",
    api_key=os.environ["FRAMES_API_KEY"],
)

resp = client.chat.completions.create(
    model="frames-f1",
    messages=[{"role": "user", "content": "Which fintech startups raised a Series A in the last 90 days? Include sources."}],
    extra_body={"frames": {"budget_usd": 1}},
)
print(resp.choices[0].message.content)
```

```ts title="Node"
import OpenAI from "openai"

const client = new OpenAI({
  baseURL: "https://api.frames.ag/v1",
  apiKey: process.env.FRAMES_API_KEY,
})

const resp = await client.chat.completions.create({
  model: "frames-f1",
  messages: [{ role: "user", content: "Which fintech startups raised a Series A in the last 90 days? Include sources." }],
  // @ts-expect-error Frames extension: per-request budget in USD
  frames: { budget_usd: 1 },
})
console.log(resp.choices[0].message.content)
```

```ts title="AI SDK"
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
import { generateText } from "ai"

const frames = createOpenAICompatible({
  name: "frames",
  baseURL: "https://api.frames.ag/v1",
  apiKey: process.env.FRAMES_API_KEY,
})

const { text } = await generateText({
  model: frames("frames-f1"),
  prompt: "Which fintech startups raised a Series A in the last 90 days? Include sources.",
  // Per-request budget in USD, keyed by the provider name above
  providerOptions: { frames: { budget_usd: 1 } },
})
console.log(text)
```

Each SDK sends the budget its own way: `extra_body` in Python, a `frames` field in the OpenAI Node SDK (with `// @ts-expect-error`, since it isn't in OpenAI's types) and `providerOptions` in the Vercel AI SDK.

## Features

### Streaming

Set `"stream": true`, or send `Accept: text/event-stream` on `/v1/runs`, and the answer streams as it's written, with progress as reasoning updates. Closing the stream doesn't stop the run; cancel it with the run id from the first chunk.

### Structured output

`response_format` with a JSON schema, or `output_schema` on `/v1/runs`, returns JSON in that shape.

### Your own tools

Pass OpenAI `tools`, and F1 can ask your code to run them through `tool_calls`, alongside the paid tools it runs itself.

### Conversations

Earlier messages in the request are used as context. On `/v1/responses`, pass `previous_response_id` instead of resending them.

### Verification level

`frames.verification` (or `options.verification` on `/v1/runs`) takes `none`, `basic`, `standard` or `strict`. `standard` is the default.

### Safe retries

Send an `Idempotency-Key` header on `/v1/runs`, and a retried request returns the same run instead of paying twice.

### Approvals

If a budget goes over your project's policy, the run is created paused with HTTP 402 and `status: "requires_approval"`, holding no credits. Approve it with a `max_usd` (still capped by your plan) or cancel it.

### What comes back

The OpenAI endpoints add `frames_usage` and `frames: { run_id, status, confidence }` to the usual response. `/v1/runs` returns the full run: `result`, `usage`, `payments`, `confidence` and `tools_used`.
