Docs
Candid speaks OpenAI's API. Any OpenAI client library works: point it at Candid's base URL and use a Candid key.
Quick start
- Add credit to your account.
- Create an API key.
- Make your first request:
from openai import OpenAI
client = OpenAI(base_url="https://candidapi.fun/v1", api_key="sk-cnd-…")
reply = client.chat.completions.create(
model="candid-text",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)Authentication
Send your key in the Authorization header as a Bearer token. Keys start with sk-cnd-. Each key can make up to 300 requests a minute.
Authorization: Bearer sk-cnd-…Models
List every model with its type and price. This endpoint doesn't need a key.
curl https://candidapi.fun/v1/modelsText
POST /v1/chat/completions
OpenAI's chat completions format: messages, temperature, max_tokens, tools, response_format and the rest. Set "stream": true to receive tokens as they're written. You're charged for the tokens in and out, at the model's rates.
stream = client.chat.completions.create(
model="candid-text",
messages=[{"role": "user", "content": "Write the warlord's speech before he burns the village."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")Voice
POST /v1/audio/speech
Turns up to 4,096 characters of input into audio. Choose a voice (for example af_sky, am_adam or bf_emma), a speed from 0.5 to 2, and a response_format: mp3, opus, aac, flac, wav or pcm. Priced per character.
audio = client.audio.speech.create(
model="candid-voice",
voice="af_sky",
input="You left your jacket at the bar last night.",
)
audio.write_to_file("speech.mp3")Transcription
POST /v1/audio/transcriptions
Upload audio as multipart form data in file, up to 25 MB. Optionally set language (e.g. en), response_format (json or text) and timestamps. Priced per minute of audio.
with open("call.mp3", "rb") as f:
text = client.audio.transcriptions.create(model="candid-transcribe", file=f)
print(text.text)Images
POST /v1/images/generations
Send a prompt, and optionally n (1–4), size such as 1024x1024 or 832x1216, and output_format (png, jpeg or webp). You can also add negative_prompt, seed and steps. Images come back as base64 in data[].b64_json. Priced per image.
import base64
result = client.images.generate(
model="candid-image",
prompt="a neon-lit noir detective in the rain",
size="832x1216",
)
open("image.png", "wb").write(base64.b64decode(result.data[0].b64_json))Video
Videos take a minute or more, so you start a job and collect the result.
POST /v1/videos/quote
See the price first. Takes the same options as creating a video, and returns price_usd.
POST /v1/videos
Starts a video. Send model, prompt, and optionally seconds, resolution (e.g. 720p), aspect_ratio (e.g. 16:9) and negative_prompt. Image-to-video models also need image, as a URL or a base64 data URL. The price is charged when the video starts, and returned to your balance if it can't be made.
GET /v1/videos/{id}
Its status: queued, in_progress, completed or failed. Check every few seconds.
GET /v1/videos/{id}/content
The finished MP4. Videos are kept for 7 days.
curl https://candidapi.fun/v1/videos \
-H "Authorization: Bearer $CANDID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "candid-video", "prompt": "a velvet curtain opens on an empty cabaret", "seconds": 5, "resolution": "720p"}'
# → {"id": "video_…", "status": "queued", "cost_usd": 1.23, …}
curl https://candidapi.fun/v1/videos/video_… -H "Authorization: Bearer $CANDID_API_KEY"
curl https://candidapi.fun/v1/videos/video_…/content -H "Authorization: Bearer $CANDID_API_KEY" -o video.mp4Errors
Errors use OpenAI's shape, so client libraries raise them as usual:
{
"error": {
"message": "Your balance is empty. Add credit to continue.",
"type": "insufficient_balance",
"code": "insufficient_balance"
}
}400 | Something in the request isn't valid. The message says what. |
401 | The API key is missing or not valid. |
402 | Your balance is too low. Add credit. |
404 | The model or video doesn't exist. |
429 | Too many requests. Wait a moment and retry. |
502, 503 | The model couldn't finish. Retry after a short wait. |
Billing
Every request draws from your prepaid balance. You add credit by paying in USDG on Robinhood Chain from your billing page: link your wallet once, then each payment from it is added to your balance once the network confirms it. One USDG is one dollar of credit. A request that fails isn't charged. A long text request can take your balance slightly below zero; the next request waits until you top up.