@apicity/xai 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,862 @@
1
+ # @apicity/xai
2
+
3
+ [![npm](https://img.shields.io/npm/v/@apicity/xai?color=cb0000)](https://www.npmjs.com/package/@apicity/xai)
4
+ [![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript&logoColor=white)](tsconfig.json)
6
+
7
+ X.AI / Grok provider for chat and search.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @apicity/xai
13
+ # or
14
+ pnpm add @apicity/xai
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { xai as createXai } from "@apicity/xai";
21
+
22
+ const xai = createXai({ apiKey: process.env.XAI_API_KEY! });
23
+ ```
24
+
25
+ ## Real-world example: structured vision analysis with Grok-4
26
+
27
+ Hand Grok-4 a portrait, a system prompt that nails down the output schema,
28
+ and `text.format.type: "json_object"` — get back a reproduction-ready
29
+ JSON description with deterministic shot/pose vocabulary. The flow below
30
+ is taken verbatim from
31
+ [`tests/integration/xai-vision-json.test.ts`](../../../tests/integration/xai-vision-json.test.ts)
32
+ and replays against
33
+ [`tests/recordings/xai_3613880225/vision-analysis-json_243984103/recording.har`](../../../tests/recordings/xai_3613880225/vision-analysis-json_243984103/recording.har),
34
+ so the response shapes match what xAI actually returns.
35
+
36
+ ```typescript
37
+ import { readFile } from "node:fs/promises";
38
+ import { xai as createXai } from "@apicity/xai";
39
+
40
+ const xai = createXai({ apiKey: process.env.XAI_API_KEY! });
41
+
42
+ // 1. Load the image and inline it as a data URL. xAI also accepts
43
+ // https:// URLs, but inlining keeps the call self-contained and
44
+ // works against private hosts.
45
+ const image = await readFile("./portrait.jpg");
46
+ const base64 = image.toString("base64");
47
+
48
+ // 2. The system prompt enumerates the legal vocabulary for `shot` and
49
+ // constrains `pose` to body geometry only. Combined with
50
+ // `text.format.type: "json_object"` this gives Grok no room to drift
51
+ // off-schema — temperature 0 keeps the result reproducible.
52
+ const SYSTEM_PROMPT = [
53
+ "You are an expert image-to-prompt analyst.",
54
+ "Return only a JSON object with keys prompt, shot, and pose.",
55
+ "prompt: a single-paragraph reproduction-ready image prompt, 1900 characters or fewer, with no line breaks.",
56
+ 'shot: exactly "<size>, <angle>" where size is one of extreme close-up, close-up, medium close-up, medium shot, medium long shot, long shot, or extreme long shot, and angle is one of eye-level, low-angle, high-angle, overhead, or dutch.',
57
+ "pose: only body geometry for human figures, with no clothing, hair, background, or lighting details.",
58
+ ].join(" ");
59
+
60
+ // 3. Multimodal Responses request: system turn + a user turn whose
61
+ // content is an array of `input_image` + `input_text` parts.
62
+ const result = await xai.post.v1.responses({
63
+ model: "grok-4",
64
+ input: [
65
+ { role: "system", content: SYSTEM_PROMPT },
66
+ {
67
+ role: "user",
68
+ content: [
69
+ {
70
+ type: "input_image",
71
+ image_url: `data:image/jpeg;base64,${base64}`,
72
+ detail: "high",
73
+ },
74
+ {
75
+ type: "input_text",
76
+ text: 'Analyze this image and produce a reproduction-ready JSON description with keys "prompt", "shot", and "pose".',
77
+ },
78
+ ],
79
+ },
80
+ ],
81
+ text: { format: { type: "json_object" } },
82
+ store: false,
83
+ temperature: 0,
84
+ max_output_tokens: 300,
85
+ });
86
+
87
+ // 4. The Responses API wraps output in a typed item array. Find the
88
+ // assistant message, then the first `output_text` part inside it.
89
+ // Discriminated unions narrow `item.type === "message"` so
90
+ // `item.content` is statically typed.
91
+ const message = result.output.find((item) => item.type === "message");
92
+ const outputText =
93
+ message?.type === "message"
94
+ ? message.content.find((part) => part.type === "output_text")?.text
95
+ : undefined;
96
+
97
+ if (!outputText) throw new Error("Grok did not return output_text");
98
+
99
+ const analysis = JSON.parse(outputText) as {
100
+ prompt: string;
101
+ shot: string;
102
+ pose: string;
103
+ };
104
+
105
+ console.log(analysis.shot);
106
+ // → "medium close-up, eye-level"
107
+
108
+ console.log(analysis.pose);
109
+ // → "upright torso facing forward, head straight and centered, shoulders squared, arms relaxed downward (implied)"
110
+
111
+ // 5. Reasoning-token accounting. Grok-4 spent 623 of its 728 output
112
+ // tokens reasoning before emitting the 105-token JSON answer —
113
+ // surfaced in `usage.output_tokens_details.reasoning_tokens`.
114
+ console.log(result.usage);
115
+ // → {
116
+ // input_tokens: 2684,
117
+ // input_tokens_details: { cached_tokens: 679 },
118
+ // output_tokens: 728,
119
+ // output_tokens_details: { reasoning_tokens: 623 },
120
+ // total_tokens: 3412,
121
+ // }
122
+ ```
123
+
124
+ **Notes**
125
+
126
+ - `store: false` keeps the response off xAI's history surface. Flip to
127
+ `true` to chain follow-ups via `previous_response_id` — useful for
128
+ multi-turn refinement ("now describe the wardrobe") without re-uploading
129
+ the image each time.
130
+ - The Responses output array also carries reasoning items and tool calls
131
+ when present. Always discriminate on `item.type` before reading content;
132
+ TypeScript's narrowing keeps you honest.
133
+ - For raw chat-style usage without the Responses wrapping, use
134
+ `xai.post.v1.chat.completions` instead — same auth, same model catalog,
135
+ just OpenAI-compatible request/response shapes.
136
+ - Errors surface as `XaiError` with `status` and the parsed body attached,
137
+ so `try { ... } catch (e) { if (e instanceof XaiError) ... }` gives you
138
+ the upstream error directly.
139
+
140
+ ## API Reference
141
+
142
+ 39 endpoints across 17 groups. Each method mirrors an upstream URL path.
143
+
144
+ ### batches
145
+
146
+ <details>
147
+ <summary><code>GET</code> <b><code>xai.v1.batches</code></b></summary>
148
+
149
+ <code>GET https://api.x.ai/v1/batches/{paramsOrIdOrSignal}</code>
150
+
151
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
152
+
153
+ ```typescript
154
+ const res = await xai.v1.batches({ /* ... */ });
155
+ ```
156
+
157
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
158
+
159
+ </details>
160
+
161
+ <details>
162
+ <summary><code>GET</code> <b><code>xai.v1.batches.requests</code></b></summary>
163
+
164
+ <code>GET https://api.x.ai/v1/batches/{batchId}/requests{query}</code>
165
+
166
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
167
+
168
+ ```typescript
169
+ const res = await xai.v1.batches.requests({ /* ... */ });
170
+ ```
171
+
172
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
173
+
174
+ </details>
175
+
176
+ <details>
177
+ <summary><code>GET</code> <b><code>xai.v1.batches.results</code></b></summary>
178
+
179
+ <code>GET https://api.x.ai/v1/batches/{batchId}/results{query}</code>
180
+
181
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
182
+
183
+ ```typescript
184
+ const res = await xai.v1.batches.results({ /* ... */ });
185
+ ```
186
+
187
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
188
+
189
+ </details>
190
+
191
+ <details>
192
+ <summary><code>POST</code> <b><code>xai.v1.batches</code></b></summary>
193
+
194
+ <code>POST https://api.x.ai/v1/batches</code>
195
+
196
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
197
+
198
+ ```typescript
199
+ const res = await xai.v1.batches({ /* ... */ });
200
+ ```
201
+
202
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
203
+
204
+ </details>
205
+
206
+ <details>
207
+ <summary><code>POST</code> <b><code>xai.v1.batches.cancel</code></b></summary>
208
+
209
+ <code>POST https://api.x.ai/v1/batches/{batchId}:cancel</code>
210
+
211
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
212
+
213
+ ```typescript
214
+ const res = await xai.v1.batches.cancel({ /* ... */ });
215
+ ```
216
+
217
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
218
+
219
+ </details>
220
+
221
+ <details>
222
+ <summary><code>POST</code> <b><code>xai.v1.batches.requests</code></b></summary>
223
+
224
+ <code>POST https://api.x.ai/v1/batches/{batchId}/requests</code>
225
+
226
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
227
+
228
+ ```typescript
229
+ const res = await xai.v1.batches.requests({ /* ... */ });
230
+ ```
231
+
232
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
233
+
234
+ </details>
235
+
236
+ ### chat
237
+
238
+ <details>
239
+ <summary><code>GET</code> <b><code>xai.v1.chat.deferredCompletion</code></b></summary>
240
+
241
+ <code>GET https://api.x.ai/v1/chat/deferred-completion/{requestId}</code>
242
+
243
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
244
+
245
+ ```typescript
246
+ const res = await xai.v1.chat.deferredCompletion({ /* ... */ });
247
+ ```
248
+
249
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
250
+
251
+ </details>
252
+
253
+ <details>
254
+ <summary><code>POST</code> <b><code>xai.v1.chat.completions</code></b></summary>
255
+
256
+ <code>POST https://api.x.ai/v1/chat/completions</code>
257
+
258
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
259
+
260
+ ```typescript
261
+ const res = await xai.v1.chat.completions({ /* ... */ });
262
+ ```
263
+
264
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
265
+
266
+ </details>
267
+
268
+ ### collections
269
+
270
+ <details>
271
+ <summary><code>DELETE</code> <b><code>xai.v1.collections</code></b></summary>
272
+
273
+ <code>DELETE https://api.x.ai/v1/collections/{collectionId}</code>
274
+
275
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
276
+
277
+ ```typescript
278
+ const res = await xai.v1.collections({ /* ... */ });
279
+ ```
280
+
281
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
282
+
283
+ </details>
284
+
285
+ <details>
286
+ <summary><code>DELETE</code> <b><code>xai.v1.collections.documents</code></b></summary>
287
+
288
+ <code>DELETE https://api.x.ai/v1/collections/{collectionId}/documents/{fileId}</code>
289
+
290
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
291
+
292
+ ```typescript
293
+ const res = await xai.v1.collections.documents({ /* ... */ });
294
+ ```
295
+
296
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
297
+
298
+ </details>
299
+
300
+ <details>
301
+ <summary><code>GET</code> <b><code>xai.v1.collections</code></b></summary>
302
+
303
+ <code>GET https://api.x.ai/v1/collections/{paramsOrIdOrSignal}</code>
304
+
305
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
306
+
307
+ ```typescript
308
+ const res = await xai.v1.collections({ /* ... */ });
309
+ ```
310
+
311
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
312
+
313
+ </details>
314
+
315
+ <details>
316
+ <summary><code>GET</code> <b><code>xai.v1.collections.documents</code></b></summary>
317
+
318
+ <code>GET https://api.x.ai/v1/collections/{collectionId}/documents/{paramsOrFileId}</code>
319
+
320
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
321
+
322
+ ```typescript
323
+ const res = await xai.v1.collections.documents({ /* ... */ });
324
+ ```
325
+
326
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
327
+
328
+ </details>
329
+
330
+ <details>
331
+ <summary><code>GET</code> <b><code>xai.v1.collections.documents.batchGet</code></b></summary>
332
+
333
+ <code>GET https://api.x.ai/v1/collections/{collectionId}/documents:batchGet{query}</code>
334
+
335
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
336
+
337
+ ```typescript
338
+ const res = await xai.v1.collections.documents.batchGet({ /* ... */ });
339
+ ```
340
+
341
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
342
+
343
+ </details>
344
+
345
+ <details>
346
+ <summary><code>PATCH</code> <b><code>xai.v1.collections.documents</code></b></summary>
347
+
348
+ <code>PATCH https://api.x.ai/v1/collections/{collectionId}/documents/{fileId}</code>
349
+
350
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
351
+
352
+ ```typescript
353
+ const res = await xai.v1.collections.documents({ /* ... */ });
354
+ ```
355
+
356
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
357
+
358
+ </details>
359
+
360
+ <details>
361
+ <summary><code>POST</code> <b><code>xai.v1.collections</code></b></summary>
362
+
363
+ <code>POST https://api.x.ai/v1/collections</code>
364
+
365
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
366
+
367
+ ```typescript
368
+ const res = await xai.v1.collections({ /* ... */ });
369
+ ```
370
+
371
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
372
+
373
+ </details>
374
+
375
+ <details>
376
+ <summary><code>POST</code> <b><code>xai.v1.collections.documents</code></b></summary>
377
+
378
+ <code>POST https://api.x.ai/v1/collections/{collectionId}/documents/{fileId}</code>
379
+
380
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
381
+
382
+ ```typescript
383
+ const res = await xai.v1.collections.documents({ /* ... */ });
384
+ ```
385
+
386
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
387
+
388
+ </details>
389
+
390
+ <details>
391
+ <summary><code>PUT</code> <b><code>xai.v1.collections</code></b></summary>
392
+
393
+ <code>PUT https://api.x.ai/v1/collections/{collectionId}</code>
394
+
395
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
396
+
397
+ ```typescript
398
+ const res = await xai.v1.collections({ /* ... */ });
399
+ ```
400
+
401
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
402
+
403
+ </details>
404
+
405
+ ### customVoices
406
+
407
+ <details>
408
+ <summary><code>POST</code> <b><code>xai.v1.customVoices</code></b></summary>
409
+
410
+ <code>POST https://api.x.ai/v1/custom-voices</code>
411
+
412
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
413
+
414
+ ```typescript
415
+ const res = await xai.v1.customVoices({ /* ... */ });
416
+ ```
417
+
418
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
419
+
420
+ </details>
421
+
422
+ ### documents
423
+
424
+ <details>
425
+ <summary><code>POST</code> <b><code>xai.v1.documents.search</code></b></summary>
426
+
427
+ <code>POST https://api.x.ai/v1/documents/search</code>
428
+
429
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
430
+
431
+ ```typescript
432
+ const res = await xai.v1.documents.search({ /* ... */ });
433
+ ```
434
+
435
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
436
+
437
+ </details>
438
+
439
+ ### files
440
+
441
+ <details>
442
+ <summary><code>DELETE</code> <b><code>xai.v1.files</code></b></summary>
443
+
444
+ <code>DELETE https://api.x.ai/v1/files/{fileId}</code>
445
+
446
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
447
+
448
+ ```typescript
449
+ const res = await xai.v1.files({ /* ... */ });
450
+ ```
451
+
452
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
453
+
454
+ </details>
455
+
456
+ <details>
457
+ <summary><code>GET</code> <b><code>xai.v1.files</code></b></summary>
458
+
459
+ <code>GET https://api.x.ai/v1/files/{fileIdOrSignal}</code>
460
+
461
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
462
+
463
+ ```typescript
464
+ const res = await xai.v1.files({ /* ... */ });
465
+ ```
466
+
467
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
468
+
469
+ </details>
470
+
471
+ <details>
472
+ <summary><code>POST</code> <b><code>xai.v1.files</code></b></summary>
473
+
474
+ <code>POST https://api.x.ai/v1/files</code>
475
+
476
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
477
+
478
+ ```typescript
479
+ const res = await xai.v1.files({ /* ... */ });
480
+ ```
481
+
482
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
483
+
484
+ </details>
485
+
486
+ ### imageGenerationModels
487
+
488
+ <details>
489
+ <summary><code>GET</code> <b><code>xai.v1.imageGenerationModels</code></b></summary>
490
+
491
+ <code>GET https://api.x.ai/v1/image-generation-models/{modelIdOrSignal}</code>
492
+
493
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
494
+
495
+ ```typescript
496
+ const res = await xai.v1.imageGenerationModels({ /* ... */ });
497
+ ```
498
+
499
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
500
+
501
+ </details>
502
+
503
+ ### images
504
+
505
+ <details>
506
+ <summary><code>POST</code> <b><code>xai.v1.images.edits</code></b></summary>
507
+
508
+ <code>POST https://api.x.ai/v1/images/edits</code>
509
+
510
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
511
+
512
+ ```typescript
513
+ const res = await xai.v1.images.edits({ /* ... */ });
514
+ ```
515
+
516
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
517
+
518
+ </details>
519
+
520
+ <details>
521
+ <summary><code>POST</code> <b><code>xai.v1.images.generations</code></b></summary>
522
+
523
+ <code>POST https://api.x.ai/v1/images/generations</code>
524
+
525
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
526
+
527
+ ```typescript
528
+ const res = await xai.v1.images.generations({ /* ... */ });
529
+ ```
530
+
531
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
532
+
533
+ </details>
534
+
535
+ ### languageModels
536
+
537
+ <details>
538
+ <summary><code>GET</code> <b><code>xai.v1.languageModels</code></b></summary>
539
+
540
+ <code>GET https://api.x.ai/v1/language-models/{modelIdOrSignal}</code>
541
+
542
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
543
+
544
+ ```typescript
545
+ const res = await xai.v1.languageModels({ /* ... */ });
546
+ ```
547
+
548
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
549
+
550
+ </details>
551
+
552
+ ### models
553
+
554
+ <details>
555
+ <summary><code>GET</code> <b><code>xai.v1.models</code></b></summary>
556
+
557
+ <code>GET https://api.x.ai/v1/models/{modelIdOrSignal}</code>
558
+
559
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
560
+
561
+ ```typescript
562
+ const res = await xai.v1.models({ /* ... */ });
563
+ ```
564
+
565
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
566
+
567
+ </details>
568
+
569
+ ### realtime
570
+
571
+ <details>
572
+ <summary><code>POST</code> <b><code>xai.v1.realtime.clientSecrets</code></b></summary>
573
+
574
+ <code>POST https://api.x.ai/v1/realtime/client_secrets</code>
575
+
576
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
577
+
578
+ ```typescript
579
+ const res = await xai.v1.realtime.clientSecrets({ /* ... */ });
580
+ ```
581
+
582
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
583
+
584
+ </details>
585
+
586
+ ### responses
587
+
588
+ <details>
589
+ <summary><code>DELETE</code> <b><code>xai.v1.responses</code></b></summary>
590
+
591
+ <code>DELETE https://api.x.ai/v1/responses/{id}</code>
592
+
593
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
594
+
595
+ ```typescript
596
+ const res = await xai.v1.responses({ /* ... */ });
597
+ ```
598
+
599
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
600
+
601
+ </details>
602
+
603
+ <details>
604
+ <summary><code>GET</code> <b><code>xai.v1.responses</code></b></summary>
605
+
606
+ <code>GET https://api.x.ai/v1/responses/{id}</code>
607
+
608
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
609
+
610
+ ```typescript
611
+ const res = await xai.v1.responses({ /* ... */ });
612
+ ```
613
+
614
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
615
+
616
+ </details>
617
+
618
+ <details>
619
+ <summary><code>POST</code> <b><code>xai.v1.responses</code></b></summary>
620
+
621
+ <code>POST https://api.x.ai/v1/responses</code>
622
+
623
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
624
+
625
+ ```typescript
626
+ const res = await xai.v1.responses({ /* ... */ });
627
+ ```
628
+
629
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
630
+
631
+ </details>
632
+
633
+ ### stt
634
+
635
+ <details>
636
+ <summary><code>POST</code> <b><code>xai.v1.stt</code></b></summary>
637
+
638
+ <code>POST https://api.x.ai/v1/stt</code>
639
+
640
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
641
+
642
+ ```typescript
643
+ const res = await xai.v1.stt({ /* ... */ });
644
+ ```
645
+
646
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
647
+
648
+ </details>
649
+
650
+ ### tokenizeText
651
+
652
+ <details>
653
+ <summary><code>POST</code> <b><code>xai.v1.tokenizeText</code></b></summary>
654
+
655
+ <code>POST https://api.x.ai/v1/tokenize-text</code>
656
+
657
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
658
+
659
+ ```typescript
660
+ const res = await xai.v1.tokenizeText({ /* ... */ });
661
+ ```
662
+
663
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
664
+
665
+ </details>
666
+
667
+ ### tts
668
+
669
+ <details>
670
+ <summary><code>POST</code> <b><code>xai.v1.tts</code></b></summary>
671
+
672
+ <code>POST https://api.x.ai/v1/tts</code>
673
+
674
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
675
+
676
+ ```typescript
677
+ const res = await xai.v1.tts({ /* ... */ });
678
+ ```
679
+
680
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
681
+
682
+ </details>
683
+
684
+ ### videoGenerationModels
685
+
686
+ <details>
687
+ <summary><code>GET</code> <b><code>xai.v1.videoGenerationModels</code></b></summary>
688
+
689
+ <code>GET https://api.x.ai/v1/video-generation-models/{modelIdOrSignal}</code>
690
+
691
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
692
+
693
+ ```typescript
694
+ const res = await xai.v1.videoGenerationModels({ /* ... */ });
695
+ ```
696
+
697
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
698
+
699
+ </details>
700
+
701
+ ### videos
702
+
703
+ <details>
704
+ <summary><code>GET</code> <b><code>xai.v1.videos</code></b></summary>
705
+
706
+ <code>GET https://api.x.ai/v1/videos/{requestId}</code>
707
+
708
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
709
+
710
+ ```typescript
711
+ const res = await xai.v1.videos({ /* ... */ });
712
+ ```
713
+
714
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
715
+
716
+ </details>
717
+
718
+ <details>
719
+ <summary><code>POST</code> <b><code>xai.v1.videos.edits</code></b></summary>
720
+
721
+ <code>POST https://api.x.ai/v1/videos/edits</code>
722
+
723
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
724
+
725
+ ```typescript
726
+ const res = await xai.v1.videos.edits({ /* ... */ });
727
+ ```
728
+
729
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
730
+
731
+ </details>
732
+
733
+ <details>
734
+ <summary><code>POST</code> <b><code>xai.v1.videos.extensions</code></b></summary>
735
+
736
+ <code>POST https://api.x.ai/v1/videos/extensions</code>
737
+
738
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
739
+
740
+ ```typescript
741
+ const res = await xai.v1.videos.extensions({ /* ... */ });
742
+ ```
743
+
744
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
745
+
746
+ </details>
747
+
748
+ <details>
749
+ <summary><code>POST</code> <b><code>xai.v1.videos.generations</code></b></summary>
750
+
751
+ <code>POST https://api.x.ai/v1/videos/generations</code>
752
+
753
+ [Upstream docs ↗](https://docs.x.ai/docs/api-reference)
754
+
755
+ ```typescript
756
+ const res = await xai.v1.videos.generations({ /* ... */ });
757
+ ```
758
+
759
+ Source: [`packages/provider/xai/src/xai.ts`](src/xai.ts)
760
+
761
+ </details>
762
+
763
+ ## Middleware
764
+
765
+ ```typescript
766
+ import { xai as createXai, withRetry } from "@apicity/xai";
767
+
768
+ const xai = createXai({ apiKey: process.env.XAI_API_KEY! });
769
+ const models = withRetry(xai.get.v1.models, { retries: 3 });
770
+ ```
771
+
772
+ ## Rate Limiting
773
+
774
+ Client-side rate limiting that queues requests to stay within xAI API limits.
775
+
776
+ ```typescript
777
+ import {
778
+ xai as createXai,
779
+ withRateLimit,
780
+ withRetry,
781
+ createRateLimiter,
782
+ XAI_RATE_LIMITS,
783
+ } from "@apicity/xai";
784
+
785
+ const xai = createXai({ apiKey: process.env.XAI_API_KEY! });
786
+ ```
787
+
788
+ ### Using xAI tier presets
789
+
790
+ ```typescript
791
+ // Use built-in tier presets (free, tier1, tier2, tier3, tier4)
792
+ const limiter = createRateLimiter(XAI_RATE_LIMITS.tier1);
793
+ // => { rpm: 60, concurrent: 10 }
794
+
795
+ const chat = withRateLimit(xai.post.v1.chat.completions, limiter);
796
+ ```
797
+
798
+ ### Custom limits
799
+
800
+ ```typescript
801
+ const limiter = createRateLimiter({ rpm: 30, concurrent: 5 });
802
+ const chat = withRateLimit(xai.post.v1.chat.completions, limiter);
803
+ ```
804
+
805
+ ### Shared limiter across endpoints
806
+
807
+ RPM limits apply globally, so share a single limiter across all endpoints:
808
+
809
+ ```typescript
810
+ const limiter = createRateLimiter(XAI_RATE_LIMITS.tier2);
811
+
812
+ const chat = withRateLimit(xai.post.v1.chat.completions, limiter);
813
+ const responses = withRateLimit(xai.post.v1.responses, limiter);
814
+ const images = withRateLimit(xai.post.v1.images.generations, limiter);
815
+ ```
816
+
817
+ ### Composing with retry
818
+
819
+ Place `withRateLimit` innermost so retries count against the limit:
820
+
821
+ ```typescript
822
+ const limiter = createRateLimiter(XAI_RATE_LIMITS.tier1);
823
+
824
+ const chat = withRetry(
825
+ withRateLimit(xai.post.v1.chat.completions, limiter),
826
+ { retries: 2 }
827
+ );
828
+ ```
829
+
830
+ ### Batch processing
831
+
832
+ Fire requests in parallel — the limiter handles pacing automatically:
833
+
834
+ ```typescript
835
+ const limiter = createRateLimiter(XAI_RATE_LIMITS.tier1);
836
+ const chat = withRateLimit(xai.post.v1.chat.completions, limiter);
837
+
838
+ const results = await Promise.all(
839
+ prompts.map((p) =>
840
+ chat({
841
+ model: "grok-3",
842
+ messages: [{ role: "user", content: p }],
843
+ })
844
+ )
845
+ );
846
+ ```
847
+
848
+ ### xAI rate limit tiers
849
+
850
+ | Preset | RPM | Concurrent | Spend threshold |
851
+ |--------|-----|------------|-----------------|
852
+ | `free` | 5 | 2 | $0 |
853
+ | `tier1` | 60 | 10 | $0+ |
854
+ | `tier2` | 200 | 25 | $100+ |
855
+ | `tier3` | 500 | 50 | $500+ |
856
+ | `tier4` | 1000 | 100 | $1,000+ |
857
+
858
+ Part of the [apicity](https://github.com/justintanner/apicity) monorepo.
859
+
860
+ ## License
861
+
862
+ MIT — see [LICENSE](LICENSE).