gpu.aiDocs
UPDATED 2026.08.06READ 7 MINEDIT ON GITHUB →
CH·07API

Fine-tuning.

Train a LoRA or QLoRA adapter on a curated base model through an OpenAI-compatible API. You upload a dataset and submit a job; GPU.ai provisions the training GPU, runs it, and tears the GPU down when it finishes. You pay per GPU-hour for the time the run takes.

§ 07.1How it works

  1. Upload a JSONL chat dataset to POST /v1/files with purpose=fine-tune. The server validates it and returns a file id.
  2. Create a job with POST /v1/fine_tuning/jobs, referencing that training_file and choosing a method (lora or qlora).
  3. Track it with GET /v1/fine_tuning/jobs/{id} and the events endpoint for milestone log lines.
  4. Download the adapter once the job is succeeded: the job's result_files are short-lived signed URLs for adapter_model.safetensors and adapter_config.json.

Everything lives under https://api.gpu.ai/v1 with Authorization: Bearer gpuai_live_.... The write calls (upload, create, cancel) need serverless:write; the reads (list, get, events) accept either serverless:read or serverless:write. A full_access key covers everything.

§ 07.2Base models

Fine-tuning runs against an operator-curated allowlist of tunable base models. Pass the id as the model field on a create request; anything outside the list is rejected with a 422.

Base model idModel
qwen2.5-7b-instructQwen 2.5 7B Instruct (Apache-2.0)

The list grows over time. Want a base we don't carry yet? Tell us at support@gpu.ai.

§ 07.3Datasets

Datasets are JSONL, one JSON object per line, in the OpenAI chat format:

train.jsonl
{"messages":[{"role":"system","content":"You are a terse assistant."},{"role":"user","content":"2+2?"},{"role":"assistant","content":"4"}]}
{"messages":[{"role":"user","content":"Capital of France?"},{"role":"assistant","content":"Paris"}]}

Every line must be valid JSON with a messages array; roles are system, user, and assistant; content is a string; and each example needs at least one assistant message. You need at least 10 examples, and the dataset caps at 100 MiB. A malformed dataset is rejected at upload with an error naming the offending line, so you find out before a GPU is ever provisioned.

§ 07.4Endpoints

POST/v1/filesAUTH
POST/v1/fine_tuning/jobsAUTH
GET/v1/fine_tuning/jobsAUTH
GET/v1/fine_tuning/jobs/{id}AUTH
GET/v1/fine_tuning/jobs/{id}/eventsAUTH
POST/v1/fine_tuning/jobs/{id}/cancelAUTH

The job list is cursor-paginated with ?after= and ?limit=, following the same conventions as the rest of the API — see conventions.

§ 07.5A full run

The API mirrors OpenAI's files and fine_tuning.jobs, so the official SDKs work unchanged. Two native extensions carry what OpenAI has no field for: method and gpuai.

# Upload the dataset and start a QLoRA job in one step
gpu fine-tune create \
  --model qwen2.5-7b-instruct \
  --dataset ./train.jsonl \
  --method qlora

# Track it
gpu fine-tune get 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34
gpu fine-tune events 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34

# Download the adapter once it succeeds
gpu fine-tune download 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34 --out ./adapters/3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34

# Stop early if you need to
gpu fine-tune cancel 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34

§ 07.6Job lifecycle

StatusMeaning
queuedAccepted; waiting for a training GPU.
runningTraining in progress.
succeededFinished. result_files holds the downloadable adapter.
failedTraining failed; the job's error object says why.
cancelledCancelled by you. The GPU is torn down and billing stops.

created_at and finished_at are unix seconds. result_files is empty until the job succeeds, then holds freshly-signed download URLs regenerated on every read — they are short-lived and never persisted, so fetch the job again rather than caching a URL. fine_tuned_model is null: this release delivers the adapter as downloadable files rather than a hosted endpoint.

§ 07.7Billing & the budget cap

Fine-tuning bills per GPU-hour for the duration of the training run, not per token. A job that fails or is cancelled bills only for the GPU-hours it actually consumed.

§ 07.8Errors

Errors use the OpenAI envelope, so SDK error handling works as-is:

ERROR ENVELOPE
{ "error": { "message": "model is not tunable", "type": "invalid_request_error", "code": "unprocessable_entity" } }

The two you are most likely to hit, both raised before any GPU is provisioned: a base model outside the allowlist returns 422 unprocessable_entity on create, and a malformed dataset returns 400 invalid_dataset on upload, naming the offending line. Note that a create call does not reserve the run's full cost up front, so keep enough balance to cover the GPU-hours you expect the job to take.