# AI APIs for Beginners: Make a Request and Handle the Response

> Make a Claude Messages API request in Python, validate its JSON response, and test authentication, rate limits and bad output with local fixtures.

- Source: https://www.zarifautomates.com/blog/complete-guide-ai-apis-beginners
- Published: 2026-09-17
- Updated: 2026-09-17
- Pillar: Agents & AI Engineering
- Tags: agent-course, ai-engineering, practical-guide
- Author: Zarif

---

An AI API lets your application ask a model to do a task. The useful part is getting the answer back into your own workflow: a support ticket becomes a proposed label, a document becomes extracted fields, or a question becomes a draft response.

Start with one small request. This lesson builds a Python client that asks Claude to classify a synthetic support ticket, then accepts only three possible labels. You will also run eight local HTTP cases so you can see what happens when authentication fails, a request is rate-limited, or the answer cannot be used.

[Download the complete Python example](/downloads/agent-course/api_lesson.py). The default command runs the local tests. A separate `--live` option sends one request to Anthropic; it requires your own API account and may incur API charges.

## What you need

Use Python 3.10 or newer and a terminal. The example uses Python's standard library, so there is no package installation. Save the download as `api_lesson.py` in a new folder.

For an actual model request, you also need an Anthropic API key, an enabled model, and available API billing allowance. Keep the key on your machine. Do not put it in browser JavaScript, a source file, a screenshot, or a Git commit. The local exercises need no key and make no model calls.

## 1. Read the request you are about to send

The [Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) accepts an HTTP `POST` to `https://api.anthropic.com/v1/messages`. The request has three headers that matter here:

| Header | Purpose |
| --- | --- |
| `x-api-key` | Authenticates your API account |
| `anthropic-version: 2023-06-01` | Selects the documented API version |
| `content-type: application/json` | Identifies the request body's format |

The model name and output limit belong in the JSON body. The download creates this structure, with a system instruction that limits the response to one label:

```python
payload = {
    "model": model,
    "max_tokens": 1024,
    "system": 'Return only JSON with one key, "label": billing, technical, or other.',
    "messages": [{
        "role": "user",
        "content": "I was charged twice for invoice INV-DEMO-42."
    }],
}
```

The full instruction also identifies ticket text as data and prohibits taking actions. That helps describe the task; the application still validates the answer. A customer can put misleading instructions in a ticket, and a model can return the wrong label even when the JSON is valid.

`max_tokens` limits output tokens, not the total price of a request. This example makes one attempt and does not retry automatically. Check the model and account limits before enabling the live path.

## 2. Run the client against a local HTTP server

```bash
python3 api_lesson.py
```

If your system names Python 3 `python`, use that command instead.

The script starts a temporary server on `127.0.0.1`, sends actual HTTP requests to it, and shuts it down when the checks finish. It checks the API-version header, fixture key, message body and output limit. Its responses are synthetic examples of the provider's protocol.

The result starts with:

```json
{
  "mode": "local HTTP fixtures; no model call",
  "passed": 8,
  "results": [
    {"case": "ok", "outcome": "billing"}
  ]
}
```

The real output includes all eight rows. These checks were executed on September 17, 2026. They establish that the client handles the fixtures; they do not measure Claude's classification accuracy or prove that your API account can authenticate.

## 3. Understand the response before using it

A successful Messages response contains content blocks, a stop reason and usage information. Here is the shortened successful fixture used by the example:

```json
{
  "type": "message",
  "stop_reason": "end_turn",
  "content": [{"type": "text", "text": "{\"label\":\"billing\"}"}],
  "usage": {"input_tokens": 0, "output_tokens": 0}
}
```

The zero token counts are local fixture values. An actual response reports its own usage.

The client joins the text blocks, parses their JSON, and checks the application contract: exactly one key named `label`, with a value of `billing`, `technical`, or `other`. It rejects extra fields, malformed JSON, unknown labels and responses that did not finish with `end_turn`.

The accepted result is a proposal. The script does not update a support system or contact a customer. A later action needs its own authorization and review rules.

## 4. Inspect the failures

[Anthropic's error reference](https://platform.claude.com/docs/en/api/errors) distinguishes HTTP status errors and provides request IDs for diagnosis. The client keeps that ID and any `retry-after` header without printing the API key.

| Local case | Observed result | What to investigate with a real provider |
| --- | --- | --- |
| Valid response | `billing` accepted | Whether the label is correct for the ticket |
| HTTP 401 | Rejected; one attempt | Key validity and account access |
| HTTP 429 | Rejected; retry delay retained | Rate limit or applicable account limit |
| HTTP 500 | Rejected; one attempt | Provider status and whether another attempt is justified |
| Unsupported label | Rejected | Prompt, model behavior and allowed-label contract |
| Malformed JSON | Rejected | Response format; do not strip arbitrary text until it parses |
| `max_tokens` stop | Rejected | Output budget and task size |
| Refusal stop | Rejected | Whether the task is appropriate; do not treat refusal text as a label |

A connection timeout is a separate failure: the client cannot infer a successful result from it. The download includes a transport-error handler, but timeout behavior is not exercised by these eight fixtures. A production client can add bounded retries for selected failures, but that needs a deliberate cost and timeout policy. Retrying a request is another request.

## 5. Optionally send one real request

Only do this when you intend to use your own API account. The script reads an existing `ANTHROPIC_API_KEY` environment variable or prompts for the key with hidden input; it does not store it. Set `ANTHROPIC_MODEL` to a model your API account can use. The [official API overview](https://platform.claude.com/docs/en/api/overview) documents the Models endpoint for checking availability.

The example below uses `claude-sonnet-5`. Check the current model ID in the official documentation and your account before running it; availability and billing are account-specific.

```bash
export ANTHROPIC_MODEL="claude-sonnet-5"
# The script prompts privately for your key if needed.
python3 api_lesson.py --live
```

The live path uses the same parser as the local tests and is written to print the validated label, request ID and returned usage. The label may differ from the local fixture. This lesson's test evidence covers the local requests; no paid Anthropic call was executed for it.

If the model returns prose around the JSON, the strict parser will reject it. Keep that failure visible. For a larger integration, move to the provider's supported structured-output mechanism and test it with the model you select, rather than assuming that an instruction guarantees valid output.

## Your deliverable

In the same folder as `api_lesson.py`, keep its eight-case output and one additional fixture you wrote yourself. A useful next fixture is valid JSON containing an extra `send_email` field: the client should reject it without performing anything.

You now have a request, a response parser, and a failure table you can inspect. Continue with [webhook delivery](/blog/how-to-use-webhooks-ai-automation) to handle the reverse direction: another service initiating a request to your application.

## Related Guides

- [Agent Design Patterns: Run Four Control Flows and Their Failures](/blog/ai-agent-design-patterns-production-systems)
- [Choose an Agent Starter: Inspect Three Repositories and Test One](/blog/best-ai-agent-template-libraries-and-starters)
- [Agent Evaluation Tools: Compare Five Options on One Ticket Task](/blog/best-ai-agent-testing-and-evaluation-tools)

## Continue the course

Lesson 1 of 17.

Next lesson: [Build a Webhook Receiver: Verify Deliveries and Recover After Failure](https://www.zarifautomates.com/blog/how-to-use-webhooks-ai-automation.md).

[Browse available lessons](https://www.zarifautomates.com/blog/pillar/agents-and-ai-engineering#agent-course-heading).
