AI API requests and safe response handling

Translate a bounded request, inspect completion and usage, and repair a guard that mistakes partial text for success. Required practice is a local simulation; provider examples are optional dated references.

Jump to practiceReading and practice work without the video.

Watch estimate: 11:41 · complete tutorial Read estimate: 14 min Practice estimate: 30 min Updated: 2026-10-06
In this lesson · 02 of 10All lessons in this track →

Saved in this browser. View your learning.

The video explains the main idea. Use the sources, instructions and checks below to complete the practice.

Complete tutorial11:41

Captions appear here during playback.

AI API requests and safe response handling

Translate a supported synthetic request and inspect text, usage and completion status. Distinguish simulated permanent and transient failures, and state live recovery limits. Record the actual runtime, fixture revision and verification result; identify the SDK/model evidence required for live reproduction.

Download transcriptOpen video file

Local synthetic cases; independently reviewed narration. Automated screen capture and actual isolated fixture replay. No live provider compatibility or learning-effect claim. · Reviewed 2026-10-06. Microsoft Mark · locally synthesized. On a small screen, use fullscreen for diagram details or read the transcript.

Read the descriptive transcript

0:00 · Outcome and preparation

In this lesson, you will translate a bounded request, read its text, status and usage, and decide whether a result is ready for the next step. You will also reject an unsupported feature before a request is created and explain how permanent and transient failures differ. You need basic Python functions, environment variables and exceptions from the previous lesson. The required practice runs locally with Python and the standard library. It needs no keys, account, paid service or operational data. If Python is unavailable, trace the fixtures on paper and label execution unverified. We will demonstrate a provider-neutral contract. It is a teaching simulation, not the schema of a real provider. The page retains dated OpenAI and Anthropic examples, but they must be checked against the chosen SDK and model before any separately authorized live use. A local test pass does not prove an API round trip or a model's quality.

What appears on screen

Your local contract: Bounded request → provisional text → final status → inspect result Preparation: Python standard library, no keys/accounts/operational data. Local simulation, not a real provider schema. Test pass ≠ API compatibility or model quality.

1:05 · Define the bounded request

An API is an interface your code calls. For a generation task, specify the input and instructions, the supported features you need, an output bound and what your application expects back. Do not begin by assuming every provider or model supports the same options. Our fictional request asks for a short proposal from supplied logistics facts. It is a drafting request, not permission to send messages or change a system. Our simulated capability declaration supports streaming but does not support reasoning controls or schema output. Requesting an unsupported feature must fail before creation of the request. Tokens are units used by a model's tokenizer, not a guaranteed number of English words. A token limit bounds generation according to the endpoint's rules; it does not guarantee a complete or correct answer. The exact live fields and capabilities belong in the versioned reference. Our request contains only allowed fields. A feature typed into a generic dictionary is not automatically available. Watch `validate_request` return a clear error for an unsupported request before the reducer processes any response fixture. **Action:** Run the worked example with the unsupported capability fixture. Display validation error and no response processing.

What appears on screen

Open fixtures.json: { "capabilities": { "stream": true, "reasoning": false, "schema": false }, "request": { "input": "Fictional R1 logistics proposal; no savings measured", "max_output_tokens": 128, "stream": true } } Run python worked-example.py unsupported: request_rejected: Unsupported required capability: schema response_processed: false Observed capability rejection: Required schema unsupported No response processed Validation occurs before request creation.

2:33 · Map provider differences without guessing

Different APIs organize requests and responses differently. The lesson's dated examples place OpenAI Responses instructions and input in their respective fields, and Anthropic Messages system instructions separately from message content. These are illustrative reference examples, not the local fixture format. Check the actual version before implementation. An adapter translates a supported request into that interface and maps the returned fields to your application's contract. Preserve the request ID, text, terminal status, refusal information and known or unknown usage. Do not select behavior only by provider name or by whether the text is empty. If a feature is unsupported, reject it explicitly or choose a documented alternative. Do not silently drop it and claim equivalent behavior. History must be deliberately supplied or managed by the application according to the chosen interface. Neither a stronger model nor a chat subscription establishes API access, credit or permission to use real data. Our existing B3 offline pack lets you practice this mapping. Its Messages adapter is deliberately unfinished. Later you will implement it, compare against the worked example only after your attempt, and record what its tests do and do not establish.

