# OpenAI API tutorial: the Responses API in Python and JavaScript

> The Responses API is the OpenAI API endpoint the docs recommend for new projects. You send a model id and an input, optionally with instructions, and get back a list of output items plus an `output_text` shortcut. This tutorial creates a key, installs the official Python and JavaScript SDKs, makes a first call, streams it and carries a conversation across turns.

Mindket editors · Published 2026-11-05 · Updated 2026-11-05 · https://mindket.com/learn/openai-api/openai-api-quickstart

## In short

- Keep the secret in an environment variable and away from any page a visitor can open.
- Switch on each item type in the output list once tools or reasoning join the request.
- Send your behavior rules again on every chained turn.
- Pick one place to hold history for each conversation and stay with it.
- Port older chat code in small slices, plain text first.

## Before you start

You need an [OpenAI platform](https://platform.openai.com/) account and an API key, which you create on the [API keys page](https://platform.openai.com/api-keys) 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](https://developers.openai.com/api/docs/guides/api-keys) 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](https://developers.openai.com/api/docs/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`.

````python
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)
````

1. Export the key in the shell that will run the code.
2. Install the SDK: `pip install openai` for Python, or `npm install openai` for JavaScript.
3. Save the code as `example.py`, or the JavaScript version as `example.mjs`.
4. Run `python example.py` or `node example.mjs`. One short story prints when the call returns.
5. Print `response.output` instead of `output_text` and 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](https://developers.openai.com/api/docs/guides/text)'s instructions example with the [streaming guide](https://developers.openai.com/api/docs/guides/streaming-responses)'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](https://developers.openai.com/api/docs/guides/reasoning) 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.

````javascript
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.created` arrives once, when the response starts.
- `response.output_text.delta` arrives many times, each with the next piece of text in `delta`.
- `response.completed` arrives once, when generation ends.
- `error` can arrive in the middle of a stream, so the loop needs its own branch for it.

**Older API surfaces and their dates**

1. **June 3, 2026** Prompt creation de-emphasized. Reusable prompt objects begin their deprecation, and the text guide asks you to keep prompts in code.
2. **August 26, 2026** Assistants API sunset. It is no longer available, and the migration guide moves its users to the Responses API.
3. **November 30, 2026** The prompts endpoint shuts down. The date the text guide gives for v1/prompts to stop working.

Sources: [OpenAI Docs: Text generation](https://developers.openai.com/api/docs/guides/text) (read 2026-11-05), [OpenAI Docs: Migrate to the Responses API](https://developers.openai.com/api/docs/guides/migrate-to-responses) (read 2026-11-05)

> 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](https://mindket.com/learn/claude-api/claude-api-quickstart), 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.

````python
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](https://developers.openai.com/api/docs/guides/migrate-to-responses) 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](https://x.com/athyuttamre/status/1899541471532867821) 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](https://x.com/OpenAIDevs/status/1960409187122602172) 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.
>
> OpenAI Developers, OpenAI's official account for developers, [X](https://x.com/OpenAIDevs/status/1960409187122602172) (read 2026-11-05)

[![Atty Eleti (@athyuttamre) on X, 11 March 2025: Introducing the Responses API: the new primitive of the OpenAI API. It is the culmination of 2 years of learnings designing the OpenAI API, and the foundation of our next chapter of building agents. Here is the story of how we designed it.](https://mindket.com/blog/shots/x-athyuttamre-responses-api.webp?v=18e0ff1d7e48)](https://x.com/athyuttamre/status/1899541471532867821)

Atty Eleti on X, 11 March 2025

[![OpenAI Developers (@OpenAIDevs) on X, 26 August 2025: We are winding down the Assistants API beta. It will sunset one year from now, August 26, 2026. We have put together a guide to help you migrate to the Responses API. The post adds that the Responses API has already overtaken Chat Completions in token activity.](https://mindket.com/blog/shots/x-openaidevs-assistants-sunset.webp?v=baaf4f0ad42d)](https://x.com/OpenAIDevs/status/1960409187122602172)

OpenAI Developers on X, 26 August 2025

[Build Hour: Responses API](https://www.youtube.com/watch?v=hNr5EebepYs)

OpenAI's Build Hour on the Responses API, from OpenAI's own YouTube channel.

> 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.content` from 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_id` and also replaying the full transcript, so the model reads the same turns twice.
- Expecting `previous_response_id` to 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 `delta` chunks.
- Shipping the key to a public page with `dangerouslyAllowBrowser`.

## Try it yourself

- Print the type of every item in `response.output` for a plain question. Which types come back from `gpt-6-astra`?
- Move your instructions into a `developer` message at the top of `input` and compare the two replies.
- Chain three turns with `previous_response_id` and leave out `instructions` on 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 `reasoning` effort set to `low` and then `high`, 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](https://mindket.com/learn/openai-api/openai-function-calling) 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](https://mindket.com/blog/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

1. You chain a second turn with previous_response_id. What happens to the instructions you sent on the first turn?

   A. They carry over on their own
   B. They are not in the context, so you send them again
   C. They turn into a developer message
   D. They make the request fail with a 400

   Answer: B. instructions applies to the current request only, so a chained turn needs them sent again.

2. Which way of reading the reply holds up once reasoning or tools are involved?

   A. Loop over response.output and check each item type
   B. Read output[0].content[0].text
   C. Read choices[0].message.content
   D. Read the first streaming event

   Answer: A. The output list can start with a reasoning item or a tool call, so code should switch on each item type.

3. Which streaming event carries the next piece of text?

   A. response.created
   B. response.completed
   C. error
   D. response.output_text.delta

   Answer: D. Each response.output_text.delta event holds the next piece of text in its delta field.

4. You move a Chat Completions request that uses a JSON schema to Responses. Where does the schema go?

   A. response_format
   B. instructions
   C. text.format
   D. tools

   Answer: C. 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](https://platform.openai.com/settings/organization/billing/overview) 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](https://developers.openai.com/api/docs/models) 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](https://github.com/openai/openai-python) 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) (read 2026-11-05)
- [OpenAI Docs: Text generation](https://developers.openai.com/api/docs/guides/text) (read 2026-11-05)
- [OpenAI Docs: Streaming API responses](https://developers.openai.com/api/docs/guides/streaming-responses) (read 2026-11-05)
- [OpenAI Docs: Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) (read 2026-11-05)
- [OpenAI Docs: Migrate to the Responses API](https://developers.openai.com/api/docs/guides/migrate-to-responses) (read 2026-11-05)
- [OpenAI Docs: Reasoning models](https://developers.openai.com/api/docs/guides/reasoning) (read 2026-11-05)
- [OpenAI Docs: SDKs and CLI](https://developers.openai.com/api/docs/libraries) (read 2026-11-05)
- [OpenAI Docs: API keys and service accounts](https://developers.openai.com/api/docs/guides/api-keys) (read 2026-11-05)
- [OpenAI Docs: Models](https://developers.openai.com/api/docs/models) (read 2026-11-05)
- [OpenAI Docs: GPT-6 Astra](https://developers.openai.com/api/docs/models/gpt-6-astra) (read 2026-11-05)
- [openai/openai-python on GitHub](https://github.com/openai/openai-python) (read 2026-11-05)
- [openai/openai-node on GitHub](https://github.com/openai/openai-node) (read 2026-11-05)
- [X: Atty Eleti introduces the Responses API](https://x.com/athyuttamre/status/1899541471532867821) (read 2026-11-05)
- [X: OpenAI Developers on winding down the Assistants API beta](https://x.com/OpenAIDevs/status/1960409187122602172) (read 2026-11-05)
- [YouTube (OpenAI): Build Hour: Responses API](https://www.youtube.com/watch?v=hNr5EebepYs) (read 2026-11-05)

[Browse AI agents](https://mindket.com/agents)
