Skip to content
Guide

Migrate from OpenAI

Gridline's Files, fine-tuning and Batch APIs follow OpenAI's. Point the OpenAI SDK at Gridline's base URL with a Gridline token, change the model names, and check the differences below.

Overview

OpenAI's API stops accepting new fine-tuning jobs on 6 January 2027, and models fine-tuned from gpt-5 or o3 shut down with those models on 11 December 2026 (OpenAI's deprecations page).

  1. Set the SDK's base URL to Gridline's and its API key to a Gridline token.
  2. Upload the same files: chat-format training files with purpose=fine-tune, batch input with purpose=batch.
  3. Name a Gridline base model where you named a GPT model. A job that succeeds names its model ft:<base>:<org>:<job>; use that in batches.
  4. Check the fields and features Gridline does not offer: each is refused by name, never silently ignored.

Point your client at Gridline

Python, with the official openai package:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.gridlinegpu.com/openai/v1",
    api_key=os.environ["GRIDLINE_TOKEN"],
)

Node, with the official openai package (JavaScript; in TypeScript, Gridline's extra fields below need a cast):

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.gridlinegpu.com/openai/v1",
  apiKey: process.env.GRIDLINE_TOKEN,
});

Both SDKs also read the base URL and key from the environment, so code that builds a client with no arguments needs no change:

export OPENAI_BASE_URL="https://api.gridlinegpu.com/openai/v1"
export OPENAI_API_KEY="$GRIDLINE_TOKEN"

Keep /openai/v1 in the base URL. The OpenAI-compatible routes are all under it, so a request without it, such as https://api.gridlinegpu.com/v1/models, answers 404. The models list is https://api.gridlinegpu.com/openai/v1/models.

Your token

Sign up, verify your email and add credits by card at Get started. Then create a token with the Gridline CLI. gridline setup --apply asks you to approve it in your browser, then saves a private token, valid for 30 days:

python -m pip install 'gridlinegpu[source-contract]>=0.3.2,<0.4'
gridline setup --apply
export GRIDLINE_TOKEN="$(cat ~/.config/gridline/python-token)"

Send it as the bearer token, where you sent your OpenAI key. OpenAI's organization and project settings have no Gridline equivalent; leave them unset.

Check before you upload

The Gridline CLI checks your OpenAI training and validation files and your OpenAI job offline. It lists every line Gridline's upload would refuse, each job field Gridline would refuse or treat differently, and prints the equivalent Gridline call. Nothing is sent. --job takes the job's create body, or the job object OpenAI returns for client.fine_tuning.jobs.retrieve, saved as JSON:

gridline migrate openai train.jsonl \
  --validation-file valid.jsonl \
  --job openai-job.json \
  --model qwen2.5-7b-instruct

It exits 0 when Gridline would accept the files and the job, and 1 otherwise. The API's answer at upload and at create decides.

Files

client.files.create works unchanged. The purposes:

purposeFor
fine-tuneTraining and validation files: one chat conversation per line, a messages list, as OpenAI's chat fine-tuning format.
batchBatch input: one request per line, with custom_id, method, url and body.
batch_outputWritten by Gridline for a batch's output and error files. You download them; you never upload with it.

A fine-tuning job's training and validation files are each at most 64 MiB. A batch input file is at most 200 MB and 50,000 requests.

Model names

Gridline fine-tunes and serves open models. Name one of these where you named a GPT model:

ModelWeightsFine-tuningBatch endpoint
qwen2.5-0.5b-instructQwen/Qwen2.5-0.5B-Instruct (Apache-2.0)Yes/v1/chat/completions
qwen2.5-1.5b-instructQwen/Qwen2.5-1.5B-Instruct (Apache-2.0)Yes/v1/chat/completions
qwen2.5-7b-instructQwen/Qwen2.5-7B-Instruct (Apache-2.0)Yes/v1/chat/completions
mistral-7b-instruct-v0.3mistralai/Mistral-7B-Instruct-v0.3 (Apache-2.0)Yes/v1/chat/completions
qwen3-8bQwen/Qwen3-8B (Apache-2.0)Yes/v1/chat/completions
llama-3.1-8b-instructRedHatAI/Llama-3.1-8B-Instruct (Llama 3.1 Community License)Yes/v1/chat/completions
gpt-oss-20bopenai/gpt-oss-20b (Apache-2.0)Yes (adapter download only)/v1/chat/completions
qwen3.5-9bQwen/Qwen3.5-9B (Apache-2.0)Yes (adapter download only)/v1/chat/completions
qwen3.8-27bQwen/Qwen3.8-27B-FP8 (Apache-2.0)No/v1/chat/completions
nemotron-3.5-lightning-30b-a3bnvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-NVFP4 (OpenMDW-1.1)No/v1/chat/completions
gemma4-26b-a4b-itgoogle/gemma-4-26B-A4B-it (Apache-2.0)No/v1/chat/completions
qwen3-embedding-0.6bQwen/Qwen3-Embedding-0.6B (Apache-2.0)No/v1/embeddings
bge-m3BAAI/bge-m3 (MIT)No/v1/embeddings