What appears on screen

Dated provider reference: Responses-style: instructions + input Messages-style: system + messages Illustrative mappings; no SDK calls. Application adapter result: Request ID, text, terminal status, refusal, usage Preserve UNKNOWN usage Reject unsupported feature explicitly B3 learner starter: Messages adapter intentionally unfinished. Implement after your attempt. A chat subscription does not establish API access, credit or permission.

4:00 · Read a complete simulated response

Now run the complete fixture. These events come from a local JSON file. Nothing contacts a model. The reducer receives text chunks and a completed event. It preserves the fixture ID and usage. The final status is complete and no refusal occurred, so our application may pass this result to its next inspection step. That is transport-contract readiness, not factual correctness. Before using the draft, compare its claims with the supplied source. A complete response can still invent a result. Today's fixture uses authored text; its passing status does not measure model reliability. Notice that usage may be absent. Unknown usage should remain null or explicitly unknown; it must not become zero. Zero is a numerical claim. We record only safe metadata here, not raw keys or sensitive prompt content.

What appears on screen

Open worked-example.py: validate_request(request, capabilities) result = reduce_events(events) release = should_release(result) No network imports. Local fixtures only. Run python worked-example.py complete: request_id: mock-01 text: "We propose a two-week trial. Savings remain unknown." status: complete refusal: false usage: {"input_tokens":31,"output_tokens":12} release: true scope: local synthetic fixture only Inspect the complete draft: Transport-contract readiness only. Compare claims against supplied source. Unknown usage remains null, not zero.

5:00 · Streaming and the misleading success

Streaming lets an interface show text before the response has completed. That can make progress visible, but chunks are provisional. The cancelled fixture contains readable text and then stops with cancellation. The reducer reports partial, preserves the request ID and leaves missing usage unknown. It does not release the text as a finished result. Here is the mistake we want to catch: “If there is any text, the call succeeded.” Our starter deliberately makes that decision. It would accept the cancelled fixture because the text is nonempty. Predict what happens with a refusal containing explanatory text. A refusal is also not a completed answer for the requested task. The same shortcut would accept it. Repair the decision by checking the terminal status and refusal flag, not merely the string. For this local contract, release requires complete status and no refusal. The code also rejects unknown or contradictory event sequences. A stream that ends without a terminal event stays partial. An error after completion is invalid in our fixture contract. These are local validation rules, not a claim that every SDK emits the same event sequence. **Action:** Show the worked release condition and rerun cancelled, refusal, incomplete and complete. Demonstrate rechecking a repaired rule, not just displaying it.

What appears on screen

Run python worked-example.py cancelled: request_id: mock-02 text: "We propose a trial" status: partial refusal: false usage: null release: false scope: local synthetic fixture only Run python starter.py refusal: DELIBERATELY FLAWED before repair request_id: mock-04 text: "I cannot fulfill that request." status: refused refusal: true usage: null release: true scope: local synthetic fixture only Edit isolated starter.py: BEFORE: return bool(result["text"]) AFTER, equivalent multiline formatting: return ( result["status"] == "complete" and result["refusal"] is False ) Original learner starter remains unfinished. Rerun repaired guard: cancelled: status=partial, refusal=false, release=false, usage=null refusal: status=refused, refusal=true, release=false, usage=null incomplete: status=partial, refusal=false, release=false, usage={"input_tokens":31,"output_tokens":128} complete: status=complete, refusal=false, release=true, usage={"input_tokens":31,"output_tokens":12}

6:31 · Failures and bounded recovery

