Before you start
You need an OpenAI platform account and an API key, which you create on the API keys page of the dashboard. Every official SDK reads the key from the OPENAI_API_KEY environment variable, so it never has to sit in your code. On macOS or Linux, run export OPENAI_API_KEY="your_api_key_here". In PowerShell, run setx OPENAI_API_KEY "your_api_key_here".
A key you create for yourself acts with your own access. For a deployed app, a scheduled job or CI, the API keys guide points to a project service account instead. That is a non-human identity that owns its own keys, so the workload does not depend on one developer staying on the team. The API shows a service-account key in full only once, when it is created, so it goes straight into a secret manager.
Scopes on a key can narrow what it may do, but they never grant access its owner lacks. Rotation follows a fixed order: issue a replacement, update the workload, confirm it works, then revoke the old key. The service account keeps its identity while its keys change. Separate projects can isolate resources, members and usage for different apps or environments. A workload that runs on an identity provider OpenAI supports can use workload identity federation and store no long-lived key at all.
The Python SDK needs Python 3.10 or later. The JavaScript SDK supports Node.js 22 and 24 LTS, plus Deno, Bun and Cloudflare Workers. Node.js 20 reached end of life on 30 April 2026 and is no longer supported. Browsers are blocked by default. The dangerouslyAllowBrowser option turns them on, and then anyone who opens the page can read your key.
Make the first call and read what comes back
The quickstart's first request is three moves: create a client, call responses.create with a model and an input, print output_text. Below is the Python version from the developer quickstart. In JavaScript the call is await client.responses.create({ model, input }), saved as example.mjs, with console.log(response.output_text) at the end.
What comes back is a response object with its own id, not a string. Its output property is a list of items. In the text guide's example for this exact prompt, the list holds one item. Its id starts with msg_, its type is message and its role is assistant. Inside sits a content list with one output_text part: the unicorn story, next to an empty annotations list where citations land when a tool supplies them.
The migration guide prints the same request run on gpt-5.5, and there the list starts with a reasoning item whose id begins with rs_. The message comes second. output_text is an SDK helper that joins every text part into one string, so it hides that difference. It suits a quick script. Code that adds tools or keeps history should walk the list instead, because the text guide warns that the reply is not reliably at output[0].content[0].text.
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input="Write a one-sentence bedtime story about a unicorn.",
)
print(response.output_text)- Export the key in the shell that will run the code.
- Install the SDK:
pip install openaifor Python, ornpm install openaifor JavaScript. - Save the code as
example.py, or the JavaScript version asexample.mjs. - Run
python example.pyornode example.mjs. One short story prints when the call returns. - Print
response.outputinstead ofoutput_textand read the item types behind that string.
| Item type | When it shows up | What your code does |
|---|---|---|
message | The model wrote text for the user | Read its text parts, or use output_text |
reasoning | A reasoning model worked before it answered | Keep it and send it back if you replay history yourself |
function_call | The model wants one of your functions run | Run it and reply with a function_call_output that carries the same call_id |
web_search_call or file_search_call | A built-in tool ran on OpenAI's side | Nothing to run, read the citations in the message |
Steer with instructions, then stream the reply
instructions is a top-level parameter for how the model should behave: tone, goals, examples of a good answer. It takes priority over the prompt in input. Sending it is roughly the same as putting a message with the developer role at the top of the input list. Developer messages carry your application's rules and rank ahead of user messages, which carry what the person typed. The model's own turns have the assistant role.
The text guide compares the two roles to code. A developer message is the function definition, holding the rules and business logic. A user message is the arguments that definition runs on.
It applies to one request only. A turn chained with previous_response_id does not see the instructions you sent earlier, so code that chains has to send them again on every call. The prompt itself belongs in your code, where it gets typed inputs, review and tests. OpenAI is retiring the reusable prompt objects some older tutorials store on the platform, as the timeline shows.
By default the API writes the whole answer before it sends one HTTP response, and on a long answer that wait is what a user sees. Streaming fixes the wait with one more flag. Set stream to true and the SDK gives you typed server-sent events, a one-way HTTP stream the server keeps writing to. The sample joins the text guide's instructions example with the streaming guide's event loop.
The docs' example also sets reasoning: { effort: "low" }. Reasoning effort tells a reasoning model how much to think before it answers, and lower effort favors speed and fewer tokens. Supported values depend on the model, and the reasoning guide gives medium as the default for GPT-6.1 Sol and GPT-6 Luna. In Python, pass stream=True and iterate with for event in stream, checking the same event.type names.
import OpenAI from "openai";
const client = new OpenAI();
const stream = await client.responses.create({
model: "gpt-6-astra",
reasoning: { effort: "low" },
instructions: "Talk like a pirate.",
input: "Are semicolons optional in JavaScript?",
stream: true,
});
for await (const event of stream) {
if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta);
} else if (event.type === "response.completed") {
console.log("\nResponse completed.");
} else if (event.type === "error") {
console.error(event.message);
}
}response.createdarrives once, when the response starts.response.output_text.deltaarrives many times, each with the next piece of text indelta.response.completedarrives once, when generation ends.errorcan arrive in the middle of a stream, so the loop needs its own branch for it.
Prompt creation de-emphasized
Reusable prompt objects begin their deprecation, and the text guide asks you to keep prompts in code.
Source: OpenAI Docs: Text generation (read November 5, 2026)
Assistants API sunset
It is no longer available, and the migration guide moves its users to the Responses API.
Source: OpenAI Docs: Migrate to the Responses API (read November 5, 2026)
The prompts endpoint shuts down
The date the text guide gives for v1/prompts to stop working.
Source: OpenAI Docs: Text generation (read November 5, 2026)
Once you start reading a stream, the Python SDK does not retry it, because a replay could repeat text your app already showed. Streaming also makes moderation harder, because a partial answer is harder to judge than a finished one. Moderation scores, if you ask for them, arrive only after the full output.
Keep a conversation going across turns
Ask "tell me a joke", then "explain why this is funny" in a second request, and the second request knows nothing about the first. Each call stands alone. You have three ways to give the model that context.
Manual replay is the plainest. Keep a list of items in your app, append the model's output after each reply, add the next user message and send the whole list as input. Nothing is stored for you, and nothing is hidden from you.
The lightest is previous_response_id: pass the id of the last response with only the new user input, and OpenAI supplies the earlier turns. It works because responses are stored by default, and stored response objects are kept for 30 days. The code is the conversation state guide's own example.
The chain saves you from resending text. It does not save you from paying for it: every earlier input token in the chain is billed as input again on the next turn. If you came from the Claude API course, the shift is real, because Anthropic's Messages API is stateless and your code always resends the history.
For a thread that must outlive one session or move between devices, create a conversation with client.conversations.create() and pass its id as conversation on each request. A conversation stores messages, tool calls and tool outputs as items under its own durable id.
All three hit the same ceiling. The context window is the most tokens one request can use, and it counts input, output and reasoning tokens together. A long thread grows toward it turn by turn, and output past the limit may be cut off. For that case the guide points to compaction, set on /responses with context_management and a compact_threshold. Its tokenizer tool counts how many tokens a string holds before you send it.
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input="tell me a joke",
)
print(response.output_text)
second_response = client.responses.create(
model="gpt-6-astra",
previous_response_id=response.id,
input=[{"role": "user", "content": "explain why this is funny."}],
)
print(second_response.output_text)| Way | Where the history lives | What you send each turn | How long it is kept |
|---|---|---|---|
| Manual replay | Your application | Every earlier item plus the new message | As long as you keep it, and store can be false |
previous_response_id | OpenAI, response by response | The last response id and the new input | Response objects, 30 days by default |
| Conversations API | A conversation object with its own id | The conversation id and the new input | Its items have no 30-day limit |
Replaying by hand with store set to false? Keep every item from output, the encrypted reasoning items included. In Python that is history += response.output before the next user message.
How Responses relates to Chat Completions
Chat Completions is the older endpoint, /v1/chat/completions, and it is still supported. The migration guide recommends Responses for all new projects, and some models settle the question: GPT-6 Astra and GPT-6.1 Sol only call tools through Responses. Starting with GPT-5.4, Chat Completions also refuses tool calls when the reasoning effort is anything other than none.
The guide frames a port as three changes. Requests go to /v1/responses, output is read from the typed output list, and you choose how the app carries state between turns. Text-only message lists port without edits, since the same array works as messages in one API and as input in the other.
Everything else changes shape, and the table is the map. Where Chat Completions glues text, tool calls and metadata into one message object, Responses splits them into separate items. The migration guide has side-by-side samples for every row.
Storage differs as well. Responses are stored by default, while Chat Completions are stored by default only for new accounts, and store: false turns storage off in either API. Two gaps catch people porting old code. Responses has no n, so ask more than once when you want several candidates. And the Assistants API is gone, so code that still calls it needs a rewrite on Responses.
| Chat Completions | Responses API |
|---|---|
A messages array | input, a string or a list of items |
| A system or developer message | Top-level instructions |
choices[0].message.content | output_text, or the message item in output |
| Tool calls inside the assistant message | A separate function_call item, matched by call_id |
response_format for a JSON schema | text.format |
Stream chunks with a delta field | Typed events such as response.output_text.delta |
Several generations with n | Not available, so make separate requests |
| Functions non-strict by default | Strict mode tried first when strict is left out |
What Atty Eleti and OpenAI Developers posted about the Responses API
Atty Eleti of OpenAI, one of the people who designed the endpoint, introduced the Responses API on X on 11 March 2025, the day it launched. He called it the new primitive of the OpenAI API, built on two years of lessons, and the foundation for building agents. His post opens a thread on how the team designed it. The typed item list from your first call is that primitive at work, so learn its types before you add tools.
OpenAI Developers posted on 26 August 2025 that the Assistants API beta would end one year later, with a guide for moving to Responses. The same post said Responses had already overtaken Chat Completions in token activity. That shutdown is the one in the timeline. Code that still calls Assistants needs a rewrite now, while Chat Completions code can move one flow at a time.
It’s our recommended path to integrate with the OpenAI API today, and for the future.

