### POST /v1/jobs response (201 Created) Source: https://royaltechno.ai/api-docs Example response from POST /v1/jobs with HTTP 201 Created. Shows the job object with queued status, input echo, cost in USD cents, and creation timestamp. ```json { "id": "job_3Xk9aQ2", "object": "job", "model": "veo-3.1", "status": "queued", "input": { "prompt": "a cinematic drone shot over snowy mountains at sunrise", "aspect_ratio": "landscape", "duration_sec": 8 }, "cost_usd_cents": 2, "created_at": "2026-07-29T09:41:00Z" } ``` -------------------------------- ### Poll job status with GET /v1/jobs/{id} Source: https://royaltechno.ai/api-docs Polls a job status with GET /v1/jobs/{id} using If-None-Match for conditional requests. The response shows a succeeded job with output URL and a notice field that is null unless the job shipped below what was ordered. ```http # Poll every 10 s (video) / 5 s (images), all your jobs from ONE loop, sending # the last ETag as If-None-Match → 304 with no body when nothing changed. # On 429 sleep Retry-After. While queued, `queue_position` shows your place # among jobs of the same media type; `retrying: true` = an automatic re-run. # Video takes minutes — never resubmit a slow job: you would pay twice. GET https://api.royaltechno.cc/v1/jobs/job_3Xk9aQ2 If-None-Match: "3f9c1e2b7a4d5c60" ``` ```json { "id": "job_3Xk9aQ2", "status": "succeeded", "output": { "type": "video", "url": "https://cdn.royaltechno.cc/saas/videos/job_3Xk9aQ2.mp4", "mime_type": "video/mp4", "duration_sec": 8 }, "cost_usd_cents": 2, // null unless the job shipped below what you ordered (e.g. an upstream // upscale is down and 1080p came back as 720p). Never silent. "notice": null } ``` -------------------------------- ### GET /v1/jobs/{id} Source: https://royaltechno.ai/api-docs Polls the status of a submitted job. Returns the job details including the output URL when the job has succeeded. ```APIDOC ## GET /v1/jobs/{id} ### Description Polls the status of a submitted job. Returns the job details including the output URL when the job has succeeded. ### Method GET ### Endpoint https://api.royaltechno.cc/v1/jobs/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The job ID returned from the POST /v1/jobs request. #### Headers - **If-None-Match** (string) - Optional - Send the last ETag to receive a 304 response with no body when nothing has changed. ### Request Example ``` GET https://api.royaltechno.cc/v1/jobs/job_3Xk9aQ2 If-None-Match: "3f9c1e2b7a4d5c60" ``` ### Response #### Success Response (200 OK) - **id** (string) - The job ID. - **status** (string) - The current status, e.g., `succeeded`. - **output** (object) - The output details when the job has succeeded. - **type** (string) - The media type, e.g., `video`. - **url** (string) - The stable storage URL of the output. - **mime_type** (string) - The MIME type of the output. - **duration_sec** (integer) - The duration of the video in seconds. - **cost_usd_cents** (integer) - The cost of the job in USD cents. - **notice** (string or null) - A notice if the job shipped below what was ordered, otherwise null. #### Response Example ```json { "id": "job_3Xk9aQ2", "status": "succeeded", "output": { "type": "video", "url": "https://cdn.royaltechno.cc/saas/videos/job_3Xk9aQ2.mp4", "mime_type": "video/mp4", "duration_sec": 8 }, "cost_usd_cents": 2, "notice": null } ``` ``` -------------------------------- ### RoyalTechno AI reference client with sliding-window job submission and polling Source: https://royaltechno.ai/api-docs Reference client for the RoyalTechno AI API. It uses a sliding window to keep at most MAX_IN_FLIGHT jobs in flight, submits with a unique Idempotency-Key per job, polls with If-None-Match/ETag, and treats queue_full as a non-retryable 429 that pauses submission until Retry-After. The main() entry point accepts --model, --prompt, --count, and --kind. ```python for attempt in range(MAX_ATTEMPTS): r = session.request(method, f"{API}{path}", timeout=TIMEOUT_SEC, **kw) if r.status_code in FATAL: raise Fatal(r.json()["error"]) if r.status_code not in RETRYABLE: return r if r.json().get("error", {}).get("code") == "queue_full": return r time.sleep(max(float(r.headers.get("Retry-After", 0)), backoff(attempt))) raise RuntimeError(f"gave up after {MAX_ATTEMPTS} attempts: {method} {path}") ``` ```python def submit(model, job_input): """POST one job -> (job, 0) or (None, seconds to wait) when the queue is full.""" headers = {"Idempotency-Key": str(uuid.uuid4())} # one UUID per job, reused only for its retries r = request("POST", "/jobs", json={"model": model, "input": job_input}, headers=headers) if r.status_code == 429: return None, float(r.headers.get("Retry-After", 60)) return r.json(), 0.0 ``` ```python def poll(job_id, etag): """GET one job with If-None-Match -> (job or None on 304, etag).""" r = request("GET", f"/jobs/{job_id}", headers={"If-None-Match": etag} if etag else {}) if r.status_code == 304: return None, etag return r.json(), r.headers.get("ETag", etag) ``` ```python def run(jobs, kind="image"): """Sliding window: <= MAX_IN_FLIGHT in flight, one poll loop, never resubmits. `jobs` is a list of (model, input) tuples; returns the finished job dicts.""" todo, inflight, done = list(jobs), {}, [] interval, next_submit = POLL_SEC[kind], 0.0 while todo or inflight: now = time.time() while todo and len(inflight) < MAX_IN_FLIGHT and now >= next_submit: job, wait = submit(*todo[0]) if job is None: # queue_full: stop filling, wait Retry-After next_submit = now + wait break todo.pop(0) inflight[job["id"]] = {"etag": None, "next_poll": now + interval} for job_id, st in list(inflight.items()): if st["next_poll"] > now: continue job, st["etag"] = poll(job_id, st["etag"]) st["next_poll"] = now + interval if job and job["status"] in ("succeeded", "failed", "canceled"): done.append(job) del inflight[job_id] wakeups = [st["next_poll"] for st in inflight.values()] + ([next_submit] if todo else []) if wakeups: time.sleep(max(0.2, min(wakeups) - time.time())) return done ``` ```python def main(): ap = argparse.ArgumentParser(description="RoyalTechno AI reference client") ap.add_argument("--model", default="nano-banana-pro") ap.add_argument("--prompt", required=True) ap.add_argument("--count", type=int, default=1, help="how many separate jobs to submit") ap.add_argument("--kind", choices=sorted(POLL_SEC), default="image", help="picks the poll cadence") a = ap.parse_args() for job in run([(a.model, {"prompt": a.prompt})] * a.count, kind=a.kind): detail = (job.get("output") or {}).get("url") or (job.get("error") or {}).get("message") print(job["id"], job["status"], detail) ``` ```python if __name__ == "__main__": main() ``` -------------------------------- ### Submit image job with cURL Source: https://royaltechno.ai/api-docs Submits an image generation job to the same endpoint using the nano-banana-pro model. Requires an API key set as $RT_API_KEY. ```bash curl https://api.royaltechno.cc/v1/jobs \ -H "Authorization: Bearer $RT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "nano-banana-pro", "input": { "prompt": "a royal crown made of glass, studio lighting", "aspect_ratio": "landscape" } }' ``` -------------------------------- ### Python reference client for RoyalTechno AI API Source: https://royaltechno.ai/api-docs Reference Python client that implements the rate-limit rules: sliding window of 20 in-flight jobs, one poll loop with If-None-Match, Retry-After + exponential backoff on 429/503, stop on 4xx, one Idempotency-Key per job, and never resubmits in-flight jobs. Requires Python 3.9+ and the `requests` library; set RT_API_KEY environment variable before running. ```python #!/usr/bin/env python3 """RoyalTechno AI - reference API client (Python 3.9+, `requests` only). Follows every rule in https://royaltechno.ai/api-docs#rules, so copy it as-is: a sliding window of MAX_IN_FLIGHT jobs, ONE poll loop for all jobs with If-None-Match, Retry-After + exponential backoff on 429/503, stop on 4xx, one Idempotency-Key per job, and it never resubmits a job that is in flight. RT_API_KEY=rt_live_... python rt_client.py --model nano-banana-pro \ --prompt "a royal crown made of glass, studio lighting" --count 2 Live numbers for YOUR key: GET /v1/account -> "limits" (they can be higher than the defaults below after a per-key allowance).""" import argparse import os import random import time import uuid import requests API = "https://api.royaltechno.cc/v1" MAX_IN_FLIGHT = 20 # per API key: GET /v1/account -> limits.recommended_max_in_flight POLL_SEC = {"image": 5, "video": 10, "chain": 30} BACKOFF_BASE_SEC, BACKOFF_MAX_SEC, MAX_ATTEMPTS = 2, 60, 6 RETRYABLE = {429, 503} # honour Retry-After, then back off FATAL = {400, 401, 402, 403, 404} # never clears by retrying: fix the request, key, balance or plan TIMEOUT_SEC = 30 session = requests.Session() session.headers["Authorization"] = f"Bearer {os.environ['RT_API_KEY']}" class Fatal(Exception): """A refusal that retrying can never fix.""" def backoff(attempt): """2 s doubling to 60 s, +/- 20 % jitter so retries never line up.""" return min(BACKOFF_MAX_SEC, BACKOFF_BASE_SEC * 2 ** attempt) * random.uniform(0.8, 1.2) def request(method, path, **kw): """One API call with the rules baked in: 4xx raises, 429/503 sleep for max(Retry-After, backoff) and retry, at most MAX_ATTEMPTS tries. ``` -------------------------------- ### Submit video job with cURL Source: https://royaltechno.ai/api-docs Submits a video generation job to the RoyalTechno API using the veo-3.1 model. Requires an API key set as $RT_API_KEY. Returns a job ID immediately. ```bash curl https://api.royaltechno.cc/v1/jobs \ -H "Authorization: Bearer $RT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "veo-3.1", "input": { "prompt": "a cinematic drone shot over snowy mountains at sunrise", "aspect_ratio": "landscape", "duration_sec": 8 } }' ``` -------------------------------- ### Animate image with start_image_url using cURL Source: https://royaltechno.ai/api-docs Animates a previously generated image by passing its stable output URL as start_image_url. Works with any HTTPS URL or data URI. On Veo and Seedance 2.0 family, you can also add end_image_url for first+last frame animation. ```bash # output.url of a finished image job — public + stable, so it goes # straight into the next request. No download, no re-hosting, no base64. curl https://api.royaltechno.cc/v1/jobs \ -H "Authorization: Bearer $RT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "veo-3.1", "input": { "prompt": "the crown slowly rotates, light glinting across the glass", "start_image_url": "https://cdn.royaltechno.cc/saas/images/job_9fK2xQ.png" } }' ``` -------------------------------- ### POST /v1/jobs Source: https://royaltechno.ai/api-docs Submits a new video or image generation job to the RoyalTechno API. The same endpoint is used for video generation, image generation, and image-to-video animation. ```APIDOC ## POST /v1/jobs ### Description Submits a new video or image generation job to the RoyalTechno API. The same endpoint is used for video generation, image generation, and image-to-video animation. ### Method POST ### Endpoint https://api.royaltechno.cc/v1/jobs ### Parameters #### Request Body - **model** (string) - Required - The model to use. Supported models include `veo-3.1`, `nano-banana-pro`, and the Seedance 2.0 family. - **input** (object) - Required - The input block containing generation parameters. - **prompt** (string) - Required - The text prompt describing the desired output. - **aspect_ratio** (string) - Optional - The aspect ratio of the output (e.g., `landscape`). - **duration_sec** (integer) - Optional - The duration of the video in seconds (for video models). - **start_image_url** (string) - Optional - URL of a starting image for image-to-video animation. Any HTTPS URL or data URI works. - **end_image_url** (string) - Optional - URL of an ending image for first+last frame animation (Veo and Seedance 2.0 family). ### Request Example ```json { "model": "veo-3.1", "input": { "prompt": "a cinematic drone shot over snowy mountains at sunrise", "aspect_ratio": "landscape", "duration_sec": 8 } } ``` ### Response #### Success Response (201 Created) - **id** (string) - The job ID. - **object** (string) - Always `job`. - **model** (string) - The model used. - **status** (string) - The initial status, typically `queued`. - **input** (object) - The input block echoed back. - **cost_usd_cents** (integer) - The cost of the job in USD cents. - **created_at** (string) - The creation timestamp in ISO 8601 format. #### Response Example ```json { "id": "job_3Xk9aQ2", "object": "job", "model": "veo-3.1", "status": "queued", "input": { "prompt": "a cinematic drone shot over snowy mountains at sunrise", "aspect_ratio": "landscape", "duration_sec": 8 }, "cost_usd_cents": 2, "created_at": "2026-07-29T09:41:00Z" } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.