July 14, 2026
Fine-Tuning GPT-4 with OpenAI's API: A Current Guide
The fine-tuning window is closing, and the flagship model was never the real target. Here's the current state, verified against OpenAI's own documentation as of July 14, 2026.
OpenAI is retiring its self-serve fine-tuning platform on a fixed schedule. As of May 7, 2026, organizations with no prior fine-tuning history can no longer start a job. On July 2, 2026, organizations that haven't run inference on a fine-tuned model in the last 60 days lose the ability to create new jobs. On January 6, 2027, every remaining customer loses job-creation access, full stop. Inference on models you've already fine-tuned keeps working until the underlying base model is deprecated. Source: OpenAI's deprecations page.
Separately, if you have a fine-tuned gpt-3.5-turbo, gpt-4, or gpt-4.1-nano-2025-04-14 model in production, it's on the deprecation list and gets removed by October 23, 2026. More on migration below.
What "fine-tune GPT-4" actually means today
Flagship gpt-4 and gpt-4-0613 were never generally available for fine-tuning. OpenAI ran invite-only experimental access starting in 2023. gpt-4-0613 and its fine-tuned variant are now on the deprecation list, scheduled for removal by October 23, 2026, migrating to gpt-5.5.
The GPT-4-class models you can fine-tune today are the GPT-4.1 family: gpt-4.1-2025-04-14, gpt-4.1-mini-2025-04-14, and gpt-4.1-nano-2025-04-14. If you started this article expecting to fine-tune "GPT-4" itself, the honest answer is that you can't, and never really could. GPT-4.1 is the closest thing that exists.
The four fine-tuning methods and what each is for
OpenAI supports four fine-tuning methods, each scoped to a different set of models.
| Method | Eligible models | Use case |
|---|---|---|
| Supervised fine-tuning (SFT) | gpt-4.1-2025-04-14, gpt-4.1-mini-2025-04-14, gpt-4.1-nano-2025-04-14 | Teaching a consistent format, tone, or task behavior from labeled examples |
| Direct Preference Optimization (DPO) | Same three GPT-4.1 models | Steering output toward preferred responses using paired comparisons |
| Vision fine-tuning | gpt-4o-2024-08-06 only | Image classification or extraction tasks where the base model's visual judgment needs correction |
| Reinforcement fine-tuning (RFT) | o4-mini-2025-04-16 only | Reasoning tasks with a programmatic grader, where you can score correctness automatically |
Source: OpenAI's supervised fine-tuning guide, model optimization guide, and reinforcement fine-tuning guide.
Should you fine-tune at all?
Fine-tuning should follow prompting and retrieval. Those approaches solve the large majority of customization problems. Fine-tuning earns its cost after they hit a measurable ceiling. The operating sequence in Building Your AI Team starts on a hosted model, adds retrieval when the model needs missing context, and reaches for fine-tuning when a specific, persistent gap remains.
OpenAI's own platform decision reinforces this. Winding down self-serve fine-tuning while investing in longer context windows and cheaper inference is a bet that prompt-based approaches are now faster and cheaper for most teams than maintaining a custom model.
Fine-tuning still earns its place for narrow, high-volume tasks: matching a specific brand voice at scale, enforcing an exact output format, or teaching classification categories that don't map cleanly onto a prompt. If that's your case, and your organization still has access, here's how to do it.
Preparing your data
Training data is JSONL, one JSON object per line. Each line has a messages array, and each message is a dictionary with a role key and a content key. The role key accepts system, user, assistant, or tool as values.
Here's a minimal example, using a neutral support-ticket classification task:
{"messages": [{"role": "system", "content": "You classify support tickets into billing, technical, or account categories and respond with only the category name."}, {"role": "user", "content": "I was charged twice for my subscription this month."}, {"role": "assistant", "content": "billing"}]}
{"messages": [{"role": "system", "content": "You classify support tickets into billing, technical, or account categories and respond with only the category name."}, {"role": "user", "content": "The app crashes every time I try to export a report."}, {"role": "assistant", "content": "technical"}]}
{"messages": [{"role": "system", "content": "You classify support tickets into billing, technical, or account categories and respond with only the category name."}, {"role": "user", "content": "I can't remember which email I used to sign up."}, {"role": "assistant", "content": "account"}]}
The API requires a minimum of 10 examples. OpenAI recommends starting around 50 well-crafted examples, and quality improvements scale roughly with doubling the dataset size from there. The current per-example token limit is 65,536 tokens.
Before uploading, check your dataset for structural errors. This script mirrors the validation pattern in OpenAI's own fine-tuning cookbook:
import json
from collections import defaultdict
dataset = []
with open("training_data.jsonl") as f:
for line_number, line in enumerate(f, start=1):
if not line.strip():
continue
try:
dataset.append(json.loads(line))
except json.JSONDecodeError as error:
raise ValueError(f"Invalid JSON on line {line_number}") from error
format_errors = defaultdict(int)
for ex in dataset:
if not isinstance(ex, dict):
format_errors["data_type"] += 1
continue
messages = ex.get("messages", None)
if not messages:
format_errors["missing_messages_list"] += 1
continue
for message in messages:
if "role" not in message or "content" not in message:
format_errors["message_missing_key"] += 1
if any(k not in ("role", "content", "name") for k in message):
format_errors["message_unrecognized_key"] += 1
if message.get("role", None) not in ("system", "user", "assistant", "tool"):
format_errors["unrecognized_role"] += 1
content = message.get("content", None)
if not content or not isinstance(content, str):
format_errors["missing_content"] += 1
if not any(m.get("role") == "assistant" for m in messages):
format_errors["example_missing_assistant_message"] += 1
if format_errors:
print("Found errors:")
for k, v in format_errors.items():
print(f"{k}: {v}")
else:
print("No errors found")
The validated training_data.jsonl file is ready to upload.
A worked example: from dataset to job
Upload the training file:
curl https://api.openai.com/v1/files \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F purpose="fine-tune" \
-F file="@training_data.jsonl"
Create the fine-tuning job, using a real, currently supported GPT-4.1-family model ID:
curl https://api.openai.com/v1/fine_tuning/jobs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"training_file": "file-RCnFCYRhFDcq1aHxiYkBHw",
"model": "gpt-4.1-nano-2025-04-14"
}'
The equivalent in the current Python SDK (openai>=1.0):
from openai import OpenAI
client = OpenAI()
file = client.files.create(
file=open("training_data.jsonl", "rb"),
purpose="fine-tune",
)
job = client.fine_tuning.jobs.create(
training_file=file.id,
model="gpt-4.1-nano-2025-04-14",
method={"type": "supervised", "supervised": {"hyperparameters": {"n_epochs": 2}}},
)
print(job.id)
Source: OpenAI's supervised fine-tuning guide.
Evaluating the result
Once a job completes, client.fine_tuning.jobs.retrieve(job.id) returns a fine_tuned_model field with an ID like ft:gpt-4.1-nano-2025-04-14:your-org:custom-suffix:id. Test it the same way you'd test any model, against a fixed set of cases you understand well.
The evaluation loop we recommend, described in more depth in Building Your AI Team, starts with real traces, reads failures manually before automating anything, and grows a durable golden dataset over time. Deterministic checks handle format and required fields. Model-based judges, validated against human judgment first, handle subjective quality.
Note that OpenAI's hosted Evals dashboard is being wound down alongside the fine-tuning platform. It becomes read-only for existing users on October 31, 2026, and shuts down entirely on November 30, 2026, with OpenAI steering new users toward its Datasets product instead. The open-source openai/evals repository remains active and separate from the dashboard.
Using and deploying the fine-tuned model
Reference the fine-tuned model directly by its ft:-prefixed ID at inference time. There's no separate deployment step.
completion = client.chat.completions.create(
model="ft:gpt-4.1-nano-2025-04-14:your-org:custom-suffix:id",
messages=[{"role": "user", "content": "I was double charged this month."}],
)
print(completion.choices[0].message.content)
Inference on a fine-tuned model keeps working until OpenAI deprecates the underlying base model, independent of the job-creation cutoffs above.
What it costs
OpenAI publishes these standard fine-tuning rates. GPT-4.1-family prices are per 1M tokens. Reinforcement fine-tuning for o4-mini bills training by the hour.
| Model | Training | Input | Cached input | Output |
|---|---|---|---|---|
gpt-4.1-2025-04-14 | $25.00 | $3.00 | $0.75 | $12.00 |
gpt-4.1-mini-2025-04-14 | $5.00 | $0.80 | $0.20 | $3.20 |
gpt-4.1-nano-2025-04-14 | $1.50 | $0.20 | $0.05 | $0.80 |
o4-mini-2025-04-16 | $100.00 per hour | $4.00 | $1.00 | $16.00 |
OpenAI also publishes lower batch inference rates. Check the OpenAI pricing page before committing budget because these figures can change.
If your access is already gone or your model is being retired
If your organization has lost fine-tuning job access, use prompting, retrieval, or a provider or open-weight tuning path outside OpenAI's self-serve fine-tuning platform. Organizations that still have job-creation access can consider reinforcement fine-tuning on o4-mini when the task fits a programmatic grader.
If you have a fine-tuned model on the deprecation list, here's where it migrates:
| Deprecated model | Migrates to |
|---|---|
ft-gpt-3.5-turbo | gpt-5.4-mini |
ft-gpt-4 | gpt-5.5 |
ft-gpt-4.1-nano-2025-04-14 | gpt-5.4-nano |
All three are removed by October 23, 2026. Test the migration target against your existing evaluation set before that date. A newer base model doesn't guarantee your fine-tuned behavior carries over, and you may need to re-run the fine-tuning job against the new base model if the gap is large enough.
More articles
Continue exploring the Klu blog.
July 14, 2026
Fine-Tuning OpenAI Models: What Still Works
It's time to build
Collaborate with your team on reliable Generative AI features.
Want expert guidance? Book a 1:1 onboarding session from your dashboard.