Chat completions
POST /v1/chat/completions
Call a text model. Unlike image and video, this endpoint is synchronous: one round trip gives you the answer, no polling. Request and response follow OpenAI chat/completions, streaming included, so any OpenAI SDK works by swapping the base URL.
Parameters
| Parameter | Type | Description |
|---|---|---|
| model | string | Required. Model id from /v1/models where type is text |
| messages | object[] | Required. Each item {role, content}; role is system, user or assistant. Max 64 per request |
| stream | boolean | Optional. true switches to SSE deltas; default false returns the whole reply at once |
| temperature | number | Optional. Passed through to the model |
| top_p | number | Optional. Passed through |
| max_tokens | integer | Optional. Passed through |
| stop | string|string[] | Optional. Passed through |
| presence_penalty / frequency_penalty | number | Optional. Passed through |
model is required and has no default: text models differ widely in tone, length and price, so quietly picking one would be making a decision on your behalf that you never saw.
Request
bash
curl https://open.pikpikgo.com/v1/chat/completions \
-H "Authorization: Bearer $PIKPIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "text-fast-1",
"messages": [
{ "role": "system", "content": "You are a screenwriter who specialises in short drama." },
{ "role": "user", "content": "Give me an opening for an urban mystery short, three sentences at most." }
]
}'Response
json
{
"id": "chatcmpl-2608102214300000123456",
"object": "chat.completion",
"created": 1786372470,
"model": "text-fast-1",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "At midnight the lift stops at floor 13, but the building only has twelve." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 38, "completion_tokens": 126, "total_tokens": 164 },
"credits": 4
}| Field | Description |
|---|---|
| choices[0].message.content | The reply text |
| choices[0].finish_reason | stop for a normal end, length when max_tokens was hit |
| usage | Token counts. Text is billed per token, so this is the basis of the charge |
| credits | Credits actually charged (not an OpenAI field) |
Text is billed per token, not per call: credits = input tokens × input rate + output tokens × output rate, rounded up, minimum 1 per call. Rates are quoted per million tokens on the pricing page.
Streaming
With stream: true the response becomes text/event-stream and arrives as chat.completion.chunk frames: the first declares the role, content comes one delta per frame, then a frame carrying finish_reason, then a final frame with an empty choices array plus usage and credits, closed by data: [DONE].
bash
curl -N https://open.pikpikgo.com/v1/chat/completions \
-H "Authorization: Bearer $PIKPIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "text-fast-1",
"messages": [
{ "role": "user", "content": "Give me an opening for an urban mystery short, three sentences at most." }
],
"stream": true
}'text
data: {"id":"chatcmpl-2608102214300000123456","object":"chat.completion.chunk","created":1786372470,"model":"text-fast-1","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-2608102214300000123456","object":"chat.completion.chunk","created":1786372470,"model":"text-fast-1","choices":[{"index":0,"delta":{"content":"At mid"},"finish_reason":null}]}
data: {"id":"chatcmpl-2608102214300000123456","object":"chat.completion.chunk","created":1786372470,"model":"text-fast-1","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"chatcmpl-2608102214300000123456","object":"chat.completion.chunk","created":1786372470,"model":"text-fast-1","choices":[],"usage":{"prompt_tokens":38,"completion_tokens":126,"total_tokens":164},"credits":4}
data: [DONE]With the official OpenAI SDK you never touch the SSE parsing:
javascript
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.PIKPIK_API_KEY,
baseURL: 'https://open.pikpikgo.com/v1',
})
// Streaming: deltas frame by frame, with usage and credits on the final frame
const stream = await client.chat.completions.create({
model: 'text-fast-1',
messages: [{ role: 'user', content: 'Give me an opening for an urban mystery short, three sentences at most.' }],
stream: true,
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '')
}Streaming is billed from the usage on the final frame; if upstream omits it, the call is charged the 1-credit minimum. Also note that once frames start flowing the HTTP status is already 200 — a mid-stream failure cannot become a 4xx/5xx. It arrives as an in-stream {"error": {...}} frame followed by the usual [DONE], so handle that frame while reading.

