https://emaki.ai. Every call is a POST with Authorization: Bearer <key> (create a key).
Submit a run
Answer:
200 {"success": true, "data": {"id": "<videoId>", "status": "generating", ...}}. Keep data.id.
The same endpoint makes images: pass an image model id such as nano_banana_2_lite.
Poll it
200 {"success": true, "data": {"id": "...", "status": "generating" | "completed" | "failed", "videoUrl": "...", "failureCode": ...}}.
Poll every 5 seconds. On completed the result URL is in videoUrl (images too). On failed, failureCode says why. Video can wait in a queue first; the SDKs give up after 15 minutes by default.
Credit balance (free)
{"code": 0, "data": {"remainingCredits": 120, "hasPurchasedCredits": true}}. A refusal is HTTP 200 with {"code": -1, "message": ...}, so read code, not the status line.
Errors from the submit call
The body is{"error": "...", "code"?: "..."}.