Quickstart

Create an ASOP token and call chat, KREA2, REDQW21 (Qwen Image 2.1), and listed reference-to-video models. Credits share the Create-page wallet.

Get your API token

Open asop.uk/api and generate a token. Format is asop- plus 32 hex characters, for example asop-a1b2…. asop.uk/api

Save it locally or download the JSON. The list on the site stays masked; use View to reveal again. Revoke it if it leaks.

Set up your API key

Export it in your shell:

export ASOP_API_KEY='asop-your-token-here'

Or add it to a .env file:

ASOP_API_KEY=asop-your-token-here

Install the SDK

ASOP is OpenAI-compatible. Use the OpenAI SDK, or skip this step and call HTTP directly.

pip install openai
npm install openai

Send your first request

Base URL: https://asop.uk/api/v1. Chat uses model prompt-assist.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("ASOP_API_KEY"),
    base_url="https://asop.uk/api/v1",
)

completion = client.chat.completions.create(
    model="prompt-assist",
    messages=[
        {"role": "system", "content": "You write cinematic still prompts."},
        {"role": "user", "content": "A wet neon alley at 2 a.m., rain on asphalt."},
    ],
)

print(completion.choices[0].message.content)
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.ASOP_API_KEY,
  baseURL: "https://asop.uk/api/v1",
});

const completion = await client.chat.completions.create({
  model: "prompt-assist",
  messages: [
    { role: "system", content: "You write cinematic still prompts." },
    { role: "user", content: "A wet neon alley at 2 a.m., rain on asphalt." },
  ],
});

console.log(completion.choices[0].message.content);
curl https://asop.uk/api/v1/chat/completions \
  -H "Authorization: Bearer $ASOP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "prompt-assist",
    "messages": [
      {"role": "system", "content": "You write cinematic still prompts."},
      {"role": "user", "content": "A wet neon alley at 2 a.m., rain on asphalt."}
    ]
  }'

Message roles: system (instructions), user (prompt), assistant (prior turns). Vision is supported: put image_url parts on a user message for reverse-prompt work.

Generate an image

KREA2 and REDQW21 (Qwen Image 2.1) are async: they return a job id — poll GET /jobs/{id}. Official Image3 / Dream5 are delisted.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("ASOP_API_KEY"),
    base_url="https://asop.uk/api/v1",
)

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.ASOP_API_KEY,
  baseURL: "https://asop.uk/api/v1",
});

const img = await client.images.generate({
  model: "heishou-krea2",
  prompt: "Portrait still, wet neon alley, 2:3, cinematic grain",
  size: "1024x1536",
  image_resolution: "6MP",
  krea2Mode: "story",
});

console.log(img.id, img.status);
curl https://asop.uk/api/v1/images/generations \
  -H "Authorization: Bearer $ASOP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "heishou-krea2",
    "prompt": "Portrait still, wet neon alley, 2:3, cinematic grain",
    "size": "1024x1536",
    "aspect_ratio": "2:3",
    "image_resolution": "6MP",
    "krea2Mode": "story"
  }'

Reference-to-video

POST /files first, then POST /videos/generations. Jobs are async. Poll GET /jobs/{id}; output.url is a 1-hour signed link.

import os, time, requests

base = "https://asop.uk/api/v1"
headers = {"Authorization": f"Bearer {os.environ['ASOP_API_KEY']}"}

up = requests.post(
    f"{base}/files",
    headers=headers,
    files={"file": ("ref.png", open("ref.png", "rb"), "image/png")},
    data={"purpose": "reference"},
)
file_id = up.json()["id"]

job = requests.post(f"{base}/videos/generations", headers=headers, json={
    "model": "heishou-h3",
    "prompt": "Slow push-in, cinematic grain, 2:3",
    "duration": 8,
    "aspect_ratio": "2:3",
    "images": [file_id],
}).json()

while True:
    row = requests.get(f"{base}/jobs/{job['id']}", headers=headers).json()
    if row["status"] in ("completed", "failed"):
        break
    time.sleep(4)
print(row.get("output") or row.get("error"))
curl https://asop.uk/api/v1/files \
  -H "Authorization: Bearer $ASOP_API_KEY" \
  -F "[email protected]" \
  -F "purpose=reference"

curl https://asop.uk/api/v1/videos/generations \
  -H "Authorization: Bearer $ASOP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "heishou-h3",
    "prompt": "Slow push-in, cinematic grain, 2:3",
    "duration": 8,
    "aspect_ratio": "2:3",
    "images": ["file_YOUR_ID"]
  }'

curl https://asop.uk/api/v1/jobs/JOB_ID \
  -H "Authorization: Bearer $ASOP_API_KEY"

Models you can call

All nine IDs work on this relay. Credits match the Create estimate (same wallet, live quote).

Loading live quotes…

Higher resolution, longer video, or more chat tokens costs more — same rule as Create → Estimate.

List models: GET https://asop.uk/api/v1/models with the same Bearer token.

Enable streaming (optional)

stream = client.chat.completions.create(
    model="prompt-assist",
    messages=[{"role": "user", "content": "Write a 4-beat video prompt."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
const stream = await client.chat.completions.create({
  model: "prompt-assist",
  messages: [{ role: "user", content: "Write a 4-beat video prompt." }],
  stream: true,
});
for await (const chunk of stream) {
  const t = chunk.choices?.[0]?.delta?.content;
  if (t) process.stdout.write(t);
}
curl https://asop.uk/api/v1/chat/completions \
  -H "Authorization: Bearer $ASOP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"prompt-assist","stream":true,"messages":[{"role":"user","content":"Hello"}]}'

Credits

API calls debit the same credit wallet as the Create page. The Credits ledger tags each row as Demo or API and names the model. Failed generations are refunded. 402 means the wallet is empty.

Next steps

Agents: this page is also Markdown. Index: Markdown · /docs/llms.txt.