# Your first API call, with a spending cap

Set a hard monthly cap on a separate project before you create a key, keep the key in an environment variable, make one call with curl and Python, and turn the usage fields into a cost. Anthropic and OpenAI side by side, Gemini in one line.

Guides · v1.0 · draft · https://hayzlabs.com/guides/first-api-call-with-a-spending-cap/

In short:
- Create a separate project with a monthly cap that actually stops requests before you create a key for it.
- On OpenAI turn on Enforce a hard limit; on Anthropic use a new workspace, since the Default Workspace takes no limit.
- Keep the key in an environment variable, cap reply length, and check your computed cost against the usage page next day.

**Use it when** you have used chat apps and now want to call a model from a script, without a surprise bill or a leaked key.

**Not when** a chat plan already does the job. A script is worth it for repeated or bulk work, such as classifying a few hundred items.

**You need** a vendor account with billing set up, a terminal, and Python 3 if you want the Python version. About 30 minutes. The order of the steps is our suggestion. The settings, field names and prices are from the vendors' docs, read on 2026-10-10.

## Short answer

Make the cap before the key. Create a separate project, set a monthly limit on it that actually stops requests, then create a key that can only reach that project. The trap: on OpenAI a spend limit only sends alerts until you turn on **Enforce a hard limit**. On Anthropic you cannot set a limit on the Default Workspace, so make a new one.

## Step 1. A separate project with a hard cap

| | Anthropic (Claude Console) | OpenAI (API Platform) |
|---|---|---|
| Separate space | **Settings > Workspaces > Create workspace**. Only organization admins can create one. | Project menu (top left) > **Create project**. Only organization owners can create one. |
| Cap on that space | Select the workspace under **Settings > Workspaces**, then **Spend limits**. You can also set alerts at thresholds. | **Project settings > Limits > Spend > Edit spend limit**, enter a **Monthly spend limit**, turn on **Enforce a hard limit**, **Save**. |
| Cap on the whole account | **Settings > Billing > Spend limits > Set limit** (or **Adjust limit**). It cannot exceed your tier's cap. | **Organization limits page > Spend > Edit spend limit**, with **Enforce a hard limit** on. |
| Hard cap or alert? | Hard. At a limit you set, requests fail with HTTP 400 and a message that begins "You have reached your specified ... API usage limits". | Hard only with the toggle on. Alerts alone "do not enforce a cap". At a hard limit, requests fail with HTTP 429 and `project_spend_limit_exceeded` (or `organization_spend_limit_exceeded`). |
| Catch | No limits on the Default Workspace. Each tier also has its own monthly cap, for example $500 on Start; at it, requests return HTTP 429 with `enforced_spend_limit_reached` until the 1st of next month (UTC). | "Enforcement is not instantaneous", so spend can slightly exceed the limit. OpenAI's projects help article still calls the limit a soft threshold in one paragraph; the spend limits guide is newer and describes the toggle. |

On OpenAI you can also pick which models a project may use, under **Limits > Model Usage**. Allowing only the small model below is a second guard.

> [!OUR RULE]
> Set the first cap at an amount you would not mind losing, such as $5 or $10, on a project used only for this. Raise it when you know what a normal month costs. A cap that stops a request is a nuisance. A missing cap can be a bill.

## Step 2. A key that only reaches that project

| | Anthropic | OpenAI |
|---|---|---|
| Where | **Settings > API keys > Create key**. Name it, choose an expiration, set **Linked account**, and choose the workspace from step 1. | Organization settings > your project > **API Keys**. Permissions can be **All**, **Restricted** or **Read Only**. |
| Expiry | Presets of 3 hours, 1, 7 or 30 days, a custom length, or **Never**. It cannot be changed later. | Project keys can have an expiration date, after which requests are rejected. |
| Shown once? | Yes. The full key (it starts `sk-ant-`) is shown only at creation. | Not stated on the pages we read. Copy it at creation to be safe. |

