omni-flash-video
Generate AI video with Google's Omni 1.1 Flash (Gemini Omni Flash, audio-native video with spoken dialogue and voices) or Veo 3.1 through the useapi.net Google Flow API, on the user's own Google AI plan. Supports text-to-video, reference images of people or products, start and en
Install
npx skills add https://github.com/useapi/google-flow-api/tree/main/skills/omni-flash-video
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install useapi-google-flow-api@llmmart
git clone https://github.com/useapi/google-flow-api.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole useapi/google-flow-api collection as a plugin from our marketplace. Git is the plain clone.
README
Omni 1.1 Flash video: an agent skill
An Agent Skill that lets Claude Code, Codex and other coding agents make AI video with Google's Omni 1.1 Flash (Gemini Omni Flash, audio-native: spoken dialogue, preset voices, reference images of people and products, video-to-video edits) or Veo 3.1, and download the .mp4. Ask your agent "make a 6-second portrait clip of the woman in presenter.jpg holding up our mug and saying it's the best coffee she's had" and it runs the scripts in this folder.
It calls the Google Flow API by useapi.net, a third-party REST API that runs on your own Google Flow account. Each clip spends Flow credits from your Google AI plan.
Install
With the skills CLI (asks which agents to install for):
npx skills add useapi/google-flow-api --skill omni-flash-video
# add -g to install for your user instead of the current project
Or copy the folder yourself:
git clone https://github.com/useapi/google-flow-api.git
cp -r google-flow-api/skills/omni-flash-video ~/.claude/skills/ # Claude Code, every project
cp -r google-flow-api/skills/omni-flash-video .claude/skills/ # Claude Code, this project only
cp -r google-flow-api/skills/omni-flash-video ~/.codex/skills/ # Codex
Then give the agent your token in the environment it runs in:
export USEAPI_TOKEN=user:12345-... # your useapi.net API token
export USEAPI_EMAIL=you@gmail.com # optional: which connected Google account to use
You need:
- A useapi.net API token. One $15/month subscription covers every useapi.net API.
- A Google account with a Google AI Plus, Pro or Ultra plan, connected with the Google Flow setup. An 8-second 720p Omni clip costs 12 Flow credits, a 360p draft about half.
bash,curlandjq.
Use it without an agent
The scripts work on their own too:
./scripts/video.sh "The woman in @referenceImage_1 holds up the mug and says: 'Best coffee I've had all year.'" \
referenceImage_1=./presenter.jpg referenceAudio_1=Kore aspectRatio=portrait duration=6
| Script | What it does |
|---|---|
scripts/video.sh |
Everything below in one command: prompt and local files in, .mp4 out |
scripts/upload.sh |
Upload an image (PNG, JPEG, WebP) or video (MP4), print its asset id |
scripts/generate.sh |
Start a generation, print the job id |
scripts/wait-job.sh |
Poll the job until it is done, print the final record |
scripts/download.sh |
Download the finished .mp4 file(s) |
SKILL.md is what the agent reads: the options, credit costs, prompting rules that work with Omni, and what to do on each error. The full API reference is at useapi.net/docs/api-google-flow-v1.
Skill manifest
Omni 1.1 Flash and Veo 3.1 video with the useapi.net Google Flow API
These scripts call the useapi.net Google Flow API, a third-party REST API that drives the user's own Google Flow account. Each clip spends Flow credits from the user's Google AI plan. Every script is in scripts/ next to this file. Run them with bash.
Before you start
USEAPI_TOKENmust be set. If it is missing, ask the user for their useapi.net API token (setup: https://useapi.net/docs/start-here/setup-useapi). Their Google account must already be connected (https://useapi.net/docs/start-here/setup-google-flow). To connect one by hand, the clean sign-in scripts at https://github.com/useapi/google-account-setup give cookies that keep working. Never print the token or write it to a file.USEAPI_EMAILis optional: the connected Google account to use. Leave it unset and the API picks the healthiest account.curlandjqmust be installed.
Fastest path: one command
scripts/video.sh "<prompt>" [key=value ...]
Values that are existing local files (for referenceImage_N, startImage, endImage, referenceVideo_1) are uploaded first. It prints progress on stderr and the saved .mp4 paths on stdout (OUT_DIR sets the folder, default the current one). A clip usually takes 1 to 3 minutes; run it in the background (in Claude Code, run_in_background) or with a timeout of at least 10 minutes, and do not start a second copy while it runs.
Examples:
# Text to video, 8 s landscape at 720p (the defaults)
scripts/video.sh "A lighthouse keeper climbs the spiral stairs at dusk, lantern swinging. Slow handheld follow shot."
# A presenter from a photo, speaking with a preset voice, portrait for Shorts/Reels
scripts/video.sh "The woman in @referenceImage_1 holds up the mug, smiles and says: 'Honestly the best coffee I've had all year.' Handheld phone camera." \
referenceImage_1=./presenter.jpg referenceAudio_1=Kore aspectRatio=portrait duration=6
# Animate from a first frame to a last frame
scripts/video.sh "The paper boat drifts down the gutter stream and into the drain." startImage=./first.png endImage=./last.png
# A cheap draft: 360p costs about half; upscale the keeper later
scripts/video.sh "..." resolution=360p duration=4
Costs and limits (Omni 1.1 Flash)
| Duration | Credits at 720p | At 360p |
|---|---|---|
| 4 s | 7 | 4 |
| 6 s | 10 | 5 |
| 8 s (default) | 12 | 6 |
| 10 s | 15 | 7 |
| Video-to-video edit | 20 | 10 |
aspectRatio:landscape(default) orportrait. Ask the user which one; for phone video it isportrait, and forgetting it is the most common wasted generation.- Omni tops out at 720p.
count(1 to 4) multiplies the cost. - Veo 3.1 (
model=veo-3.1-fast,veo-3.1-quality,veo-3.1-lite) has no 360p, takes up to 3 reference images and one voice (neither onveo-3.1-quality), and also accepts 1:1, 4:3 and 3:4. - Tell the user the credit cost before generating more than one clip or anything over 8 s.
Writing prompts that work
- Put the physical action first. Models drop trailing clauses. "He blinks twice, then looks into the lens and says…" works; "…and then at the end he blinks" often does not happen.
- Spoken lines go in quotes after "says:". One or two short sentences per 8 s clip. A delivery note helps: "says, flat:", "whispers:".
- A voice needs a face.
referenceAudio_N(a preset name likeKore,Charon,Puck,Aoede, or a voice id) only works together with areferenceImage_N, acharacter_Nor a video edit. A voice on its own is rejected. Omni takes up to 5 voices for several speakers, Veo one. - Point at references in the prompt with
@referenceImage_1,@character_1,@referenceAudio_1. Each marker needs its matching parameter. - Name the camera: "handheld phone camera", "static tripod shot", "slow push-in". UGC-style ads read as real with "handheld phone camera, natural light".
- Omni takes up to 7 references (
referenceImage_*andcharacter_*together; Veo 3). More references are slower and fail more often; 1 to 3 is the sweet spot. startImage/endImagemode takes no other references and no voice.
Step by step (for more control)
- Upload local files (each prints an asset id). Without
USEAPI_EMAIL, set it to the first upload's account before the next upload, or the generation is rejected for mixing accounts;video.shdoes this for you.IMG=$(scripts/upload.sh ./presenter.jpg) - Start the generation. It prints the job id (and logs it on stderr):
JOB=$(scripts/generate.sh "The woman in @referenceImage_1 waves and says: 'Hi!'" referenceImage_1="$IMG" referenceAudio_1=Kore) - Wait for it (polls every 10 s) and save the final record:
scripts/wait-job.sh "$JOB" > job.json - Download the video(s). Prints the saved paths:
scripts/download.sh job.json ./out
All options of generate.sh are listed in its header and at https://useapi.net/docs/api-google-flow-v1/post-google-flow-videos
Timeouts and resuming
If a run is interrupted after job j... was logged, do not run it again: that spends the credits twice. Resume with the job id:
scripts/wait-job.sh "<jobid>" > job.json && scripts/download.sh job.json
The scripts never retry a generation request on their own, for the same reason.
Errors
Every failed request prints the API's error and a one-line hint. The common ones:
HTTP 400: a parameter was rejected (the message names it), or Google refused the content. For a content refusal, rephrase the prompt or change the image. Video-to-video moderation is not deterministic, so one plain resubmit can also clear it.HTTP 401: the token is wrong.HTTP 404:USEAPI_EMAILis not a connected account.HTTP 402: the account has no Plus, Pro or Ultra plan, or no Flow credits left.HTTP 403: Google rejected the captcha on every try (wait a minute, try once more), orMODEL_ACCESS_DENIED(the plan does not include the model).HTTP 429: the account is busy or out of quota.retryAftersays when to try again; or unsetUSEAPI_EMAILso the API uses another account. Do not loop on it.HTTP 503: Google is temporarily overloaded. Try again in a minute or two, with fewer references or a lowercount.HTTP 596: the Google account needs reconnecting: https://useapi.net/docs/start-here/setup-google-flow
After it finishes
Tell the user the file path(s), the duration and the credits it used. The video links in job.json stay valid for about 6 hours. The clip is also in the user's Google Flow project.
Files (google-flow-api)
-
scripts
-
download.sh 714 B
#!/usr/bin/env bash # Download every video of a finished job and print the saved paths. # Usage: download.sh <job.json|-> [OUT_DIR] # The video links are signed Google URLs that stay valid for about 6 hours. source "$(dirname "$0")/_common.sh" JOB=$(cat "${1:--}") OUT="${2:-.}" mkdir -p "$OUT" SHORT=$(jq -r '(.jobid // "") | (capture("^j(?<id>[0-9]+)").id // "")' <<< "$JOB") NAME="omni-flash${SHORT:+_$SHORT}" N=0 for URL in $(jq -r '.response.media[]?.videoUrl // empty' <<< "$JOB"); do N=$((N + 1)) FILE="$OUT/${NAME}_$N.mp4" curl -sS --fail --max-time 600 -o "$FILE" "$URL" echo "$FILE" done [ "$N" -gt 0 ] || { echo "The job has no video URLs: $(jq -c '{status, error}' <<< "$JOB")" >&2; exit 1; } -
generate.sh 1.6 KB
#!/usr/bin/env bash # Start one video generation and print its job id. It does not wait; use wait-job.sh. # Usage: generate.sh "<prompt>" [key=value ...] # model=omni-flash (default) | veo-3.1-fast | veo-3.1-quality | veo-3.1-lite # aspectRatio=landscape (default) | portrait duration=4|6|8|10 (default 8) # resolution=720p (default) | 360p (omni-flash only, about half the credits) # count=1..4 seed=<int> referenceImage_1..7=<asset id> startImage=<asset id> endImage=<asset id> # character_1..7=<character id> referenceAudio_1..5=<voice name or voice id> # referenceVideo_1=<asset id> startFrameIndex_1=<0-239> endFrameIndex_1=<1-240> # Every option: https://useapi.net/docs/api-google-flow-v1/post-google-flow-videos # This spends Flow credits. It never retries by itself: a retry after an unclear failure could pay twice. source "$(dirname "$0")/_common.sh" PROMPT="${1:?Usage: generate.sh \"<prompt>\" [key=value ...]}" shift BODY=$(jq -n --arg p "$PROMPT" '{prompt: $p, model: "omni-flash", async: true}') if [ -n "${USEAPI_EMAIL:-}" ]; then BODY=$(jq --arg e "$USEAPI_EMAIL" '.email = $e' <<< "$BODY"); fi for KV in "$@"; do case "$KV" in *=*) ;; *) echo "Expected key=value, got: $KV" >&2; exit 1 ;; esac K="${KV%%=*}" V="${KV#*=}" case "$K" in duration|count|seed|startFrameIndex_1|endFrameIndex_1) BODY=$(jq --arg k "$K" --argjson v "$V" '.[$k] = $v' <<< "$BODY") ;; *) BODY=$(jq --arg k "$K" --arg v "$V" '.[$k] = $v' <<< "$BODY") ;; esac done JOB=$(api POST videos "$BODY") ID=$(jq -r '.jobid // empty' <<< "$JOB") [ -n "$ID" ] || { echo "No job id in the answer: $JOB" >&2; exit 1; } log "job $ID" echo "$ID" -
upload.sh 1.2 KB
#!/usr/bin/env bash # Upload a local image (PNG, JPEG, WebP) or video (MP4) and print its asset id (mediaGenerationId). # Usage: upload.sh <file> # The asset belongs to one Google account: USEAPI_EMAIL if set, otherwise the one the API picks. # Use the id as referenceImage_N, startImage, endImage or referenceVideo_1 in generate.sh. source "$(dirname "$0")/_common.sh" FILE="${1:?Usage: upload.sh <file>}" [ -f "$FILE" ] || { echo "No such file: $FILE" >&2; exit 1; } EXT=$(tr '[:upper:]' '[:lower:]' <<< "${FILE##*.}") case "$EXT" in png) TYPE=image/png ;; jpg|jpeg) TYPE=image/jpeg ;; webp) TYPE=image/webp ;; mp4) TYPE=video/mp4 ;; *) echo "Unsupported file type .$EXT (use png, jpg, jpeg, webp or mp4)" >&2; exit 1 ;; esac P="assets" if [ -n "${USEAPI_EMAIL:-}" ]; then P="assets/$(enc "$USEAPI_EMAIL")"; fi OUT=$(mktemp) CODE=$(acurl -sS --max-time 320 -o "$OUT" -w '%{http_code}' -X POST -H "Content-Type: $TYPE" --data-binary "@$FILE" "$API/$P") || true CODE="${CODE:-000}" BODY=$(_finish POST "$P") ID=$(jq -r '.mediaGenerationId.mediaGenerationId // empty' <<< "$BODY") [ -n "$ID" ] || { echo "Upload answered without an asset id: $BODY" >&2; exit 1; } log "uploaded $FILE" echo "$ID" -
video.sh 1.4 KB
#!/usr/bin/env bash # Everything in one command: upload local files, generate, wait, download. Prints the saved .mp4 paths. # Usage: video.sh "<prompt>" [key=value ...] # Any value that is an existing local file (for referenceImage_N, startImage, endImage or # referenceVideo_1) is uploaded first and replaced by its asset id. Other options as in generate.sh. # Example: video.sh "The woman in @referenceImage_1 holds up the mug and says: 'Best coffee I've had.'" \ # referenceImage_1=./presenter.jpg referenceAudio_1=Kore aspectRatio=portrait duration=6 # OUT_DIR sets the folder (default: the current one). If the run is interrupted after the job id was # logged, resume with: wait-job.sh <jobid> > job.json && download.sh job.json DIR="$(dirname "$0")" source "$DIR/_common.sh" PROMPT="${1:?Usage: video.sh \"<prompt>\" [key=value ...]}" shift ARGS=() for KV in "$@"; do K="${KV%%=*}" V="${KV#*=}" if [ "$K" != "$KV" ] && [ -f "$V" ]; then ID=$("$DIR/upload.sh" "$V") # Keep every upload on the same Google account as the first one, or the generation is rejected if [ -z "${USEAPI_EMAIL:-}" ]; then USEAPI_EMAIL=$(asset_email "$ID") export USEAPI_EMAIL fi KV="$K=$ID" fi ARGS+=("$KV") done JOBID=$("$DIR/generate.sh" "$PROMPT" ${ARGS[@]+"${ARGS[@]}"}) JOB=$("$DIR/wait-job.sh" "$JOBID") "$DIR/download.sh" - "${OUT_DIR:-.}" <<< "$JOB" -
wait-job.sh 846 B
#!/usr/bin/env bash # Poll a job every 10 seconds until it is completed or failed, then print the final job record (JSON). # Usage: wait-job.sh <jobid> > job.json # An Omni Flash clip usually takes 1 to 3 minutes. Exits 1 if the job failed. # A network error or HTTP 5xx is retried up to 3 times. Safe to re-run on the same job id to resume. source "$(dirname "$0")/_common.sh" JOBID="${1:?Usage: wait-job.sh <jobid>}" START=$(date +%s) while true; do JOB=$(api_poll GET "jobs/$JOBID") STATUS=$(jq -r .status <<< "$JOB") if [ "$STATUS" = completed ] || [ "$STATUS" = failed ]; then break; fi log "job $STATUS ($(( $(date +%s) - START )) s)" sleep 10 done echo "$JOB" log "job $STATUS after $(( $(date +%s) - START )) s" [ "$STATUS" = completed ] || { echo "Job failed: $(jq -c '{error, errorDetails, code}' <<< "$JOB")" >&2; exit 1; } -
_common.sh 4.7 KB
# Shared helpers for the omni-flash-video skill scripts. Sourced, not run. # Needs: curl, jq. Env: USEAPI_TOKEN (required), USEAPI_EMAIL (optional, which connected Google Flow account to use). # Written for bash 3.2 and later (the macOS default), so no bash 4 features. set -euo pipefail API="${USEAPI_API:-https://api.useapi.net/v1/google-flow}" : "${USEAPI_TOKEN:?Set USEAPI_TOKEN to your useapi.net API token (https://useapi.net/docs/start-here/setup-useapi)}" command -v jq > /dev/null || { echo "jq is required (https://jqlang.org/download/)" >&2; exit 1; } enc() { jq -rn --arg v "$1" '$v|@uri'; } log() { echo "$(date +%H:%M:%S) $*" >&2; } # acurl [curl args ...] -> curl with the Authorization header read from stdin (-H @-), # so the token does not show up in the process list acurl() { curl "$@" -H @- <<< "Authorization: Bearer $USEAPI_TOKEN"; } # _call METHOD PATH [JSON_BODY] -> sets CODE (the HTTP status, 000 on a network error) and OUT (a temp file with the body) _call() { local method="$1" path="$2" body="${3:-}" OUT=$(mktemp) if [ -n "$body" ]; then CODE=$(acurl -sS --max-time 320 -o "$OUT" -w '%{http_code}' -H "Content-Type: application/json" -X "$method" "$API/$path" -d "$body") || true else CODE=$(acurl -sS --max-time 320 -o "$OUT" -w '%{http_code}' -X "$method" "$API/$path") || true fi CODE="${CODE:-000}" } # hint CODE BODY_FILE -> one line on what to do about a failed request (stderr) hint() { local code="$1" body body=$(cat "$2" 2>/dev/null || true) case "$code" in 400) if grep -q 'PAYGATE\|paid Google Flow plan' <<< "$body"; then echo "Hint: this Google account has no paid Google AI plan, which this model needs. Connect an account with a paid plan, or set USEAPI_EMAIL to one." >&2 elif grep -q 'SAFETY\|safety\|policy' <<< "$body"; then echo "Hint: Google refused the content. Rephrase the prompt or change the reference images. Video-to-video moderation is not deterministic, so one plain resubmit can also clear it." >&2 else echo "Hint: a parameter was rejected. The error names it; see https://useapi.net/docs/api-google-flow-v1/post-google-flow-videos" >&2 fi ;; 401) echo "Hint: the API token is wrong or missing. Check USEAPI_TOKEN." >&2 ;; 402) echo "Hint: the Google account has no active Google AI plan or is out of Flow credits. Omni Flash needs Plus, Pro or Ultra." >&2 ;; 403) if grep -q 'MODEL_ACCESS_DENIED' <<< "$body"; then echo "Hint: Google refused this model on this account. Check that the account's plan includes it." >&2 else echo "Hint: Google rejected the captcha on every try. Wait a minute and try once more; if it keeps happening, see https://useapi.net/docs/api-google-flow-v1/get-google-flow-accounts-captcha-stats" >&2 fi ;; 404) echo "Hint: that Google Flow account is not connected. Check USEAPI_EMAIL, or see https://useapi.net/docs/start-here/setup-google-flow" >&2 ;; 429) echo "Hint: the account is busy or out of quota. retryAfter in the error says when to try again; or leave USEAPI_EMAIL unset so the API picks another account. Do not loop on it." >&2 ;; 503) echo "Hint: Google is temporarily unavailable or overloaded. Try again in a minute or two (fewer reference images or a lower count also help)." >&2 ;; 596) echo "Hint: the Google account needs to be reconnected: https://useapi.net/docs/start-here/setup-google-flow" >&2 ;; esac } # _finish METHOD PATH -> prints the body of a 2xx answer; anything else prints the error, a hint, and exits 1 _finish() { if [ "${CODE:0:1}" != "2" ]; then echo "$1 /$2 -> HTTP $CODE: $(cat "$OUT")" >&2 hint "$CODE" "$OUT" rm -f "$OUT" exit 1 fi cat "$OUT" rm -f "$OUT" } # api METHOD PATH [JSON_BODY] -> response body on stdout; a non-2xx answer prints the error and exits 1 api() { _call "$@" _finish "$1" "$2" } # api_poll METHOD PATH -> like api, for the polling loop: a network error or an HTTP 5xx is retried # up to 3 times, 10 s apart (except 596, which means the Google account needs reconnecting) api_poll() { local try=1 while true; do _call "$@" case "$CODE" in 000|5??) if [ "$CODE" != 596 ] && [ "$try" -le 3 ]; then log "$1 /$2 -> HTTP $CODE, retry $try of 3 in 10 s" rm -f "$OUT" try=$((try + 1)) sleep 10 continue fi ;; esac break done _finish "$1" "$2" } # asset_email MEDIA_GENERATION_ID -> the Google account an uploaded asset belongs to # (the id embeds it hex-encoded: user:123-email:<hex>-image:...) asset_email() { local hex hex=$(sed -n 's/.*-email:\([0-9a-fA-F]*\)-.*/\1/p' <<< "$1") [ -n "$hex" ] || return 0 printf "$(sed 's/../\\x&/g' <<< "$hex")" }
-
-
README.md 3.4 KB
# Omni 1.1 Flash video: an agent skill An [Agent Skill](https://agentskills.io) that lets Claude Code, Codex and other coding agents make AI video with Google's **Omni 1.1 Flash** (Gemini Omni Flash, audio-native: spoken dialogue, preset voices, reference images of people and products, video-to-video edits) or **Veo 3.1**, and download the `.mp4`. Ask your agent "make a 6-second portrait clip of the woman in presenter.jpg holding up our mug and saying it's the best coffee she's had" and it runs the scripts in this folder. It calls the [Google Flow API](https://useapi.net/docs/api-google-flow-v1?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill) by [useapi.net](https://useapi.net/?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill), a third-party REST API that runs on your own Google Flow account. Each clip spends Flow credits from your Google AI plan. ## Install With the [skills CLI](https://github.com/vercel-labs/skills) (asks which agents to install for): ```bash npx skills add useapi/google-flow-api --skill omni-flash-video # add -g to install for your user instead of the current project ``` Or copy the folder yourself: ```bash git clone https://github.com/useapi/google-flow-api.git cp -r google-flow-api/skills/omni-flash-video ~/.claude/skills/ # Claude Code, every project cp -r google-flow-api/skills/omni-flash-video .claude/skills/ # Claude Code, this project only cp -r google-flow-api/skills/omni-flash-video ~/.codex/skills/ # Codex ``` Then give the agent your token in the environment it runs in: ```bash export USEAPI_TOKEN=user:12345-... # your useapi.net API token export USEAPI_EMAIL=you@gmail.com # optional: which connected Google account to use ``` You need: 1. A useapi.net [API token](https://useapi.net/docs/start-here/setup-useapi?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill). One [$15/month subscription](https://useapi.net/docs/subscription?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill) covers every useapi.net API. 2. A Google account with a Google AI Plus, Pro or Ultra plan, connected with the [Google Flow setup](https://useapi.net/docs/start-here/setup-google-flow?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill). An 8-second 720p Omni clip costs 12 Flow credits, a 360p draft about half. 3. `bash`, `curl` and [`jq`](https://jqlang.org/download/). ## Use it without an agent The scripts work on their own too: ```bash ./scripts/video.sh "The woman in @referenceImage_1 holds up the mug and says: 'Best coffee I've had all year.'" \ referenceImage_1=./presenter.jpg referenceAudio_1=Kore aspectRatio=portrait duration=6 ``` | Script | What it does | |---|---| | `scripts/video.sh` | Everything below in one command: prompt and local files in, `.mp4` out | | `scripts/upload.sh` | Upload an image (PNG, JPEG, WebP) or video (MP4), print its asset id | | `scripts/generate.sh` | Start a generation, print the job id | | `scripts/wait-job.sh` | Poll the job until it is done, print the final record | | `scripts/download.sh` | Download the finished `.mp4` file(s) | [`SKILL.md`](./SKILL.md) is what the agent reads: the options, credit costs, prompting rules that work with Omni, and what to do on each error. The full API reference is at [useapi.net/docs/api-google-flow-v1](https://useapi.net/docs/api-google-flow-v1/post-google-flow-videos?utm_source=github.com&utm_medium=skill&utm_campaign=omni-flash-skill). -
SKILL.md 7.5 KB
--- name: omni-flash-video description: Generate AI video with Google's Omni 1.1 Flash (Gemini Omni Flash, audio-native video with spoken dialogue and voices) or Veo 3.1 through the useapi.net Google Flow API, on the user's own Google AI plan. Supports text-to-video, reference images of people or products, start and end frames, preset voices and video-to-video edits, and downloads the .mp4. Use when the user asks to make a video, clip, ad, talking-head, UGC or product video with Omni Flash, Gemini Omni, Veo 3.1 or Google Flow, or to automate Google Flow video from the command line. Needs curl, jq and a USEAPI_TOKEN environment variable. license: MIT compatibility: Needs bash, curl, jq and network access to api.useapi.net. Requires a useapi.net API token (USEAPI_TOKEN) and a Google account with a Google AI Plus, Pro or Ultra plan connected to useapi.net. metadata: author: useapi.net homepage: https://github.com/useapi/google-flow-api --- # Omni 1.1 Flash and Veo 3.1 video with the useapi.net Google Flow API These scripts call the [useapi.net Google Flow API](https://useapi.net/docs/api-google-flow-v1), a third-party REST API that drives the user's own Google Flow account. Each clip spends Flow credits from the user's Google AI plan. Every script is in `scripts/` next to this file. Run them with bash. ## Before you start 1. `USEAPI_TOKEN` must be set. If it is missing, ask the user for their useapi.net API token (setup: https://useapi.net/docs/start-here/setup-useapi). Their Google account must already be connected (https://useapi.net/docs/start-here/setup-google-flow). To connect one by hand, the clean sign-in scripts at https://github.com/useapi/google-account-setup give cookies that keep working. Never print the token or write it to a file. 2. `USEAPI_EMAIL` is optional: the connected Google account to use. Leave it unset and the API picks the healthiest account. 3. `curl` and `jq` must be installed. ## Fastest path: one command ```bash scripts/video.sh "<prompt>" [key=value ...] ``` Values that are existing local files (for `referenceImage_N`, `startImage`, `endImage`, `referenceVideo_1`) are uploaded first. It prints progress on stderr and the saved `.mp4` paths on stdout (`OUT_DIR` sets the folder, default the current one). A clip usually takes 1 to 3 minutes; run it in the background (in Claude Code, `run_in_background`) or with a timeout of at least 10 minutes, and do not start a second copy while it runs. Examples: ```bash # Text to video, 8 s landscape at 720p (the defaults) scripts/video.sh "A lighthouse keeper climbs the spiral stairs at dusk, lantern swinging. Slow handheld follow shot." # A presenter from a photo, speaking with a preset voice, portrait for Shorts/Reels scripts/video.sh "The woman in @referenceImage_1 holds up the mug, smiles and says: 'Honestly the best coffee I've had all year.' Handheld phone camera." \ referenceImage_1=./presenter.jpg referenceAudio_1=Kore aspectRatio=portrait duration=6 # Animate from a first frame to a last frame scripts/video.sh "The paper boat drifts down the gutter stream and into the drain." startImage=./first.png endImage=./last.png # A cheap draft: 360p costs about half; upscale the keeper later scripts/video.sh "..." resolution=360p duration=4 ``` ## Costs and limits (Omni 1.1 Flash) | Duration | Credits at 720p | At 360p | |---|---|---| | 4 s | 7 | 4 | | 6 s | 10 | 5 | | 8 s (default) | 12 | 6 | | 10 s | 15 | 7 | | Video-to-video edit | 20 | 10 | - `aspectRatio`: `landscape` (default) or `portrait`. Ask the user which one; for phone video it is `portrait`, and forgetting it is the most common wasted generation. - Omni tops out at 720p. `count` (1 to 4) multiplies the cost. - Veo 3.1 (`model=veo-3.1-fast`, `veo-3.1-quality`, `veo-3.1-lite`) has no 360p, takes up to 3 reference images and one voice (neither on `veo-3.1-quality`), and also accepts 1:1, 4:3 and 3:4. - Tell the user the credit cost before generating more than one clip or anything over 8 s. ## Writing prompts that work - **Put the physical action first.** Models drop trailing clauses. "He blinks twice, then looks into the lens and says…" works; "…and then at the end he blinks" often does not happen. - **Spoken lines go in quotes** after "says:". One or two short sentences per 8 s clip. A delivery note helps: "says, flat:", "whispers:". - **A voice needs a face.** `referenceAudio_N` (a preset name like `Kore`, `Charon`, `Puck`, `Aoede`, or a voice id) only works together with a `referenceImage_N`, a `character_N` or a video edit. A voice on its own is rejected. Omni takes up to 5 voices for several speakers, Veo one. - **Point at references in the prompt** with `@referenceImage_1`, `@character_1`, `@referenceAudio_1`. Each marker needs its matching parameter. - **Name the camera**: "handheld phone camera", "static tripod shot", "slow push-in". UGC-style ads read as real with "handheld phone camera, natural light". - **Omni takes up to 7 references** (`referenceImage_*` and `character_*` together; Veo 3). More references are slower and fail more often; 1 to 3 is the sweet spot. - `startImage`/`endImage` mode takes no other references and no voice. ## Step by step (for more control) 1. Upload local files (each prints an asset id). Without `USEAPI_EMAIL`, set it to the first upload's account before the next upload, or the generation is rejected for mixing accounts; `video.sh` does this for you. ```bash IMG=$(scripts/upload.sh ./presenter.jpg) ``` 2. Start the generation. It prints the job id (and logs it on stderr): ```bash JOB=$(scripts/generate.sh "The woman in @referenceImage_1 waves and says: 'Hi!'" referenceImage_1="$IMG" referenceAudio_1=Kore) ``` 3. Wait for it (polls every 10 s) and save the final record: ```bash scripts/wait-job.sh "$JOB" > job.json ``` 4. Download the video(s). Prints the saved paths: ```bash scripts/download.sh job.json ./out ``` All options of `generate.sh` are listed in its header and at https://useapi.net/docs/api-google-flow-v1/post-google-flow-videos ## Timeouts and resuming If a run is interrupted after `job j...` was logged, do not run it again: that spends the credits twice. Resume with the job id: ```bash scripts/wait-job.sh "<jobid>" > job.json && scripts/download.sh job.json ``` The scripts never retry a generation request on their own, for the same reason. ## Errors Every failed request prints the API's error and a one-line hint. The common ones: - `HTTP 400`: a parameter was rejected (the message names it), or Google refused the content. For a content refusal, rephrase the prompt or change the image. Video-to-video moderation is not deterministic, so one plain resubmit can also clear it. - `HTTP 401`: the token is wrong. `HTTP 404`: `USEAPI_EMAIL` is not a connected account. - `HTTP 402`: the account has no Plus, Pro or Ultra plan, or no Flow credits left. - `HTTP 403`: Google rejected the captcha on every try (wait a minute, try once more), or `MODEL_ACCESS_DENIED` (the plan does not include the model). - `HTTP 429`: the account is busy or out of quota. `retryAfter` says when to try again; or unset `USEAPI_EMAIL` so the API uses another account. Do not loop on it. - `HTTP 503`: Google is temporarily overloaded. Try again in a minute or two, with fewer references or a lower `count`. - `HTTP 596`: the Google account needs reconnecting: https://useapi.net/docs/start-here/setup-google-flow ## After it finishes Tell the user the file path(s), the duration and the credits it used. The video links in `job.json` stay valid for about 6 hours. The clip is also in the user's Google Flow project.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.