Kelyx API

Post from your own tools

The Kelyx write API lets you publish content straight from a script or a local LLM using a personal API key. A key can only ever create content on your own account — it can't read or change anyone else's data.

Authentication

Generate a key in Settings → API Keys. It's shown once — store it somewhere safe. Send it on every request in the X-API-Key header, over HTTPS only. Keep it in an environment variable, never in code you share.

X-API-Key: kelyx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Publishing flow

Media is uploaded first, then referenced by URL when you create the content — this keeps large files (video, audio) off the JSON request. Three steps:

  1. POST /api/v1/me — get your creator id.
  2. POST /api/v1/upload — upload media to {type}/{id}/….
  3. POST /api/v1/content/:type — create the post referencing the uploaded URL.
# 1. Find your creator id (needed to build upload keys)
ID=$(curl -s -X POST "https://kelyx-upload.4neodesigns.workers.dev/api/v1/me" \
  -H "X-API-Key: $KELYX_API_KEY" | python -c "import sys,json;print(json.load(sys.stdin)['id'])")

# 2. Upload the image to your own media folder
curl -X POST "https://kelyx-upload.4neodesigns.workers.dev/api/v1/upload?key=image/$ID/$(date +%s)-art.png&contentType=image/png" \
  -H "X-API-Key: $KELYX_API_KEY" \
  --data-binary @art.png
# -> { "publicUrl": "https://media.kelyxai.com/image/<id>/1718-art.png" }

# 3. Create the image post, referencing the uploaded URL
curl -X POST "https://kelyx-upload.4neodesigns.workers.dev/api/v1/content/image" \
  -H "X-API-Key: $KELYX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Neon dreams",
    "image_urls": ["https://media.kelyxai.com/image/<id>/1718-art.png"],
    "show_prompt": true,
    "tags": ["scifi", "neon"]
  }'
# -> { "id": "…", "type": "image" }

Python

import os, time, requests

BASE = "https://kelyx-upload.4neodesigns.workers.dev"
KEY = os.environ["KELYX_API_KEY"]  # never hard-code your key
HEADERS = {"X-API-Key": KEY}

# 1. Your creator id
creator_id = requests.post(f"{BASE}/api/v1/me", headers=HEADERS).json()["id"]

# 2. Upload media to your own folder
key = f"image/{creator_id}/{int(time.time())}-art.png"
with open("art.png", "rb") as f:
    up = requests.post(
        f"{BASE}/api/v1/upload",
        params={"key": key, "contentType": "image/png"},
        headers=HEADERS,
        data=f,
    )
up.raise_for_status()
public_url = up.json()["publicUrl"]

# 3. Create the post
res = requests.post(
    f"{BASE}/api/v1/content/image",
    headers=HEADERS,
    json={"title": "Neon dreams", "image_urls": [public_url], "tags": ["scifi"]},
)
res.raise_for_status()
print("Published:", res.json()["id"])

Content fields

Image

POST /api/v1/content/image
FieldTypeRequiredNotes
titlestringYesUp to 200 chars.
image_urlsstring[]YesYour uploaded media URLs (1–20).
descriptionstringNo
ai_toolstringNoe.g. "Midjourney".
promptstringNo
show_promptbooleanNoDefault false.
tagsstring[]NoUp to 20.

Video

POST /api/v1/content/video
FieldTypeRequiredNotes
titlestringYes
video_urlstringYesYour uploaded video URL.
typestringNo"movie" or "short". Default "movie".
synopsisstringNo
poster_urlstringNoYour uploaded image URL.
runtime_secondsintegerNo
genrestring[]No
ai_toolsstring[]No

Music track

POST /api/v1/content/track
FieldTypeRequiredNotes
titlestringYes
audio_urlstringYesUploaded under audio/<id>/…
cover_urlstringNoYour uploaded image URL.
genrestring[]No
lyricsstringNo
duration_secondsintegerNo

Book

POST /api/v1/content/book
FieldTypeRequiredNotes
titlestringYesA slug is generated for you.
synopsisstringNo
cover_urlstringNoYour uploaded image URL.
genrestring[]No
statusstringNo"ongoing" | "complete" | "hiatus".

Comic

POST /api/v1/content/comic
FieldTypeRequiredNotes
titlestringYesA slug is generated for you.
synopsisstringNo
cover_urlstringNoYour uploaded image URL.
banner_urlstringNoYour uploaded image URL.
genrestring[]No
ratingstringNo"G" | "PG" | "PG-13" | "18+".
statusstringNo"ongoing" | "complete" | "hiatus".
formatstringNo"webtoon" | "traditional".

Rate limits & errors

  • Content writes: 60 requests per minute, per key.
  • Media uploads: 300 requests per minute, per account.
  • Errors return a JSON body { "error": "…" } with a matching HTTP status: 400 (validation), 401 (bad or missing key), 429 (rate limited).