A fine-tuned model is named ft:<base>:<org>:<job-suffix>, where <job-suffix> is the first 12 hex characters of the job id after ftjob-:

ft:qwen2.5-7b-instruct:o3f9a1c2d4e5:7b2c9e41a0d3

Your suffix is not part of the name. A fine-tuned model is a chat model: name it in /v1/chat/completions batch lines. GET /models lists the catalog and your ft: models; DELETE /models/{model} deletes one.

Fine-tuning jobs

client.fine_tuning.jobs.create takes OpenAI's fields. Gridline trains a LoRA or QLoRA adapter on the base model:

FieldOn GridlineNotes
modelDifferentA Gridline base model id from the table above.
training_fileAs OpenAIA file id uploaded with purpose=fine-tune.
validation_fileAs OpenAIOptional. Without one, the job holds out 5% of the training rows to measure eval loss.
hyperparametersAs OpenAIThe table below. Send it at the top level or under method.supervised.hyperparameters, once.
methodDifferent{"type": "supervised"} or {"type": "dpo"} (preference rows); reinforcement is refused. Gridline adds the adapter: method.supervised.adapter (method.dpo.adapter), or method as the string "lora" or "qlora".
suffixIgnoredAccepted (at most 64 characters) and kept on the job, but not part of the model name: the job records a suffix_ignored event.
seedAs OpenAIAn integer from 0 to 2,147,483,647. Default 0.
metadataAs OpenAIAt most 16 string pairs.
integrationsDifferentAccepted only as null or []: Gridline offers no Weights & Biases or other integration.

Any other field is refused with Unsupported parameter and its name. The hyperparameters, OpenAI's and Gridline's:

HyperparameterFromRangeDefault
n_epochsOpenAI1 to 101 (also "auto")
learning_rate_multiplierOpenAI0.01 to 10 Times the base learning rate, 2e-4.1 (also "auto")
batch_sizeOpenAI1 to 648 (also "auto")
learning_rateGridline1e-6 to 0.01 Instead of the multiplier, never with it.2e-4
lora_rankGridline1 to 64 Up to 64 with method lora or qlora (adapters up to 383 MiB; on a 7B or 8B base, QLoRA runs on a 48 GB GPU at the LoRA rate at any rank); with DPO, at most the base's max rank, 24 to 64, so its adapter fits 128 MiB.16
lora_alphaGridline1 to 25632
max_seq_lengthGridline128 to 8,192 Up to 8,192 for supervised tuning on Qwen2.5-7B, Qwen3-8B and Llama 3.1 8B. Above 2,048, use rank 16 or lower. DPO and other bases have a 2,048-token limit.2,048

Gridline's hyperparameters go in the same dictionary; the SDKs pass them through. Python:

training = client.files.create(file=open("train.jsonl", "rb"), purpose="fine-tune")
job = client.fine_tuning.jobs.create(
    model="qwen2.5-7b-instruct",
    training_file=training.id,
    method={
        "type": "supervised",
        "supervised": {"adapter": "qlora", "hyperparameters": {"n_epochs": 2, "lora_rank": 32}},
    },
)
print(job.gridline["quote"])  # creating the job started it; the quote shows what it can cost

Node:

import fs from "node:fs";

