Quickstart
From zero to your first generated image in three steps.
Step 1 — Create a key
Open Account → API platform and hit Create key. Give it a name you will recognise later. The key is copied to your clipboard as soon as it is created, and you can copy it again from the list whenever you need it.
Step 2 — See which models are available
Put the key in an environment variable and list the models. The id in the response is what you pass as model later.
curl https://open.pikpikgo.com/v1/models \
-H "Authorization: Bearer $PIKPIK_API_KEY"Step 3 — Generate an image
curl https://open.pikpikgo.com/v1/images/generations \
-H "Authorization: Bearer $PIKPIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a rainy cyberpunk street at night, neon reflections",
"size": "16:9",
"quality": "2k",
"n": 1
}'This is synchronous by default: the call waits until the image is ready and data[0].url holds it — no task lookup needed. A single image usually takes over 30 seconds, so raise your client timeout to 300 seconds.
{
"created": 1786000246,
"data": [
{ "url": "https://cdn.pikpikgo.com/ai/xxxx.png", "revised_prompt": "a rainy cyberpunk street at night, neon reflections" }
],
"id": "2608101909450810293847",
"object": "image.generation",
"model": "img-std-1",
"status": "succeeded",
"n": 1,
"credits": 10,
"tasks": [
{
"id": "2608101909450810293847",
"status": "succeeded",
"data": [{ "url": "https://cdn.pikpikgo.com/ai/xxxx.png", "revised_prompt": "a rainy cyberpunk street at night, neon reflections" }]
}
]
}Step 4 — Prefer not to wait? Go asynchronous
Add async: true and the call returns straight away with status queued and an empty data array — nothing has been rendered yet. Keep tasks[0].id.
curl https://open.pikpikgo.com/v1/images/generations \
-H "Authorization: Bearer $PIKPIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a rainy cyberpunk street at night, neon reflections",
"size": "16:9",
"quality": "2k",
"n": 1,
"async": true
}'{
"id": "2608101909450810293847",
"object": "image.generation",
"created": 1786000185,
"model": "img-std-1",
"status": "queued",
"n": 1,
"credits": 10,
"tasks": [
{ "id": "2608101909450810293847", "status": "queued", "data": [] }
],
"data": []
}Poll that id; once status is succeeded, data[0].url holds the image. Video generation only works this way.
curl https://open.pikpikgo.com/v1/tasks/2608101909450810293847 \
-H "Authorization: Bearer $PIKPIK_API_KEY"{
"id": "2608101909450810293847",
"object": "image.generation",
"created": 1786000185,
"model": "img-std-1",
"status": "succeeded",
"prompt": "a rainy cyberpunk street at night, neon reflections",
"size": "16:9",
"quality": "2k",
"credits": 10,
"error": "",
"data": [
{ "url": "https://cdn.pikpikgo.com/ai/xxxx.png", "revised_prompt": "a rainy cyberpunk street at night, neon reflections" }
]
}Full examples
The synchronous path works with the official OpenAI SDK — only the baseURL changes:
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.PIKPIK_API_KEY,
baseURL: 'https://open.pikpikgo.com/v1',
timeout: 300 * 1000,
})
// Synchronous: one round trip returns the URL, timeout raised to 300 seconds
const res = await client.images.generate({
model: 'img-std-1',
prompt: 'a rainy cyberpunk street at night, neon reflections',
size: '1792x1024',
n: 1,
})
console.log(res.data[0].url)The asynchronous path needs submit and poll wired together, Node.js:
const BASE = 'https://open.pikpikgo.com/v1'
const KEY = process.env.PIKPIK_API_KEY
async function call(path, body) {
const res = await fetch(BASE + path, {
method: body ? 'POST' : 'GET',
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const json = await res.json()
if (json.error) throw new Error(json.error.message)
return json
}
// Submit and wait for completion: poll every 3 seconds, give up after 10 minutes
async function generateImage(prompt) {
const job = await call('/images/generations', { prompt, size: '16:9', async: true })
const id = job.tasks[0].id
for (let i = 0; i < 200; i++) {
const task = await call(`/tasks/${id}`)
if (task.status === 'succeeded') return task.data[0].url
if (task.status === 'failed') throw new Error(task.error || 'generation failed')
await new Promise((r) => setTimeout(r, 3000))
}
throw new Error('timed out waiting')
}
generateImage('a rainy cyberpunk street at night, neon reflections').then(console.log)Python (video this time, with a longer poll interval):
import os, time, requests
BASE = "https://open.pikpikgo.com/v1"
KEY = os.environ["PIKPIK_API_KEY"]
HEAD = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
def call(path, body=None):
if body is None:
r = requests.get(BASE + path, headers=HEAD, timeout=30)
else:
r = requests.post(BASE + path, headers=HEAD, json=body, timeout=60)
data = r.json()
if "error" in data:
raise RuntimeError(data["error"]["message"])
return data
def generate_video(prompt, seconds=5):
job = call("/video/generations", {"prompt": prompt, "duration": seconds})
# Video usually takes 1-5 minutes; polling every 5 seconds is plenty, tighter loops only burn rate limit
for _ in range(200):
task = call(f"/video/generations/{job['task_id']}")
if task["status"] == "succeeded":
return task["url"]
if task["status"] == "failed":
raise RuntimeError(task["error"] or "generation failed")
time.sleep(5)
raise TimeoutError("timed out waiting")
print(generate_video("slow dolly in, a girl turns back and smiles"))