Permanent authentication, permission or invalid-request failures need correction. Repeating the same broken request is not a repair. Some connection, rate-limit or server failures may be transient, but retry policies depend on the endpoint, SDK and operation. Check the installed version and choose one owner for retries. Do not wrap an SDK retry loop in another loop without understanding how the attempts multiply. A timeout per attempt is different from an overall deadline. A budget caps retry attempts and cost exposure; it does not establish that a real network deadline was enforced. Also distinguish generating a draft from a consequential downstream action. Retrying text generation is not permission to resend a message, make a purchase or repeat a transaction. Our lab does none of those actions. A cancelled or ambiguous action needs its own safe recovery policy. Use the existing B3 pack to inspect permanent failures, transient recovery and simulated budget/deadline failures. Those tests simulate policy decisions; they do not establish live transport behavior. If you cannot explain the retry owner and stopping rule, do not claim your application is safely bounded.

What appears on screen

Failure decisions: Authentication / permission / invalid request → fix cause Selected transient failure → bounded retry if safe Ambiguous downstream action → separate recovery policy Retry boundaries: One retry owner Per-attempt timeout ≠ overall deadline Simulated budget ≠ enforced HTTP deadline No transaction or sending permission B3 observed worked test summary: Ran 15 tests in 0.001s OK

7:54 · Reproduce and inspect the evidence

Run the supplied worked-example tests locally. Record the Python version, fixture revision, command and observed output. Do not copy a passing result from the page and call it your execution. For the existing B3 pack, read the README first. Run its tests against the starter to observe the unfinished adapter. After your attempt, set `LAB_IMPL` to `worked_example` and rerun to inspect the supplied solution. Clear that setting before checking your starter again. Keep your original failure, correction and recheck separate. Our supplemental streaming fixtures exercise a smaller release decision. They complement the existing adapter practice; they do not replace the full B3 tests. Passing both still leaves live SDK integration, actual provider capabilities, transport timing and factual output quality unverified. If you choose live practice in the future, use separately authorized data and access, verify the model and SDK, pricing and limits, and record the actual request and response safely. This tutorial does not ask you to create an account or run a paid call. A storage option in an API request is not a blanket promise of zero retention; review the current provider policy and organizational approval.

What appears on screen

Worked-example test summary: Python 3.14.2 Ran 11 tests in 0.002s OK Revision: tutorial-pilot-2026-10-06 Observe unfinished B3 adapter: NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures NotImplementedError: Implement the messages mapping using the contract and fixtures Ran 15 tests in 0.004s FAILED (errors=7) Inspect supplied solution after attempt: Ran 15 tests in 0.001s OK Clear LAB_IMPL before checking starter. Evidence and limits: Record fixture/runtime, command, observed output, correction. No live SDK compatibility, provider capabilities, transport timing or factual quality proved.

9:21 · Independent practice

Pause before the solution chapter. First, repair the starter's release decision. It should preserve the reducer's IDs, status and unknown usage while refusing partial, failed, refused or invalid results. Add an explanation of why nonempty text is insufficient. Second, use the B3 pack to implement the unfinished adapter without changing expected tests. Independently reject an unsupported capability before request creation and write a migration note explaining what would change if a capability became available. Keep your code, first failure, corrected result, version information and limitation note. Do not edit expected answers to make the tests green. If you only trace on paper, label that honestly and predict each final status and release decision.

What appears on screen

Independent practice · pause: Repair should_release in your own copy. Preserve ID / status / unknown usage. Reject partial, failed, refused, invalid. B3: implement unfinished adapter; keep expected tests. Your evidence record: First failure → correction → recheck Version + fixture revision + limitation Reject unsupported feature before request creation Paper trace? Label execution unverified.

10:15 · Feedback and transfer

