ChatGPT API System Role: What It Is and How to Use It
The ChatGPT API system role is not a greeting. It is a hidden instruction message that tells the model how to behave: its persona, tone, rules and output format. Users never see it, and you must resend it with every request.
- The system message is an instruction to the model, never a greeting and never shown to the end user unless your code displays it.
- The API is stateless, so send the system message first in the messages list on every request, together with the conversation history.
- Specific prompts with a task, audience, constraints and output format beat vague ones like 'You are a pro'.
- Newer models use the developer role (or the Responses API instructions field), and the hierarchy is system/developer, then user, then assistant.
- A system prompt is not a security boundary: never put secrets in it, and expect users to be able to override or extract it.
What is the system role in the ChatGPT API?
The system role is a channel for instructions from you, the developer, to the model. When the docs show "You are a helpful assistant.", that is not a greeting to the user. It is a directive that shapes every answer that follows.
The end user never sees it unless your application prints it. The model reads it as the highest-priority guidance about who it should be and how it should respond.
The chat completions API has three main roles:
| Role | Who writes it | Purpose |
|---|---|---|
| system | You, the developer | Persona, tone, rules, constraints, output format |
| user | The end user (or your code on their behalf) | The actual request or question |
| assistant | The model, or you when seeding | Previous replies, conversation history, few-shot examples |
How do I use a system message? Same question, three personas
Put the system message first in the messages list, then the user turn. The example below asks one question under three different system prompts.
The wording of the answers will vary from run to run, but the style difference is consistent.
from openai import OpenAI
client = OpenAI()
question = "What is an API rate limit?"
personas = {
"pirate": "You are a pirate. Answer in pirate speech, max 2 sentences.",
"concise expert": "You are a senior backend engineer. Answer in one precise sentence, no analogies.",
"friendly tutor": "You are a patient tutor for beginners. Explain with a simple everyday analogy, then give one example. Keep it under 80 words.",
}
for name, system_prompt in personas.items():
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": question},
],
)
print(name, "->", resp.choices[0].message.content, "\n")How do I show a greeting if I want one?
If you want the chat to open with 'Hi, how can I help?', put it in the history as an assistant message, or render it in your UI without sending it to the model at all.
Do not put the greeting in the system message. The model would treat it as an instruction, not as something it already said.
messages = [
{"role": "system", "content": "You are a support assistant for Acme billing."},
{"role": "assistant", "content": "Hi! I can help with invoices and payments. What do you need?"},
{"role": "user", "content": "I was charged twice."},
] What should go in a good system prompt?
Cover the things the model cannot guess. Not every prompt needs all of them, but the strong ones state most of these:
Role or persona, the audience, tone, output format (JSON schema, bullet count, length), scope and refusals (what to decline and how), domain knowledge or policy, and context such as today's date.
Vague prompts leave the model to fill gaps with generic defaults. Specific prompts replace guesses with decisions.
| Vague | Specific rewrite |
|---|---|
| You are a pro. | You are a tax-support agent for freelancers in Germany. Answer in plain language for non-accountants, in at most 5 bullet points. |
| Be short and helpful. | Answer in under 60 words. If the question needs more detail, ask one clarifying question instead of guessing. |
| Return JSON. | Return only a JSON object with keys "summary" (string, max 25 words) and "tags" (array of 3 lowercase strings). No prose, no code fences. |
| Only talk about our product. | Answer only questions about Acme billing. For anything else reply exactly: "I can only help with Acme billing." |
Does the system role persist across the conversation?
No. The API is stateless: the model remembers nothing between calls. Every request must include the system message plus the full history you want it to consider.
Keep the system message first in the list, then the alternating user and assistant turns.
Because it is re-sent each time, a changing history does not drop it, but only if your code keeps it. A frequent bug is trimming old messages by slicing the list and accidentally cutting the system message.
system = {"role": "system", "content": "You are a concise Python tutor."}
history = []
def chat(user_text):
history.append({"role": "user", "content": user_text})
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[system] + history[-20:], # system always first, history trimmed
)
reply = resp.choices[0].message.content
history.append({"role": "assistant", "content": reply})
return reply System vs developer role, and the Responses API instructions field
Newer models, starting with the o1 reasoning family, introduced the developer role for the same job. OpenAI describes an instruction hierarchy in which system/developer messages outrank user messages, which outrank assistant content. For these models, a system message is generally treated as a developer message, so old code usually keeps working, but new code should use developer.
The Responses API adds a top-level instructions parameter that plays the same part. One detail matters: instructions apply only to the current request, so they are not carried over by previous_response_id, and you should resend them each call.
Use the form that matches the API and model you are calling:
| API / model | Where instructions go |
|---|---|
| Chat Completions, gpt-4o and older | role: "system" |
| Chat Completions, o-series and newer | role: "developer" (system usually accepted and mapped) |
| Responses API | instructions="..." parameter, or a developer message in input |
resp = client.responses.create(
model="gpt-4o-mini",
instructions="You are a concise expert. Answer in one sentence.",
input="What is an API rate limit?",
)
print(resp.output_text)What are the limits and common mistakes?
A system prompt is guidance, not a guarantee and not a security boundary. Users can try prompt injection ('ignore previous instructions'), and clever prompting can extract the text. Older models such as gpt-3.5-turbo weighted the system message less than gpt-4 class models, so rules were broken more often there.
Every system token is billed as input and uses context window on every call. A 1,500-token prompt across 10,000 requests is 15 million input tokens, so keep it tight.
System or user? Put stable rules and persona in system. Put the specific task and changing data in the user message. Per-request details like a customer's name or order belong in the user turn, not in a system prompt you keep rewriting.
| Mistake | Why it hurts | Fix |
|---|---|---|
| Contradictory rules (be brief / be thorough) | Model picks one unpredictably | Set priorities: 'Be brief unless asked for detail' |
| Very long prompts | Cost, and rules get diluted | Cut to the rules that change behavior |
| Expecting guaranteed compliance | Models are probabilistic | Validate output in code; use structured outputs |
| Forgetting to resend it | Persona vanishes mid-chat | Prepend it on every call |
| Putting secrets or API keys in it | Can be extracted | Keep secrets server-side, enforce permissions in code |
Common questions
Is the system message shown to the user?
No. It is sent to the model only, and users see it only if your app displays it or the model is tricked into repeating it. Treat it as visible-in-principle.
Do I need a system message at all?
No, it is optional. Without it the model uses its default behavior, which is fine for simple tasks, but a short system message gives far more consistent tone and format.
Can I use more than one system message?
Yes, the API accepts several, but one well-organized message at the start is easier to maintain and reason about. Splitting rules across scattered messages invites contradictions.
Should instructions go in the system or the user message?
Use system for rules that apply to every turn, such as persona and format. Use the user message for the one-off task and for data that changes per request.
How long should a system prompt be?
As short as it can be while stating every behavior you need, often 50 to 300 tokens. Add a rule only when you have seen a failure it fixes, and remove rules that no longer change outputs.