const training = await client.files.create({
  file: fs.createReadStream("train.jsonl"),
  purpose: "fine-tune",
});
const job = await client.fineTuning.jobs.create({
  model: "qwen2.5-7b-instruct",
  training_file: training.id,
  hyperparameters: { n_epochs: 2, lora_rank: 32 },
});
  • Every job object carries Gridline's fields under gridline: the quote when it is created (an estimate, never a price) and, once it succeeds, the adapter and the eval loss before and after training.
  • A job must finish within 2 hours; create refuses one estimated longer.One job trains at a time per account. New accounts can have 1 open at once, other accounts 3, unless Gridline has set a different limit; a refused create gives yours in error.limit.
  • Download the adapter of a model that succeeded, two files: GET /gridline/adapters/{model}/files/adapter_model.safetensors and .../adapter_config.json, under the base URL (https://api.gridlinegpu.com/openai/v1/gridline/adapters/...).
  • A QLoRA adapter is trained against a 4-bit quantized base and served on the bf16 base, so its outputs can differ slightly from what training measured.

The Fine-tuning API page has the training format, the quote and the errors.

Batches

client.batches.create works unchanged. completion_window is 24h or 72h only, and endpoint is one of /v1/chat/completions or /v1/embeddings. Input lines keep OpenAI's shape: custom_id, method, url and body.

batch_input = client.files.create(file=open("requests.jsonl", "rb"), purpose="batch")
batch = client.batches.create(
    input_file_id=batch_input.id,
    endpoint="/v1/chat/completions",
    completion_window="24h",
)
const input = await client.files.create({
  file: fs.createReadStream("requests.jsonl"),
  purpose: "batch",
});
const batch = await client.batches.create({
  input_file_id: input.id,
  endpoint: "/v1/chat/completions",
  completion_window: "24h",
});
  • One base model per batch. A chat batch may also name up to 8 of your ft: models of that base (fewer on some bases: Models), line by line in body.model.
  • These request fields are refused at validation, never ignored: tools, tool_choice, parallel_tool_calls, functions, function_call, stream, stream_options, logit_bias, service_tier, store, prediction, audio, modalities, metadata, reasoning_effort, web_search_options, suffix, best_of, dimensions. On chat lines response_format is accepted as text, json_object or json_schema (see Structured outputs); user is accepted and ignored.
  • Output and error files are written as with OpenAI; download them with client.files.content.
  • 72h is Gridline's flex window, at the same price as 24h. The OpenAI Python SDK's type admits only "24h", so pass completion_window="24h", extra_body={"completion_window": "72h"} (see Completion window).

The Batch API page has the line format, statuses, limits and retention.

Usage and limits by API key

Each batch and fine-tuning job records the API key that created it. GET /gridline/usage/keys lists your keys with their batches, fine-tuning jobs, tokens (input, output, trained), the amounts held and charged, and their limits. from and to are UTC days (YYYY-MM-DD), at most 31 days in all; without them, this month so far. Batches and jobs made before per-key tracking are one line with api_key_id null.

GET /gridline/usage/keys/{key} pages one key's batches and jobs, newest first (limit 1 to 100, default 20; after a batch or job id), with the key's spend today and in total. {key} is a key id from the list, or current for the key you call with.

The account owner can give each key a daily and a total spend limit when signed in to Gridline, under Usage by API key. A create that would pass one is refused with HTTP 429, error.code limit_exceeded and error.limit naming the limit, the key, its spend and the maximum; nothing is created. A daily refusal carries Retry-After until 00:00 UTC.

LimitCountsResetsAt most
api_key_daily_spend_usdThe key's spend in the current UTC day.At 00:00 UTC.1,000 USD
api_key_total_spend_usdThe key's spend over its life.Never.100,000 USD

How the limits behave today:

  • Limits are checked when a batch or fine-tuning job is created, not while it runs: a batch already running can finish above its key's limit.
  • A batch or job counts toward its key's spend once it starts and reserves credit. Batches and jobs created but not yet started do not count yet, so several created close together, or at the same time, can each pass the same limit.
  • A batch's cost is not known when it is created, so one batch can pass a limit by up to its own cost. A fine-tuning job is checked with its quoted maximum.
  • Your account's credit and the account's own limits still bound what every key can spend.

Routes

Every route under https://api.gridlinegpu.com/openai/v1:

RouteShapeUse
POST /filesOpenAI'sUpload a file with purpose=fine-tune or purpose=batch.
POST /files/distillGridline'sTurn a batch's requests and results into fine-tune training and validation files.
GET /filesOpenAI'sList your files.
GET /files/{file_id}OpenAI'sRetrieve a file object.
GET /files/{file_id}/contentOpenAI'sDownload a file, such as a batch's output.
DELETE /files/{file_id}OpenAI'sDelete a file.
POST /fine_tuning/jobsOpenAI'sCreate a fine-tuning job (fields below).
GET /fine_tuning/jobsOpenAI'sList your jobs.
GET /fine_tuning/jobs/{job_id}OpenAI'sRetrieve a job.
GET /fine_tuning/jobs/{job_id}/eventsOpenAI'sProgress, warnings and eval metrics.
GET /fine_tuning/jobs/{job_id}/checkpointsOpenAI'sA succeeded job's one checkpoint: its ft: model at the last step, with its metrics.
POST /fine_tuning/jobs/{job_id}/cancelOpenAI'sCancel a job.
POST /batchesOpenAI'sCreate a batch.
GET /batchesOpenAI'sList your batches.
GET /batches/{batch_id}OpenAI'sRetrieve a batch.
POST /batches/{batch_id}/cancelOpenAI'sCancel a batch.
GET /modelsOpenAI'sThe catalog models and your ft: models.
GET /models/{model}OpenAI'sOne model.
DELETE /models/{model}OpenAI'sDelete one of your ft: models.
GET /gridline/adapters/{model}/files/{name}Gridline'sDownload your ft: model's adapter: adapter_model.safetensors and adapter_config.json.
POST /gridline/adapters/{model}/mergedGridline'sStart a merge of your ft: model's adapter into its base, to download as full safetensors at no charge.
GET /gridline/adapters/{model}/mergedGridline'sThe merge's status and, once it has succeeded, the export's file list.
DELETE /gridline/adapters/{model}/mergedGridline'sDelete the merged export, or cancel a pending merge.
GET /gridline/adapters/{model}/merged/files/{path}Gridline'sDownload one file of the merged export: a redirect that carries the file's SHA-256 in x-gridline-sha256.
GET /gridline/usage/keysGridline'sUsage per API key over a window of UTC days: batches, fine-tuning jobs, tokens, held and charged amounts, and each key's spend limits.
GET /gridline/usage/keys/{key}Gridline'sOne API key's batches and jobs, and its spend today and in total against its limits. {key} is a key id, or current for the key you call with.

What is different

Settings both take, with a different default or effect:

SettingOn OpenAIOn Gridline
n_epochs "auto" or absentChosen from the dataset size.1 epoch. Set n_epochs to the number your OpenAI job ran.
seed absentOpenAI generates one.0.
Example lengthUp to 65,536 tokens an example.Rows longer than max_seq_length (default 2,048) are cut at max_seq_length tokens; a row left with no trained token is dropped.

What OpenAI offers that Gridline does not, and what to do instead:

On OpenAIOn Gridline
Real-time requests (POST /chat/completions, /embeddings, /responses)Not offered: Gridline runs offline batches. Send the same request bodies as batch lines, or download your adapter and serve it yourself.
GPT base modelsFine-tune and batch the open models in the table above.
Reinforcement fine-tuningRefused at create: supervised fine-tuning and DPO only (method.type "supervised" or "dpo").
Full fine-tunesGridline trains a LoRA or QLoRA adapter on the base; full fine-tunes are not offered. To take the model with you, download the adapter merged into its base (POST /gridline/adapters/{model}/merged).
Tools, function calling and images in training filesRefused at upload, by name: tools, functions, name, tool_calls, content parts and the tool and function roles.
Intermediate fine-tuning checkpointsNot offered: the checkpoints route lists only the final step's adapter, registered as one ft: model.
The Uploads API (POST /uploads)Not offered: upload each file in one request.
Other batch endpoints (/v1/responses, /v1/completions, moderations, images, audio)Refused at validation: batches target chat completions and embeddings.
Streaming, tools and the other refused fields in batch linesRefused at validation, never ignored: the list above. On chat lines response_format is accepted as text, json_object or json_schema.

Costs

Per 1M tokensOpenAI, fine-tuned gpt-4o-mini-2024-07-18Gridline, Llama 3.1 8B
Training$3.00$0.29
Batch input$0.15$0.049
Batch output$0.60$0.115

Same price for Qwen 2.5 7B and Mistral 7B.

For example, train a 250,000-token file for 3 epochs (750,000 training tokens), then run a batch on the fine-tuned model with 10,000,000 input and 1,000,000 output tokens. On OpenAI that is $2.25 for training and $2.10 for the batch, $4.35 in all. On Gridline it is $0.50 for training (the minimum per job) and $0.605 for the batch, $1.105 in all.

Different models; quality not compared. OpenAI prices as of 13 November 2026.

Fine-tuning and batch jobs are paid for from your prepaid credits.

What a credit is worth, the credit packs and each model's rates are on the pricing page.

Contact us

Whether you have a question about fine-tuning or batch, need help with a job, or want to share feedback, send us a note. We’ll make sure it reaches the right person.

New messageGridline / contact
0/5,000