If cancelled text was released, your decision used appearance instead of completion status. If a refusal was released, check both the status and refusal flag. If missing usage became zero, restore unknown. If unsupported capabilities reached processing, move validation before creation. Recheck the full fixture set after the repair. The completed rule in this teaching contract is complete status and no refusal. That is one guard in a larger workflow. A real adapter must also handle the actual SDK's events and errors, validate required structured output and inspect the content's factual support. Our small reducer is not production code or a comprehensive security boundary. Now explain an unfamiliar case without looking at the code: text arrives, no terminal event arrives, and the connection ends. What should your application report? What remains unknown? What would you check before doing anything with the text? You have practiced the transferable distinction between provisional output and an inspected result. Reviewing the lesson does not certify a live integration. Save the evidence record and move to the next lesson when you can explain your guard and its limits. Use the dated provider references when you are ready for separately authorized integration.

What appears on screen

Feedback · solution ahead: Nonempty text is insufficient. Check complete status AND no refusal. Restore unknown usage; validate features first. Observed repair test summary: Before repair: failure expected Ran 2 tests in 0.009s FAILED (failures=6) After isolated repair: Ran 2 tests in 0.001s OK Transfer the guard: Text arrives; no terminal event; connection ends. Report partial; usage remains unknown if absent. Inspect support and permissions before any action.

Watching explains the example. Complete the lesson practice to check what you can do. Explore all visual lessons →

Jump to practice · Download practice and rubric · Searchable reference

Disclosure: all people, facts, drafts and events are fictional teaching materials. Video narration is synthesized locally, not a human instructor recording. Reading and practice require no video, account or paid service.

Outcome and preparation

Preparation: Basic Python functions, environment variables and exceptions; Python 3.11+ with standard library. If Python is unavailable, trace on paper and label execution unverified. No paid tools or real operational data.

What you will be able to do

  • Translate a supported synthetic request and inspect text, usage and completion status.
  • Distinguish simulated permanent and transient failures, and state live recovery limits.
  • Record the actual runtime, fixture revision and verification result; identify the SDK/model evidence required for live reproduction.

Keep repaired code, first/corrected results, actual runtime/fixture revision and limitation note. This is a teaching contract, not a live SDK parser or security boundary. Watching and review completion do not establish demonstrated capability.

Define the bounded request

An API is an interface your code calls. For a generation task, specify the input and instructions, the supported features you need, an output bound and what your application expects back. Do not begin by assuming every provider or model supports the same options.

Our fictional request asks for a short proposal from supplied logistics facts. It is a drafting request, not permission to send messages or change a system. Our simulated capability declaration supports streaming but does not support reasoning controls or schema output. Requesting an unsupported feature must fail before creation of the request.

Tokens are units used by a model’s tokenizer, not a guaranteed number of English words. A token limit bounds generation according to the endpoint’s rules; it does not guarantee a complete or correct answer. The exact live fields and capabilities belong in the versioned reference.

Our request contains only allowed fields. A feature typed into a generic dictionary is not automatically available. Inspect validate_request: it returns a clear error for an unsupported request before the reducer processes any response fixture.

Inspect the synthetic input

{
  "input": "Fictional team: six people, 40 requests weekly, about 18 minutes each. Propose a two-week trial; no savings have been measured.",
  "max_output_tokens": 128,
  "stream": true
}

Capability declaration: stream: true, reasoning: false, schema: false. Predict the error before running:

python worked-example.py unsupported

Validation rejects unsupported schema before reducing events; this is a fixture check, not a provider rejection.

Map provider differences

Different APIs organize requests and responses differently. The lesson’s dated examples place OpenAI Responses instructions and input in their respective fields, and Anthropic Messages system instructions separately from message content. These are illustrative reference examples, not the local fixture format. Check the actual version before implementation.

An adapter translates a supported request into that interface and maps the returned fields to your application’s contract. Preserve the request ID, text, terminal status, refusal information and known or unknown usage. Do not select behavior only by provider name or by whether the text is empty.

If a feature is unsupported, reject it explicitly or choose a documented alternative. Do not silently drop it and claim equivalent behavior. History must be deliberately supplied or managed by the application according to the chosen interface. Neither a stronger model nor a chat subscription establishes API access, credit or permission to use real data.

