Skip to content
Zarif Automates

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

ZarifZarif
|Published

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. 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 accepts an HTTP POST to https://api.anthropic.com/v1/messages. The request has three headers that matter here:

HeaderPurpose
x-api-keyAuthenticates your API account
anthropic-version: 2023-06-01Selects the documented API version
content-type: application/jsonIdentifies 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:

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

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:

{
  "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:

{
  "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 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 caseObserved resultWhat to investigate with a real provider
Valid responsebilling acceptedWhether the label is correct for the ticket
HTTP 401Rejected; one attemptKey validity and account access
HTTP 429Rejected; retry delay retainedRate limit or applicable account limit
HTTP 500Rejected; one attemptProvider status and whether another attempt is justified
Unsupported labelRejectedPrompt, model behavior and allowed-label contract
Malformed JSONRejectedResponse format; do not strip arbitrary text until it parses
max_tokens stopRejectedOutput budget and task size
Refusal stopRejectedWhether 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 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.

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 to handle the reverse direction: another service initiating a request to your application.

Zarif

Zarif

Zarif builds AI agents and automation workflows and writes about what holds up in production: the sources worth following, the roles the AI era is creating, and agent workflows you can inspect end to end.