Knowledge baseSeptember 8, 2026·updated September 8, 2026

OpenAI API: what the request consists of

Analysis of the request body and response fields: what determines the form of the result, where money on tokens leaks, why the response breaks off mid-sentence, and when repeating a request makes sense and when not.

What does this solve?

The first request to the model is assembled in two minutes by copy-pasting someone else’s curl, and this is enough before production. Then it begins: the answer breaks off in the middle of a sentence, 429 arrives once an hour, the monthly bill is twice as high as the estimate, and the model “does not hold the format.” Each of these four problems is caused by a specific request body field or a specific response field, which usually no one reads. Parsing the composition of the request is needed in order to pull the desired lever, and not to rewrite the prompt at random for the third time.

How it works

Vehicle and key

Outwardly, this is a regular HTTPS POST with JSON in the body. There are two required headings: Authorization: Bearer <ключ> i Content-Type: application/json. The key lives on the server. In a browser bundle or in a mobile application, it will be obtained in the evening, and the owner of the key will pay. The working scheme is the same: the client goes to his own backend, the backend goes to the provider, and he also calculates user limits and writes logs.

There are several endpoints - text generation (classic chat completions and a later format with a single input field), embeddings, audio, images, files, batch processing. The form is the same everywhere: JSON inside, JSON or event flow out.

Request body

FieldWhat it controlsWhen to touch
modelWhich model considers the request Addto the config: model names live their own lives, there is no need to re-roll the code because of them
messages (or input in a new format)the dialogue itself is an array of messages with rolesalways
tools, tool_choicedescriptions of the application functions that the model can call and the enforcement of the callwhen the answer must be based on system data rather than model memory
response_format / JSON schemaresponse form up to a strict schemealways when the answer is parsing the code and not a person
temperature, top_pscatter when choosing the next tokendown - data extraction, classification, calculations; above are draft texts
max_tokens / max_output_tokensresponse length ceilingalways set: this is also a cost-effective fuse
stoplines at which the generation is terminatedrigid formats, separators
streamgive answer in partsinterfaces where a living person is waiting
seedattempt to repeat outputtests and debugging
user, metadatarequest tag for logs and analysis of abusesmulti-user products

Roles and memory

Messages differ in roles. The system role (in new formats - the role of the developer) sets the framework: who is responsible, in what format, what not to do. The user has a task. Assistant - past responses of the model. The individual role returns the result of the tool call.

The server does not store the dialogue. Each request leaves entirely, with the entire history that the application has stitched together itself. Part of the API knows how to keep state on its side by reference to the previous response, but by default trimming old replicas, compressing them into summaries and ordering messages is the job of the application.

Tokens and money

Payment and limits are calculated in tokens, separately for entry and exit. Three things follow from this. The dialogue becomes more expensive with each remark, because the previous ones leave again. The ten-page regulations inserted into the system message are paid for each request, and not just once. And the constant part of the prompt is kept at the beginning and unchanged - this is how prefix caching works; everything that changes is sent to the tail.

What comes in response

In addition to the text in the response, there are two fields for which they read it. finish_reason tells why the generation ended: normal, hit the length limit, went into a tool call, got under the filter. usage shows the actual consumption of tokens for entry and exit. Trimmed JSON after finish_reason: length will not be parsed, and it is not the model that is to blame, but the lack of verification in the code.

Calling tools

Four-cycle cycle. First: the request leaves with a list tools —function name, description, JSON argument scheme. Second: the model does not return text, but a call - name, arguments and call identifier. Third: the application executes the function itself and adds a message to the history with the result and the same identifier. Fourth: the request is repeated with an extended history, and the model formulates an answer.

The model does not execute anything - it forms an intention, the decision to call the function is made by the code. Arguments come from the model and are checked like any external input: the model does not prevent you from sending a non-existent order ID or a negative quantity.

Streaming

At stream: true The response comes in a stream of server events: chunks with text deltas and a final marker. Treats “the interface is silent for ten seconds.” The fee is complicated error handling: the connection is already open, code 200 has already been sent, and a fall in the middle must be caught separately. The token counters in the stream come at the end and only if you ask for them with an explicit option.

Errors and repetitions

CodeWhat happenedReaction
400request body or schema not validfix the code, repeating is pointless
401, 403key, rights, regionrepair access
404model not available for projectcheck model name and access
429request limit, token limit or balance has run outpause and repeat with increasing delay
5xx, timeoutprovider siderepeat with increasing delay

It only makes sense to repeat 429 and 5xx: exponential backoff with random addition, finite number of attempts, respect for the title Retry-After. Repeated POST has the risk of performing the operation twice - the API has an idempotency header for this. The request ID from the response headers is always written to the log: without it there is nothing to talk about in support.

Useful scripts

  • Classification of incoming requests into a strict JSON schema with a confidence field and routing of controversial cases per person.
  • Extracting fields from letters, invoices and invoices into a structure ready for recording in the database.
  • Calling tools: catalog search, reading balances, draft order - the response is collected from system data, and not from the model’s memory.
  • Streaming the response to the chat interface so that the first words appear immediately, and not after full generation.
  • Batch processing of the archive: thousands of requests of the same type are sent as a file and are counted deferred, cheaper than individual calls.

Limitations

  • No status. Remembering the dialogue is the application’s job, and it also pays for the history at every step.
  • Context window is finite. Trimming, compression and priority of messages are written by hand; nothing is compressed automatically.
  • There will be no complete repeatability even at zero temperature and a given seed. Tests are based on checking the properties of the response, and not on byte-by-byte comparisons.
  • A strict scheme guarantees the form of the answer; The code checks the veracity of the values ​​inside it.
  • Limits on requests and tokens per minute make a naive loop for for thousands of documents that are inoperative - a queue with concurrency limitation is needed.
  • Data goes to someone else's circuit. For personal data and trade secrets, separate measures are needed: depersonalization, agreement, local model.
  • The connection to the provider is deeper than it seems: the names of the models, the format of the tools, the behavior of the filters, and the counting of tokens are different. The layer is made thin, but it is done.
  • The delay of someone else's service is not under control. A synchronous model call inside processing a web request will sooner or later run into a timeout.

How to check the result

  • Send minimal request via curl and read in response usage i finish_reason, not just text.
  • Specially expose tiny max_tokens and make sure the code sees the break and doesn't try to parse incomplete JSON.
  • Count prompt tokens locally before sending and check with usage —the discrepancy will show that excess is being included in the request.
  • Collect a golden set of 20–50 real examples with standard answers and run it after each prompt edit. Comparison “by eye” stops working on the third iteration.
  • Check retrays on a stub that sends 429: the number of attempts is finite, the delay is increasing, Retry-After is taken into account, the log does not turn into an endless loop.
  • Ensure that the request ID and token consumption are logged for each call, including unsuccessful ones.

Rule: everything that should be predictable is specified by the request fields and checked by the code; In words, only wishes are specified in the prompt.

  • openai
  • api
  • llm
  • integration