Screenshots of X's own embeds of the posts, taken on 5 November 2026, with the time in UTC. Select one to open the post on X. X cuts the OpenAI Developers post short with Show more.
Common mistakes
- Reading
choices[0].message.contentfrom a Responses object out of habit. - Taking
output[0]as the answer when a reasoning item sits in front of the message. - Dropping reasoning or tool items while replaying history by hand.
- Sending a chained turn without its instructions and wondering why the tone drifted.
- Passing
previous_response_idand also replaying the full transcript, so the model reads the same turns twice. - Expecting
previous_response_idto make earlier turns free, when they are billed as input on every call. - Feeding typed Responses events to an old stream handler that waits for
deltachunks. - Shipping the key to a public page with
dangerouslyAllowBrowser.
Try it yourself
- Print the type of every item in
response.outputfor a plain question. Which types come back fromgpt-6-astra? - Move your instructions into a
developermessage at the top ofinputand compare the two replies. - Chain three turns with
previous_response_idand leave outinstructionson the third. Does the tone hold? - Make one call with
store=False, then try to chain a second turn from its id. What happens? - Ask the same question with
reasoningeffort set tolowand thenhigh, and time how long the first streamed word takes. - Port one Chat Completions call from your own code with the table, then compare the outputs side by side.
Next
Lesson 2 gives the model tools: your own functions described in JSON Schema, plus the web search and file search that OpenAI runs for you. It starts from the function_call item in the first table and the call_id that pairs it with your result.
Every limit and bill in this lesson is counted in tokens. What are tokens in AI shows how text turns into them before it reaches a model, which helps when a chained thread starts to cost more than you planned.
Check yourself
You chain a second turn with previous_response_id. What happens to the instructions you sent on the first turn?
Show answer
B. They are not in the context, so you send them again instructions applies to the current request only, so a chained turn needs them sent again.
Which way of reading the reply holds up once reasoning or tools are involved?
Show answer
A. Loop over response.output and check each item type The output list can start with a reasoning item or a tool call, so code should switch on each item type.
Which streaming event carries the next piece of text?
Show answer
D. response.output_text.delta Each response.output_text.delta event holds the next piece of text in its delta field.
You move a Chat Completions request that uses a JSON schema to Responses. Where does the schema go?
Show answer
C. text.format Structured Outputs moved from response_format to text.format in the Responses API.
Frequently asked questions
Is the OpenAI API free to use?
Only for a first test. The quickstart calls your first request a free test, then sends you to the billing page to add credits for real use. After that, usage is metered in tokens and billed to your organization. Prices change often, so this lesson leaves them out.
Which OpenAI model should I start with?
On 5 November 2026 the models page suggests GPT-6 Astra for complex reasoning and coding, GPT-6.1 Sol to balance intelligence and cost, and GPT-6 Luna for high-volume work. Each lists a context window of 1,050,000 tokens and up to 128,000 output tokens. For production, the text guide asks you to pin a dated snapshot, such as gpt-5.5-2026-04-23, so behavior stays the same between releases.
What do the SDKs retry on their own?
Connection errors and the 408, 409, 429 and 500-level statuses, each retried twice by default with a short exponential backoff. Requests time out after 10 minutes. Change that with max_retries in Python or maxRetries in JavaScript, and log _request_id from failed calls, as the Python SDK README suggests.
Do I have to rewrite my Chat Completions code now?
Not in one go. The guide suggests moving one user flow at a time, starting with plain text generation, and comparing latency, token usage and errors before routing more traffic over. Any flow that needs function calling on the newest models moves first.
Sources
- OpenAI Docs: Developer quickstart https://developers.openai.com/api/docs/quickstart
- OpenAI Docs: Text generation https://developers.openai.com/api/docs/guides/text
- OpenAI Docs: Streaming API responses https://developers.openai.com/api/docs/guides/streaming-responses
- OpenAI Docs: Conversation state https://developers.openai.com/api/docs/guides/conversation-state
- OpenAI Docs: Migrate to the Responses API https://developers.openai.com/api/docs/guides/migrate-to-responses
- OpenAI Docs: Reasoning models https://developers.openai.com/api/docs/guides/reasoning
- OpenAI Docs: SDKs and CLI https://developers.openai.com/api/docs/libraries
- OpenAI Docs: API keys and service accounts https://developers.openai.com/api/docs/guides/api-keys
- OpenAI Docs: Models https://developers.openai.com/api/docs/models
- OpenAI Docs: GPT-6 Astra https://developers.openai.com/api/docs/models/gpt-6-astra
- openai/openai-python on GitHub https://github.com/openai/openai-python
- openai/openai-node on GitHub https://github.com/openai/openai-node
- X: Atty Eleti introduces the Responses API https://x.com/athyuttamre/status/1899541471532867821
- X: OpenAI Developers on winding down the Assistants API beta https://x.com/OpenAIDevs/status/1960409187122602172
- YouTube (OpenAI): Build Hour: Responses API https://www.youtube.com/watch?v=hNr5EebepYs


