Janvia

API reference

OpenAI-compatible endpoints

Send OpenAI's image and speech requests with the OpenAI SDKs; each one starts a task and answers at once.

Requests written for OpenAI's image and speech endpoints work here: set the base URL to https://janvia.io/v1 and use an API key from this site. model must be one of our model IDs; see Models.

Unlike OpenAI, these endpoints don't keep the connection open until the result is ready. Each request starts an ordinary task and answers at once with the task object. Fetch the result with GET /v1/tasks/TASK_ID, or pass callback_url and we POST the task to you when it ends (see Callbacks). Routing, billing and refunds are those of the task API.

Three endpoints are supported:

  • POST /v1/images/generations: text to image, JSON.
  • POST /v1/images/edits: image editing and reference images, multipart, with images in image or image[].
  • POST /v1/audio/speech: text to speech, JSON.

For video, use the task API. OpenAI's own video API has been shut down, so there is no equivalent here.

Submit, then fetch the result

curl https://janvia.io/v1/images/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red fox in the snow",
    "size": "1536x1024",
    "callback_url": "https://example.com/hooks/relay"
  }'

The answer is HTTP 200 with the task, usually queued. The X-Relay-Task-Id and Location headers name it too:

{
  "id": "task_9f2c...",
  "object": "task",
  "model": "gpt-image-2",
  "status": "queued",
  "output": null,
  "cost_usd": 0.06,
  ...
}

Fetch it until status is completed or failed; the files are in output.files[].url:

curl https://janvia.io/v1/tasks/task_9f2c... \
  -H "Authorization: Bearer $API_KEY"

Python

The SDK call returns the task rather than OpenAI's data list. Our own fields, such as callback_url and route, go in extra_body.

import time

import httpx
from openai import OpenAI

client = OpenAI(base_url="https://janvia.io/v1", api_key="sk_xxx")

task = client.images.generate(
    model="gpt-image-2",
    prompt="a red fox in the snow",
    size="1536x1024",
    # extra_body={"callback_url": "https://example.com/hooks/relay"},
).to_dict()

while task["status"] not in ("completed", "failed"):
    time.sleep(3)
    task = client.get(f"/tasks/{task['id']}", cast_to=httpx.Response).json()

if task["status"] == "completed":
    print(task["output"]["files"][0]["url"])
else:
    print(task["error"])

Editing works the same way: client.images.edit(model=..., image=[open("product.png", "rb")], prompt=...) returns the task. So does client.audio.speech.create(...), whose response you read with .json(); once the task completes, the audio is at output.files[0].url.

Node.js

import OpenAI from 'openai';

const client = new OpenAI({ baseURL: 'https://janvia.io/v1', apiKey: 'sk_xxx' });

let task = await client.images.generate({
  model: 'gpt-image-2',
  prompt: 'a red fox in the snow',
  size: '1536x1024',
  // callback_url: 'https://example.com/hooks/relay',
});

while (!['completed', 'failed'].includes(task.status)) {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  task = await client.get(`/tasks/${task.id}`);
}

console.log(task.output?.files[0].url ?? task.error);

In TypeScript, the SDK types the result as OpenAI's response, so cast it to your own task type. client.audio.speech.create(...) returns a Response: read the task with await response.json().

How parameters map

Images:

  • size: a width and height such as 1024x1536 becomes the nearest aspect_ratio, and the resolution follows the long side: up to 1536 is 1K, up to 2560 is 2K, larger is 4K. auto, or no size, uses the model's default.
  • n: images to generate, 1–4.
  • quality: low, medium, high and auto as is; standard means medium; hd, xhigh and max mean high.
  • output_format: png, jpeg or webp.
  • response_format: only url. Results are files on the task, so b64_json returns 400.
  • Not supported (400): background: "transparent", mask, stream.
  • Other OpenAI parameters are ignored.

You may also send our own fields, which win over the mapping: resolution, aspect_ratio, negative_prompt, seed, image_urls, route (see Routes and routing) and callback_url.

Speech:

  • input is the text to read; voice is a voice ID (a string, or { "id": ... }).
  • response_format: mp3 (default) or wav, the format of the audio file.
  • speed: 0.5–2.
  • stream_format: "sse" is not supported; instructions is ignored.

Errors and retries

An error at submit time (bad parameters, low balance and so on) returns 4xx, creates no task and charges nothing. A task that fails later is refunded in full, with its error code on the task.

The SDKs retry timeouts, network errors and 5xx responses on their own. To make such a retry return the same task instead of paying for a second one, send an Idempotency-Key:

client.images.generate(
    model="gpt-image-2",
    prompt="a red fox in the snow",
    extra_headers={"Idempotency-Key": "order-123"},
)
await client.images.generate(
  { model: 'gpt-image-2', prompt: 'a red fox in the snow' },
  { headers: { 'Idempotency-Key': 'order-123' } }
);