Our existing B3 offline pack lets you practice this mapping. Its Messages adapter is deliberately unfinished. Later you will implement it, compare against the worked example only after your attempt, and record what its tests do and do not establish.

Read a complete simulated response

Now run the complete fixture. These events come from a local JSON file. Nothing contacts a model. The reducer receives text chunks and a completed event. It preserves the fixture ID and usage. The final status is complete and no refusal occurred, so our application may pass this result to its next inspection step.

That is transport-contract readiness, not factual correctness. Before using the draft, compare its claims with the supplied source. A complete response can still invent a result. Today’s fixture uses authored text; its passing status does not measure model reliability.

Notice that usage may be absent. Unknown usage should remain null or explicitly unknown; it must not become zero. Zero is a numerical claim. We record only safe metadata here, not raw keys or sensitive prompt content.

Run and compare

python --version
python worked-example.py complete

Expected fixture trace: ID mock-01; complete status; release true; text “We propose a two-week trial. Savings remain unknown.”; supplied usage 31 input and 12 output tokens. Record your observed output; the expected trace is not your execution evidence.

Streaming and the misleading success

Streaming lets an interface show text before the response has completed. That can make progress visible, but chunks are provisional. The cancelled fixture contains readable text and then stops with cancellation. The reducer reports partial, preserves the request ID and leaves missing usage unknown. It does not release the text as a finished result.

Here is the mistake we want to catch: “If there is any text, the call succeeded.” Our starter deliberately makes that decision. It would accept the cancelled fixture because the text is nonempty. Predict what happens with a refusal containing explanatory text.

A refusal is also not a completed answer for the requested task. The same shortcut would accept it. Repair the decision by checking the terminal status and refusal flag, not merely the string.

For this local contract, release requires complete status and no refusal. The code also rejects unknown or contradictory event sequences. A stream that ends without a terminal event stays partial. An error after completion is invalid in our fixture contract. These are local validation rules, not a claim that every SDK emits the same event sequence.

python worked-example.py cancelled
python starter.py refusal

The deliberately unfinished starter contains return bool(result["text"]). Predict why this accepts provisional/refusal text before changing only should_release in your own copy. Keep the distributed starter unfinished and recheck all fixtures after repair.

Failures and bounded recovery

Permanent authentication, permission or invalid-request failures need correction. Repeating the same broken request is not a repair. Some connection, rate-limit or server failures may be transient, but retry policies depend on the endpoint, SDK and operation. Check the installed version and choose one owner for retries.

Do not wrap an SDK retry loop in another loop without understanding how the attempts multiply. A timeout per attempt is different from an overall deadline. A budget caps retry attempts and cost exposure; it does not establish that a real network deadline was enforced.

Also distinguish generating a draft from a consequential downstream action. Retrying text generation is not permission to resend a message, make a purchase or repeat a transaction. Our lab does none of those actions. A cancelled or ambiguous action needs its own safe recovery policy.

Use the existing B3 pack to inspect permanent failures, transient recovery and simulated budget/deadline failures. Those tests simulate policy decisions; they do not establish live transport behavior. If you cannot explain the retry owner and stopping rule, do not claim your application is safely bounded.

Reproduce and inspect the evidence

Run the supplied worked-example tests locally. Record the Python version, fixture revision, command and observed output. Do not copy a passing result from the page and call it your execution.

For the existing B3 pack, read the README first. Run its tests against the starter to observe the unfinished adapter. After your attempt, set LAB_IMPL to worked_example and rerun to inspect the supplied solution. Clear that setting before checking your starter again. Keep your original failure, correction and recheck separate.

Our supplemental streaming fixtures exercise a smaller release decision. They complement the existing adapter practice; they do not replace the full B3 tests. Passing both still leaves live SDK integration, actual provider capabilities, transport timing and factual output quality unverified.

If you choose live practice in the future, use separately authorized data and access, verify the model and SDK, pricing and limits, and record the actual request and response safely. This tutorial does not ask you to create an account or run a paid call. A storage option in an API request is not a blanket promise of zero retention; review the current provider policy and organizational approval.

