Python Tutorial: Chat Completions and Async Image Jobs with API Crest
Disclosure: I work on API Crest. This walkthrough is based on our published API documentation. It is a getting-started guide, not a claim that I've benchmarked the service or executed the example in this article.
If you're building a small AI app, you may need two very different patterns: a quick request for a text response, and an asynchronous job for image generation. This tutorial shows both patterns using Python, and explains where people often run into trouble.
1. Get an API key and install dependencies
Create an API key from the API Crest developer console, then review the model catalog and pricing before running requests. These requests may consume paid credits.
Install Python dependencies:
python -m pip install anthropic requests
Keep the API key out of source code. Set it as an environment variable in your own shell:
export APICREST_API_KEY="YOUR_API_KEY"
On Windows PowerShell, use $env:APICREST_API_KEY = "YOUR_API_KEY" for the current session.
2. Send a chat message with the Anthropic Python SDK
The API Crest documentation documents the Anthropic-style POST /v1/messages route at https://api.vv666.ai and lists claude-haiku-5-5 as a model ID.
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["APICREST_API_KEY"],
base_url="https://api.vv666.ai",
)
response = client.messages.create(
model="claude-haiku-5-5",
max_tokens=256,
messages=[
{"role": "user", "content": "Explain idempotency in two sentences."}
],
)
for block in response.content:
if getattr(block, "type", None) == "text":
print(block.text)
The model ID is the provider's documented identifier, not a universal model name. Use the API Crest catalog to confirm current model availability and rates.
The equivalent raw HTTP shape uses a POST /v1/messages request, an API key in an accepted auth header, and JSON fields such as model, messages, and max_tokens.
3. Submit an image generation task and poll for completion
Unlike the chat request above, API Crest's media API documents an asynchronous flow:
-
POST /v1/media/generatewith a model and prompt. - Read the returned
task_id. -
GET /v1/media/status?task_id=...untilis_finalis true. - Read
result_urlfor a successful job, or handle an error state.
Here's a minimal example built around the documented gpt-image-2.5 model and media routes:
import os
import time
import requests
BASE = "https://api.vv666.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['APICREST_API_KEY']}"}
created = requests.post(
f"{BASE}/v1/media/generate",
headers=HEADERS,
json={
"model": "gpt-image-2.5",
"prompt": "A tiny robot watering a bonsai tree, soft studio lighting",
"params": {"aspect_ratio": "1:1", "quality": "auto"},
},
timeout=30,
)
created.raise_for_status()
task_id = created.json()["task_id"]
for _ in range(20):
check = requests.get(
f"{BASE}/v1/media/status",
headers=HEADERS,
params={"task_id": task_id},
timeout=30,
)
check.raise_for_status()
job = check.json()
if job.get("is_final"):
if job.get("state") == "success":
print("Result:", job.get("result_url"))
break
raise RuntimeError(f"Generation failed: {job.get('error', job.get('state'))}")
time.sleep(5)
else:
print("Job is still pending; keep the task_id and check later.")
A five-second polling interval is only an illustration, not a documented rate-limit recommendation. For production, use sensible retry/backoff logic, persist your task_id, and avoid endlessly polling a failed job.
4. Practical production checklist
Before connecting any AI API to production, I recommend:
- Keep keys on the server: never expose them in a public frontend bundle.
- Avoid retrying a creation request blindly: if the first response is lost after the job is accepted, a duplicate submission could incur extra cost.
- Log the task ID and final status: especially important for images, video, and other long-running jobs.
- Budget requests and monitor consumption: model prices and billing units differ.
- Handle 403/429/5xx responses: including permission problems, throttling, and transient failures.
- Choose the protocol per model: don't assume all model routes share one request schema.
Documentation and feedback
If you're integrating chat and media generation in the same app, what causes you more trouble: keeping usage costs predictable, handling async job states, or normalizing provider APIs?
I'd be interested to hear what you'd add to this checklist.
Top comments (0)