HTTP-first Python integration
Keep APPSCREENSHOTAPI_KEY in the environment, send an idempotency key per release, and let the JSON response drive downstream preview or upload steps. The examples below are ready for a normal Python automation script.
SDKs · Python
Use requests to call the API directly from release scripts, coding agents, or CI jobs. The request and response bodies match the OpenAPI contract exactly.
Keep APPSCREENSHOTAPI_KEY in the environment, send an idempotency key per release, and let the JSON response drive downstream preview or upload steps. The examples below are ready for a normal Python automation script.
One call renders the whole set. Authenticate with a live key (asa_live_…) from api.appscreenshotapi.com/register, post the set to POST /v1/renders, and read images[].url off the JSON response. Keep the key in an environment variable, never in source.
# Works today against the live API - no SDK required.
# pip install requests
import os
import requests
API_KEY = os.environ["APPSCREENSHOTAPI_KEY"] # asa_live_...
resp = requests.post(
"https://api.appscreenshotapi.com/v1/renders",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"Idempotency-Key": "release-42-en-us",
},
json={
"preset": "aurora-midnight",
"canvas": {"preset": "appstore.iphone_6_9"},
"theme": {
"accentColor": "#5EEAD4",
"device": {"frame": {"style": "clay"}, "shadow": {"elevation": "medium"}},
},
"slides": [
{
"role": "hook",
"headline": {"lines": ["Track every habit", "in one tap"], "accent": "one tap"},
"screenshot": {"url": "https://acme.app/shots/home.png"},
},
{
"role": "feature",
"headline": {"lines": ["See your streaks build"]},
"subtitle": "Daily, weekly, and monthly views.",
"screenshot": {"url": "https://acme.app/shots/streaks.png"},
"tilt": "left",
},
{
"role": "proof",
"headline": {"lines": ["Loved by 40,000 builders"]},
"screenshot": {"url": "https://acme.app/shots/stats.png"},
"overlays": [{"kind": "stars", "rating": 4.9, "count": "12,400 reviews"}],
},
],
},
timeout=120,
)
resp.raise_for_status()
render = resp.json()
# images[] are CDN URLs (never base64), retained for 30 days.
for image in render["images"]:
print(image["url"], f"{image['width']}x{image['height']}")
# The lint report ships in every response - read it and self-correct.
failures = [f for f in render["lint"] if f["status"] == "fail"]
if failures:
print("Store lint failures:", failures)The response also carries the machine-readable lint report and credits_used. Send an Idempotency-Key so retries replay the original render instead of billing twice.
Small sets render synchronously in one round trip. For large locale or store matrices, send "async": True. The API responds with 202 and a render id, and you poll GET /v1/renders/{id} until the status is succeeded or failed.
# Large locale or store matrices: send async and poll the render id.
import os
import time
import requests
API_KEY = os.environ["APPSCREENSHOTAPI_KEY"]
BASE = "https://api.appscreenshotapi.com"
auth = {"Authorization": f"Bearer {API_KEY}"}
# 1. Kick off an async render - returns 202 with a render id.
accepted = requests.post(
f"{BASE}/v1/renders",
headers={**auth, "Content-Type": "application/json"},
json={
"async": True,
"preset": "aurora-midnight",
"canvas": {"preset": "appstore.iphone_6_9"},
"output": {"locales": ["en-US", "de-DE", "ja-JP"], "stores": ["appstore", "play"]},
"slides": [
{"role": "hook", "headline": {"lines": ["Track every habit"]},
"screenshot": {"url": "https://acme.app/shots/home.png"}},
],
},
timeout=30,
).json()
# 2. Poll GET /v1/renders/{id} until the status is terminal.
def wait_for_render(render_id):
while True:
render = requests.get(f"{BASE}/v1/renders/{render_id}", headers=auth, timeout=30).json()
if render["status"] == "succeeded":
return render
if render["status"] == "failed":
raise RuntimeError(render.get("error", {}).get("message", "render failed"))
time.sleep(2)
render = wait_for_render(accepted["id"])
print([image["url"] for image in render["images"]])For inline build steps, poll the render id as shown above. If your workflow uses callbacks, pass webhook_url and verify the signed event before acting on it.
Set output.format to fastlane_zip and the response adds an archive.url: a deliver-ready ZIP laid out in locale folders. This is live today and pairs well with the CI/CD workflow.
# Ask for a Fastlane-ready archive alongside the images.
# Set output.format to fastlane_zip and read archive.url from the response.
import os
import requests
render = requests.post(
"https://api.appscreenshotapi.com/v1/renders",
headers={
"Authorization": f"Bearer {os.environ['APPSCREENSHOTAPI_KEY']}",
"Content-Type": "application/json",
},
json={
"preset": "aurora-midnight",
"canvas": {"preset": "appstore.iphone_6_9"},
"output": {"locales": ["en-US", "de-DE"], "format": "fastlane_zip"},
"slides": [
{"role": "hook", "headline": {"lines": ["Track every habit"]},
"screenshot": {"url": "https://acme.app/shots/home.png"}},
],
},
timeout=120,
).json()
# archive.url is a deliver-ready ZIP arranged in locale folders.
print(render["archive"]["url"], render["archive"]["bytes"], "bytes")The full request cascade (preset, theme, slides), every style enum, canvas presets, and the lint rules live in the API reference. For the flat list of every accepted value, see the options reference. The same endpoints power the Node.js quickstart and the CI/CD pipeline examples.