Choose 30 days or less for a first key (our suggestion). Copy it straight into your password manager, as [Privacy settings pass](https://hayzlabs.com/setup/privacy-settings-pass/) says. Never paste a key into a chat, an email, a notes file or a script. Each person should have their own key; OpenAI says sharing keys breaks its terms of use.

## Step 3. Put the key in an environment variable

Both SDKs read the key from an environment variable, so it never appears in your code: `ANTHROPIC_API_KEY` for Anthropic and `OPENAI_API_KEY` for OpenAI. On macOS or Linux, this asks for the key without showing it and without saving it in your shell history (our suggestion):

```sh title="Load the key for this terminal session · v1.0"
read -rs ANTHROPIC_API_KEY && export ANTHROPIC_API_KEY
read -rs OPENAI_API_KEY && export OPENAI_API_KEY
```

Paste the key when the cursor waits, then press Enter. It lasts until you close the terminal. For anything that runs unattended, both vendors recommend a secrets manager instead.

## Step 4. One call with curl

We use each vendor's small, cheap model: `claude-haiku-5-5` and `gpt-6-luna`.

```sh title="Anthropic Messages API · v1.0"
curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -d '{"model": "claude-haiku-5-5", "max_tokens": 1000,
       "messages": [{"role": "user", "content": "<YOUR_PROMPT>"}]}'
```

```sh title="OpenAI Responses API · v1.0"
curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{"model": "gpt-6-luna", "max_output_tokens": 1000,
       "input": "<YOUR_PROMPT>"}'
```

Replace the following:
- `<YOUR_PROMPT>`: a short test, such as `Name three uses of a paperclip.` Nothing private: see [What not to paste](https://hayzlabs.com/start/what-not-to-paste/).

`max_tokens` and `max_output_tokens` cap the length of each reply, and so the cost of each call. The variable name in the command is expanded by your shell, so the key itself is never typed into the command.

## Step 5. The same call in Python, with its cost

Install the SDK in a virtual environment: `pip install anthropic` or `pip install openai`. The prices below are per million tokens for these two models, for prompts up to 100,000 tokens on Haiku 5.5 and short context on luna.

```python title="first_call_anthropic.py · v1.1"
import anthropic

client = anthropic.Anthropic()

message = client.messages.create(
    model="claude-haiku-5-5",
    max_tokens=1000,
    messages=[{"role": "user", "content": "<YOUR_PROMPT>"}],
)
for block in message.content:
    if block.type == "text":
        print(block.text)

u = message.usage
cost = (u.input_tokens * 0.10 + u.output_tokens * 0.50) / 1_000_000
print(u.input_tokens, "in,", u.output_tokens, "out, about $" + f"{cost:.7f}")
```

```python title="first_call_openai.py · v1.1"
from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-luna",
    max_output_tokens=1000,
    input="<YOUR_PROMPT>",
)
print(response.output_text)

u = response.usage
cost = (u.input_tokens * 0.10 + u.output_tokens * 0.50) / 1_000_000
print(u.input_tokens, "in,", u.output_tokens, "out, about $" + f"{cost:.7f}")
```

The curl and Python calls are our suggestion; we could not run them without an API key.

## Reading the usage fields

| | Anthropic `usage` | OpenAI `usage` |
|---|---|---|
| Input | `input_tokens`: only the tokens after the last cache breakpoint. Total input is `input_tokens + cache_creation_input_tokens + cache_read_input_tokens`. | `input_tokens`, with `input_tokens_details.cached_tokens` for the cached part. OpenAI's caching guide counts the cached part inside `input_tokens`. |
| Output | `output_tokens`, with `output_tokens_details.thinking_tokens` for reasoning, which is never more than `output_tokens`. | `output_tokens`, with `output_tokens_details.reasoning_tokens`. Reasoning tokens are billed as output. |
| Total | Not given; add the parts. | `total_tokens`. |

Cost = input tokens × input price ÷ 1,000,000 + output tokens × output price ÷ 1,000,000. An example (illustrative): 15 input and 120 output tokens on either model cost 15 × $0.10 / 1M + 120 × $0.50 / 1M = $0.0000615. A thousand such calls cost about 6 cents.

| Model (2026-10-20) | Input per 1M | Output per 1M | Batch |
|---|---|---|---|
| `claude-haiku-5-5` | $0.10 ($0.50 if the prompt is over 100,000 tokens) | $0.50 ($2.50 over 100,000) | 50% off |
| `gpt-6-luna` | $0.10 short context ($0.20 long) | $0.50 ($0.75 long) | Half price, results within 24 hours |
| `gemini-3.1-flash-lite` | $0.25 (text, image, video) | $1.50 | $0.125 / $0.75 |

Put in your own call to see how far a cap goes. It starts on the example above.

<!-- WIDGET capCalls -->

> [!CHECK]
> Compare your computed cost with the vendor's usage page the next day. If they differ by more than a rounding error, you are on a different price tier or model than you think.

**Cheaper later.** If you can wait for results, batch pricing is half (the Batch column). Prompt caching pays only when many calls share a long, identical opening; a first test call is far below the minimum.

**Gemini.** Same pattern: a project cap at **Spend** > **Monthly spend cap** in AI Studio (labelled experimental), the key in `GEMINI_API_KEY`, and the paid tier for anything private, since Google says free-tier use may be used to improve its products.

## If a key leaks

1. **Disable or delete it now.** Anthropic: **Settings > API keys**, where Disable can be undone and Delete cannot. OpenAI: rotate it on the API Keys page. Gemini: create a new key, deploy it, then disable the leaked one.
2. **Replace it** in your environment and any running job.
3. **Check usage and billing** for calls you did not make. OpenAI asks you to contact it through help.openai.com for anything further.

GitHub's secret scanning partner programme scans public repositories and alerts the provider, and Anthropic and OpenAI are both partners. Do not rely on it: a key can be used before anyone revokes it.

## How to tell it worked

- The project or workspace shows a monthly limit, and on OpenAI **Enforce a hard limit** is on.
- The key's page shows it scoped to that project, with an expiry date.
- Searching your project folder for the first ten characters of the key, such as `grep -r "<KEY_START>" .`, finds nothing.
- Both calls printed a reply and two token counts, and your computed cost matches the usage page the next day.
- You know the error you will see at the cap: HTTP 400 on Anthropic, HTTP 429 `project_spend_limit_exceeded` on OpenAI.

## Limits

> [!LIMIT]
> We have not run these steps; they are read from vendor docs on 2026-10-10, and menu names change. Spend caps are enforced with a short lag on OpenAI and Gemini, so a fast loop can pass the limit slightly. A cap limits the bill, not the damage from a leaked key used within it. Prices are for short prompts on the two small models; long prompts, other models and tools such as web search cost more. Setting up agents with these keys is a separate question: see [Coding agents for people who don't write code](https://hayzlabs.com/guides/coding-agents-for-non-coders/) and [Letting an agent near your email, browser and files](https://hayzlabs.com/guides/agents-near-your-email-and-files/).

## Try this next

1. Run the same ten tasks through the small model and your usual one, and compare: [Test any new model on your own tasks](https://hayzlabs.com/workflows/personal-eval-set/).
2. Set a calendar reminder for the day your key expires.

## Sources

1. Anthropic. Rate limits and spend limits. Vendor documentation. [Link](https://platform.claude.com/docs/en/api/rate-limits)
2. Anthropic. Workspaces. Vendor documentation. [Link](https://platform.claude.com/docs/en/build-with-claude/workspaces)
3. Anthropic. Get an API key, and Authentication. Vendor documentation. [Link](https://platform.claude.com/docs/en/get-api-key) · [Link](https://platform.claude.com/docs/en/manage-claude/authentication)
4. Anthropic. Models overview, Pricing, Create a message, Prompt caching. Vendor documentation. [Models](https://platform.claude.com/docs/en/about-claude/models/overview) · [Pricing](https://platform.claude.com/docs/en/about-claude/pricing) · [API](https://platform.claude.com/docs/en/api/messages/create) · [Caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
5. OpenAI. Spend limits, and Production best practices. Vendor documentation. [Link](https://developers.openai.com/api/docs/guides/spend-limits) · [Link](https://developers.openai.com/api/docs/guides/production-best-practices)
6. OpenAI. Managing projects, and Best practices for API key safety. Help centre. [Link](https://help.openai.com/en/articles/9186755-managing-your-work-in-the-api-platform-with-projects) · [Link](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety)
7. OpenAI. Quickstart, Models, Pricing, Reasoning, Prompt caching, Batch. Vendor documentation. [Quickstart](https://developers.openai.com/api/docs/quickstart) · [Models](https://developers.openai.com/api/docs/models) · [Pricing](https://developers.openai.com/api/docs/pricing) · [Reasoning](https://developers.openai.com/api/docs/guides/reasoning) · [Caching](https://developers.openai.com/api/docs/guides/prompt-caching) · [Batch](https://developers.openai.com/api/docs/guides/batch)
8. Google. Gemini API billing, API keys and pricing. Vendor documentation. [Billing](https://ai.google.dev/gemini-api/docs/billing) · [Keys](https://ai.google.dev/gemini-api/docs/api-key) · [Pricing](https://ai.google.dev/gemini-api/docs/pricing)
9. GitHub. Secret scanning for partners, and supported patterns. Vendor documentation. [Link](https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning-for-partners) · [Link](https://docs.github.com/en/code-security/secret-scanning/introduction/supported-secret-scanning-patterns) · [2021 changelog](https://github.blog/changelog/2021-06-08-rubygems-adobe-and-openai-are-now-github-secret-scanning-integrators)