python --version
python -m unittest discover -p "test_reference.py"
python -m unittest discover -p "test_learner.py"

Reference tests are expected to pass; learner tests deliberately fail before repair. Fixture revision: tutorial-pilot-2026-10-06. Keep observed results, not copied green output.

Runtime and fixture revision:
Command:
Prediction:
Observed first result:
Correction and reason:
Observed recheck:
What executed locally:
What remains unverified:

Independent practice

Interactive practice / Fictional fixture

Record your predictions and your repair

Use the fixtures in the practice pack. Enter what each one ends as, then explain your repair. The full table unlocks after you have written your explanation and run the checks.

Everything runs in this browser. No AI model is called and no work is uploaded. Bounded checks cover specific facts and rules. Your writing is not graded: you review it yourself. Practice never marks a lesson complete automatically.

Name the conditions your guard checks and one fixture the starter released by mistake.

Try the task, run the checks, then revise. Checks are teaching feedback, not an instructor grade.

Feedback and transfer

Hint — before the solution

Text arrived, but how did the fixture end? Keep status and refusal distinct from the string; missing usage remains unknown.

Solution and specific feedback — after your attempt
FixtureStatusReleaseUsage
completecompletetruesupplied counts
cancelledpartialfalseunknown
incompletepartialfalsesupplied counts
refusalrefusedfalseunknown
failedfailedfalseunknown
unterminatedpartialfalseunknown
malformedinvalidfalseunknown
unsupportedrequest rejected before reductionnonenone

The deliberate starter bug is bool(result['text']). It accepts several bad outcomes. Reference tests should pass; learner tests should fail until the guard changes. Do not mark deliberate failures as a package defect or disguise them as successful execution.

Do this task before opening the feedback. Different wording can meet the same criteria.

Download public feedback separately.

Retrieve without the solution: text arrives, no terminal event arrives, then the connection ends. Explain status, unknown usage and checks before acting. Continue to local models.

Optional technical depth and current setup references

Optional searchable reference

A model generates output. A product combines it with an interface and features. A harness supplies context, orchestration and controls. The execution environment determines where tools run and what they can reach. No model name establishes tool availability, retrieved evidence or authority to act. Training and request context are separate; tokens are pieces of text. OpenAI tools describes connected capabilities.

Dated provider examples — reference only, not executed

Primary documentation reviewed 2026-10-06 without paid calls. OpenAI Responses uses instructions and input; Anthropic Messages uses top-level system and messages. These are illustrative field mappings, not a finished integration or the local fixture schema:

# REFERENCE ONLY. Not executed; verify the selected SDK/model and authorization.
# client.responses.create(instructions="...", input="...", max_output_tokens=128)
# client.messages.create(system="...", messages=[{"role":"user","content":"..."}],
#                        model="<verify supported model>", max_tokens=128)

OpenAI Responses migration explains the separate instruction/input fields, output helpers and history limits; previous-response linkage does not carry prior top-level instructions. Claude Messages documents system and conversational turns. Inspect multipart content, final status, refusal and usage; a snippet printing chunks alone is an unfinished illustration with no release gate.

Claude Python SDK documents configurable retries and timeouts. Check your installed SDK and choose one retry owner. No numeric OpenAI retry defaults or installed SDK compatibility are asserted here. An attempt timeout is not an overall deadline; local simulations enforce neither actual transport nor real costs.

OpenAI data controls distinguishes application-state storage from abuse-monitoring controls. store=false is not a blanket zero-retention promise. Verify current policy and organizational approval; never put keys in source, logs or screenshots. A future live task needs separately authorized data, access and bounded cost; no account or paid call is requested here. Fixture preference is not evidence of provider/model quality.

Substantive source review: 2026-10-06. All required cases are fictional and offline. Participant testing and learning-effect claims remain pending. Delayed-transfer tasks are held separately from public feedback for evaluation.

Lesson status:PRACTICE REVIEW REQUIRED

Review your practiceComplete the knowledge check