Format JSONL et upload via Files API
Sur cette page
Cours révisé le 2 novembre 2026 d’après la documentation de xAI : la remise batch est de 20 % sur les modèles texte éligibles (et non 50 %), les exemples suivent l’API REST et le SDK officiel
xai-sdk(client.batch.*; l’API Batch de xAI n’a pas le format de celle d’OpenAI, le SDK OpenAI ne s’y applique pas), et l’API Responses passe avant Chat Completions, endpoint hérité chez xAI. Sources : Batch API · Référence REST · Tarifs batch.
L’approche en masse avec JSONL
Pour les volumes importants préparés d’avance, le fichier JSONL est la voie la plus simple. Vous écrivez toutes vos requêtes dans un fichier, vous le téléversez par la Files API, puis vous créez le batch à partir de ce fichier. Un seul fichier peut porter jusqu’à 50 000 requêtes.
Structure du fichier JSONL
Un fichier JSONL (JSON Lines) contient un objet JSON par ligne. Chaque ligne représente une requête complète, avec quatre champs :
{"custom_id": "req-1", "method": "POST", "url": "/v1/responses", "body": {"model": "grok-4.3", "input": [{"role": "user", "content": "Résume ce texte en une phrase : ..."}]}}
{"custom_id": "req-2", "method": "POST", "url": "/v1/responses", "body": {"model": "grok-4.3", "input": [{"role": "user", "content": "Classe ce ticket : bug, fonctionnalité ou question. ..."}]}}
{"custom_id": "req-3", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "grok-4.3", "messages": [{"role": "user", "content": "Traduis en anglais : ..."}]}}
custom_id: identifiant unique dans le fichier ; il devient lebatch_request_iddes résultatsmethod: toujours"POST"url: l’endpoint visé, parmi les sept acceptésbody: le corps de la requête, conforme à la référence de l’API pour cet endpoint
Chaque ligne doit être un JSON valide et autonome : pas de virgule de séparation, pas de crochet englobant. Les endpoints peuvent se mêler dans un même fichier ; chaque requête est routée indépendamment.
Générer un fichier JSONL en Python
import json
documents = [
{"id": "doc-001", "text": "Premier article à résumer..."},
{"id": "doc-002", "text": "Deuxième article à résumer..."},
# ... jusqu'à 50 000 documents par fichier
]
with open("batch_requests.jsonl", "w", encoding="utf-8") as f:
for doc in documents:
ligne = {
"custom_id": doc["id"],
"method": "POST",
"url": "/v1/responses",
"body": {
"model": "grok-4.3",
"input": [
{"role": "system", "content": "Résume en trois phrases."},
{"role": "user", "content": doc["text"]},
],
},
}
f.write(json.dumps(ligne, ensure_ascii=False) + "\n")
Upload via la Files API
Le fichier se téléverse par l’endpoint /v1/files, sans autre paramètre :
curl -X POST https://api.x.ai/v1/files \
-H "Authorization: Bearer $XAI_API_KEY" \
-F file="@batch_requests.jsonl"
Avec le SDK Python :
from xai_sdk import Client
client = Client()
with open("batch_requests.jsonl", "rb") as f:
fichier = client.files.upload(f, filename="batch_requests.jsonl")
print(f"Fichier téléversé : {fichier.id}")
Créer le batch à partir du fichier
Le fichier ne s’ajoute pas à un batch existant : c’est à la création que vous le désignez, par input_file_id :
curl -X POST https://api.x.ai/v1/batches \
-H "Authorization: Bearer $XAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "resumes_articles", "input_file_id": "file-abc123"}'
batch = client.batch.create(batch_name="resumes_articles", input_file_id=fichier.id)
print(f"Batch créé : {batch.batch_id}")
Le fichier est ensuite lu en arrière-plan. Si une seule ligne est invalide, le batch est annulé avec un message d’erreur. Le suivi et la récupération des résultats se font ensuite exactement comme pour un batch alimenté par l’API.
Limites du format JSONL
- 50 000 requêtes au maximum par fichier
- 200 MB au maximum par fichier
- un
custom_idunique par ligne - un batch créé depuis un fichier est scellé : aucun ajout ultérieur par l’API n’est possible
Si votre lot dépasse ces limites, découpez-le en plusieurs fichiers et créez un batch par fichier. Le nombre de batchs par équipe n’est pas plafonné.
Générer un JSONL en JavaScript
import fs from "fs";
const documents = [
{ id: "doc-001", text: "Premier article..." },
{ id: "doc-002", text: "Deuxième article..." },
];
const lines = documents.map(doc => JSON.stringify({
custom_id: doc.id,
method: "POST",
url: "/v1/responses",
body: {
model: "grok-4.3",
input: [
{ role: "user", content: `Résume : ${doc.text}` }
]
}
}));
fs.writeFileSync("batch_requests.jsonl", lines.join("\n") + "\n");
Validation et débogage
Une ligne invalide fait annuler tout le batch : validez le fichier avant de le téléverser. Un script simple couvre l’essentiel :
import json
import os
ENDPOINTS = {
"/v1/responses", "/v1/chat/completions",
"/v1/images/generations", "/v1/images/edits",
"/v1/videos/generations", "/v1/videos", "/v1/videos/edits", "/v1/videos/extensions",
}
def valider_jsonl(chemin: str) -> list[str]:
erreurs, ids = [], set()
if os.path.getsize(chemin) > 200 * 1024 * 1024:
erreurs.append("fichier de plus de 200 MB")
with open(chemin, encoding="utf-8") as f:
for i, ligne in enumerate(f, 1):
try:
obj = json.loads(ligne)
except json.JSONDecodeError as e:
erreurs.append(f"ligne {i} : JSON invalide ({e})")
continue
cid = obj.get("custom_id")
if not cid or cid in ids:
erreurs.append(f"ligne {i} : custom_id absent ou en double")
ids.add(cid)
if obj.get("method") != "POST" or obj.get("url") not in ENDPOINTS:
erreurs.append(f"ligne {i} : method ou url invalide")
if not isinstance(obj.get("body"), dict):
erreurs.append(f"ligne {i} : body absent")
if len(ids) > 50_000:
erreurs.append("plus de 50 000 requêtes")
return erreurs
print(valider_jsonl("batch_requests.jsonl") or "Fichier valide")
Points clés à retenir
- Le format JSONL contient un objet JSON par ligne :
custom_id,method,url,body - Le fichier se téléverse par
/v1/files, puis se désigne à la création du batch (input_file_id) - Maximum 50 000 requêtes et 200 MB par fichier ; au-delà, un batch par fichier
- Un batch créé depuis un fichier est scellé, et une ligne invalide l’annule entièrement
- Validez le fichier avant l’upload