polli — Pollinations CLI
Thin wrapper around gen.pollinations.ai. Generates images, text, audio, video; transcribes speech; manages API keys, usage, quests, agents, and invite-only my-models.
If polli is not installed, run npm i -g @pollinations/cli@latest (provides the polli binary).
When to use this skill
- User asks to generate an image / text / audio / video via pollinations
- User mentions polli, pollinations, pollen, pollinations.ai
- User wants to transcribe speech or run TTS
- User asks about their pollen balance, usage, or API keys
- User wants to browse or filter available models
- User wants to inspect quests or manage invite-only my-models
- User wants to create or update a hosted prompt agent
For agent-assisted login, use polli --json auth login --no-browser. Read stderr while the command runs and show the user the verification URL and user code so they can review access. On approval, stdout contains one JSON result: {"authenticated":true}. Tokens are stored locally and never included in that result.
Quick reference
| Intent | Command |
|---|---|
| Log in once | polli auth login |
| Agent-assisted login | polli --json auth login --no-browser |
| Store an existing key | printf '%s' "$POLLINATIONS_API_KEY" \| polli auth login --with-token |
| Generate image | polli gen image "<prompt>" --output out.png |
| Generate text | polli gen text "<prompt>" |
| Text with stdin as context | echo "<ctx>" \| polli gen text "<question>" |
| Describe an image (vision) | polli gen text "what is this?" --image <url> |
| One-shot TTS | polli gen audio "<text>" --output speech.mp3 |
| Speak out loud | polli gen audio "<text>" --play (uses afplay on macOS; ffplay/mpv/mpg123 on Linux) |
| Generate video | polli gen video "<prompt>" --output out.mp4 |
| Generate a 3D model | polli gen 3d "<prompt>" --output out.glb (or --image <url> for image-only models) |
| Embed text | polli gen embeddings "<text>" ["<text2>" ...] (one vector per line; also reads stdin) |
| Change a voice | polli gen voice-change talk.mp3 --voice nova |
| Isolate speech | polli gen isolate interview.mp4 (strips music/noise) |
| Speech + character timings | polli gen audio "<text>" --timestamps (also writes speech.mp3.json) |
| Transcribe audio | polli gen transcribe path/to.mp3 |
| Upload a local file | polli upload path/to.png (prints public URL) |
| List all models | polli models |
| Filter models by type | polli models --type image (text, image, audio, video, 3d, embedding) |
| Model health + latency | polli models --stats (default 60m, --window <min>) |
| Check balance | polli usage |
| Developer earnings | polli earnings (--days <n>) |
| List your quests + claim state | polli quests (filters: --open --claimable --claimed --coming-soon) |
| Manage prompt agents | polli agents list |
| Manage invite-only community models | polli my-models list |
| Update the CLI | polli update (global installs only; npx/local get instructions) |
| Connect a coding harness to Pollinations | polli harness <bloom\|dsh\|hermes\|opencode\|openclaw\|pi\|prime\|tgpt> on (available adapters: polli harness --help) |
| Start a Linux sandbox (alpha) | polli sandbox create (--timeout <seconds> to keep it running without ssh, 0 until you pause or kill it; pollinations-large as the template for 4 vCPU and 8 GB; then list, logs <id>, timeout <id> <seconds>, pause <id>, kill <id>); the default template comes logged in, with the coding harnesses |
| ssh / scp / rsync into a sandbox | ssh <sandbox-id>.polli |
| Machine-readable output | append --json to any command |
Setup
One-time: polli auth login (device-flow; creates a key with profile, usage, and keys). To store an existing key, run
printf '%s' "$POLLINATIONS_API_KEY" | polli auth login --with-token. Verify
with polli auth status (or polli whoami).
Override the stored key for a single command with --key <key>.
Prefix any command with POLLINATIONS_ENV=staging to use staging; it keeps its own login, so run POLLINATIONS_ENV=staging polli auth login once.
Recipes
Generate an image to a file
polli gen image "a fox reading a book, studio ghibli style" --output fox.png
Defaults: tongyi-mai/z-image-turbo, 1024x1024. Without --output the file is image.<ext>, with the extension inferred from the response's Content-Type (tongyi-mai/z-image-turbo and black-forest-labs/flux.1-schnell return JPEG, so image.jpg); an explicit --output is used as given. Pick a different model with --model black-forest-labs/flux.1-schnell (see polli models --type image). For edits / img2img, pass one or more --image <url> flags — must be public http(s) URLs, local paths are rejected client-side. Only models that list "image" in input_modalities actually consume the flag — black-forest-labs/flux.1-schnell and tongyi-mai/z-image-turbo are text-only and will silently ignore --image. Find i2i-capable models with polli models --type image --json | jq -r '.[] | select(.input_modalities | contains(["image"])) | .name' (common choices: google/gemini-2.5-flash-image, black-forest-labs/flux.1-kontext-pro, prunaai/p-image-edit). To use a local file, upload it first with polli upload (see next recipe). Img2img models may treat --width/--height as a hint and keep the source aspect — check the output with file/ffprobe; if the aspect matters, pre-crop the source or crop afterwards.
Upload a local file to get a public URL
URL=$(polli upload cat.png)
polli gen image "make the cat purple" --image "$URL" --output purple.png
polli upload <file> streams one raw-file request to media.pollinations.ai (400 MiB max; 30-day lifecycle, refreshed by GETs once the object is at least 15 days old). Each upload receives a unique id. Human mode: URL on stdout and id/size/contentType on stderr. --json: full upload response on stdout. The returned URL is public (no auth to fetch) and works anywhere --image is accepted — gen image, gen video, etc.
Generate text
polli gen text "summarize the three laws of robotics"
Save to file: --output summary.txt. Use --system "<msg>" to set system prompt. For reasoning models, pass --reasoning low|medium|high to control reasoning effort. Only send --reasoning to models where reasoning: true in polli models --type text --json — the flag is not validated client-side, and non-reasoning models may silently accept it (openai), ignore it, or return a 400 (mistral).
Describe an image with a vision model
URL=$(polli upload selfie.jpg)
polli gen text "turn this person into a cartoon pet in one playful sentence" --image "$URL"
gen text --image <url...> attaches one or more public https URLs as an OpenAI-style multimodal message — repeatable for multi-image prompts. Local paths aren't supported; run them through polli upload first (see the upload recipe above). Only text models with "image" in input_modalities actually read the image — filter with polli models --type text --json | jq -r '.[] | select(.input_modalities | index("image")) | .name'. Non-vision models silently ignore the attachment. Good defaults: openai, gemini, claude.
Pipe stdin as context into text generation
cat README.md | polli gen text "what does this project do?"
stdin becomes context; the positional argument is the question.
Interactive chat session
polli gen chat --model openai/gpt-5.4-nano --system "you are a terse assistant"
Slash commands inside the session: /exit, /clear, /save <path>.
Text-to-speech
polli gen audio "hello world" --voice nova --output hello.mp3
echo "long script" | polli gen audio --voice nova --output out.mp3
Without --voice, the API uses the model's default voice. To discover the full live voice list, use the model registry: polli models --type audio --json | jq -r '.[].voices[]?' — each audio model entry includes its voices[] array. Format defaults to mp3; --format opus|aac|flac|wav to change. Accepts stdin (same as gen text). Add --play to save and then play the audio back (handy for narration/demos). Playback starts after the file is fully written, and the command blocks until playback finishes — if you want fire-and-forget, wrap in a subshell: ( polli gen audio "..." --play & ). Player on macOS: afplay; on Linux it tries ffplay, then mpv, then mpg123 in that order.
Generate music (elevenlabs/music-v2)
polli gen audio "lofi hip-hop beat" --model elevenlabs/music-v2 --duration 30 --instrumental --output track.mp3
Generate sound effects (elevenlabs/eleven-text-to-sound-v2)
polli gen audio "thunderclap with heavy rain" --model elevenlabs/eleven-text-to-sound-v2 --duration 5 --output thunder.mp3
Text-to-sound-effect via ElevenLabs (aliases: sfx, sound-effects). --duration 0.5–30s (omit to let the model pick). Billed per second of output (~$0.004/s, capped at $0.12 for a full 30s).
Multilingual TTS (elevenlabs/eleven-multilingual-v2)
polli gen audio "Bonjour, ceci est un test" --model elevenlabs/eleven-multilingual-v2 --voice rachel --output fr.mp3
Stable, lifelike TTS across 29 languages (aliases: multilingual-v2, eleven-v2) — a non-alpha alternative to the default v3.
Generate video
polli gen video "a spacecraft landing on mars" --model alibaba/wan-2.2-fast --duration 5 --output mars.mp4
Cheapest path: --model alibaba/wan-2.2-fast at ~$0.01/sec, fixed 5-second output (any --duration value is ignored — you always pay for and receive 5 sec). For image-to-video, pass --image <url> with a public HTTPS URL (local file paths and 404/rate-limited hosts will fail with a server error).
Flag support varies per model and is not enforced client-side. --duration, --aspect-ratio, and --audio are forwarded to the server but may be silently ignored — verified on wan-fast where duration is locked to 5s, --aspect-ratio 9:16 still returns 16:9, and --audio produces no audio track. Always inspect the output (file, ffprobe) before trusting a flag worked. Check polli models --type video --json for per-model capabilities.
Video is not tracked by --stats. polli models --type video --stats returns empty — the stats pipe only records text/image/audio events. To compare video models, fall back to polli models --type video --json and look at price/description fields. The same applies to --type 3d.
Generate a 3D model
polli gen 3d "a red fox" --output fox.glb
polli gen 3d --image https://media.pollinations.ai/abc --resolution high
--model defaults to hyper3d/rodin-2.5 for a text-only prompt and microsoft/trellis-2 once --image <url> is given (public http(s), upload local files first with polli upload) — trellis-2 is image-to-3D only and rejects a bare prompt. --resolution low|medium|high only affects trellis-2. The output extension is inferred from the response's Content-Type, not the model name: nvidia/asset-harvester returns a Gaussian Splat .ply, everything else returns .glb. hyper3d/rodin-2.5 and nvidia/asset-harvester require Paid Pollen.
Generate embeddings
polli gen embeddings "first text" "second text" --model google/gemini-embedding-2
printf 'first\nsecond\n' | polli gen embeddings # one input per stdin line
Prints one JSON array per input, in input order, one per line (pipe/jq-friendly). --dimensions <n> (128-4096), --task-type (Gemini retrieval hint), and --input-type query|document (Cohere) are optional. --json prints the full response including usage.
Change or isolate a voice
polli gen voice-change talk.mp3 --voice nova --format wav
polli gen isolate interview.mp4
voice-change re-speaks a local audio file in another voice (preset name or ElevenLabs voice ID) — default output voice.<format>. isolate strips music/background noise from local audio or video, keeping only speech — default output isolated.mp3. Both upload the file as multipart form data; ElevenLabs requires at least 4.6 seconds of audio for isolation, shorter clips fail with the API's audio_too_short error.
Speech with character-level timestamps
polli gen audio "Hello world" --timestamps
Saves the audio as usual (speech.mp3 by default) and appends .json to its path for the alignment/normalized_alignment character timings. Only elevenlabs/eleven-v3, elevenlabs/eleven-flash-v2.5, and elevenlabs/eleven-multilingual-v2 support this — other models fail with the API's error message.
Transcribe audio to text
polli gen transcribe recording.mp3 --language en
Models: openai/whisper-large-v3 (default), elevenlabs/scribe-v2, assemblyai/universal-2, assemblyai/universal-3.5-pro. Accepts common audio formats (mp3, wav, m4a, flac, ogg); non-audio input (e.g. a .txt file) returns a clear 400 invalid_request_error: extension "txt" not supported — no need to pre-validate with file. Default output is the plain transcript on stdout as a single line (pipe-friendly). Use --json for structured output: whisper and AssemblyAI return timing data when requested through the API; scribe returns only {text: "..."} — use whisper or AssemblyAI if you need timing data. --language <ISO-639-1> (e.g. en, fr) is an optional hint that can improve accuracy for non-English or accented speech.
Discover models
polli models --type text # text models only
polli models --type image --verbose # with context length / pricing
polli models --stats # health + avg latency + err% (60m default)
polli models --stats --window 5 # last 5 minutes only
Use --stats before choosing a model. Caveat: the err% column counts 5xx only — a model can show 0.0% while having massive 4xx rates (auth, validation, etc.). For the full picture use --stats --json and read errors_4xx, errors_5xx, latency_p95_ms.
Pricing fields are per-token, not per-request. completionImageTokens: 0.000008 means each output image-token costs that much — a single 1024x1024 image from openai/gpt-image-1-mini lands at ~$0.008, not $0.000008. Flat-priced image models (black-forest-labs/flux.1-schnell, tongyi-mai/z-image-turbo) expose completionImageTokens as the whole-image price because they emit exactly one "token" per image. When in doubt, make one call and read the true cost from polli usage --history --limit 5 --json.
Check usage and balance
polli usage # current pollen balance
polli usage --history # recent individual requests
polli usage --daily # daily cost summary
polli usage --daily --key polli-harness-claude --days 1 # cost of one harness key for the last day (`--model`, `--csv` filter/export both views; `--key` is repeatable)
polli earnings # developer earnings total + per-entity breakdown (default 30d)
polli earnings --days 7 # rolling window
polli quests # your quests + claim state (open/claimable/claimed/closed/coming)
polli quests --claimable # only rewards ready to claim
History is eventually consistent — a request you just made may not appear for 30–60s. When matching costs to freshly-generated media, use --limit 50 and filter by timestamp, and retry if the expected entry is missing. polli usage --json returns {"pollen": <number>} — the current balance only; use --history --json or --daily --json for cost breakdowns. Those print a JSON array of the API usage records (numeric cost_usd, api_key_id, available per-modality token fields), not the table columns.
Manage my-models
polli my-models list
polli my-models models --base-url https://api.example.com/v1 --bearer-token "$UPSTREAM_KEY"
polli my-models create --name my-model --title "My Model" --api chat_completions --url https://api.example.com/v1/chat/completions --bearer-token "$UPSTREAM_KEY" --upstream-model gpt-4.1-mini --required-safety privacy,secrets,sexual,violence,shield
polli my-models create --name my-image --title "My Image" --modality image --image-pricing request --completion-image-price 0.01 --base-url https://api.example.com/v1 --bearer-token "$UPSTREAM_KEY" --upstream-model flux
polli my-models update <id> --description "Updated description"
polli my-models update <id> --paid-only # only accept Paid Pollen; --no-paid-only reverts
polli my-models update <id> --required-safety privacy,secrets,sexual,violence,shield
polli my-models delete <id>
my-models manages owned community text, image, and transcription models. Any account can create private models; communityEndpointsAllowed: true is required only to publish them. API keys require account:keys. Use account:usage for narrow read-only usage and polli quests; use both permissions when a client needs both read-only account state and admin operations. Quest claiming is dashboard-only; polli quests is read-only and account-aware.
Register an image my-model
polli my-models test --modality image --base-url https://api.example.com/v1 --bearer-token "$UPSTREAM_KEY" --model image-v1
polli my-models create --name my-image --title "My Image" --modality image --image-pricing request --completion-image-price 0.01 --input-modalities text,image --base-url https://api.example.com/v1 --bearer-token "$UPSTREAM_KEY" --upstream-model image-v1
Test before creating. The probe reports the pricing mode it detected and the input modalities it found, including whether the endpoint accepts image edits; pass those back as --input-modalities text,image to advertise edit support. polli my-models list shows them in the inputs column.
Two billing modes: --image-pricing request charges --completion-image-price per generated image, while --image-pricing tokens bills returned token usage per 1M through --prompt-text-price, --prompt-image-price, and --completion-image-price.
Prices only apply to --visibility public models. A private model is owner-only and always free, so every price flag above is stored as 0 until you publish — and making a published model private clears its prices again.
--modality is fixed at creation — update cannot change it.
Manage agents
polli agents list
polli agents get <id>
polli agents create --config agent.json --name my-agent --title "My Agent"
polli agents create --config code-agent.json
polli agents update <id> --config agent.json
polli agents sync <id>
polli agents delete <id>
Prompt-agent config contains systemPrompt, baseModel, and optional mcpServers; create also requires --name and --title. Code-agent config contains type: "code_agent" and a public GitHub repository with agent.ts at its root; its model ID, title, and description come from GitHub. Use server IDs from the MCP catalog. Updates are partial: send only the fields to change; the rest keep their values. sync deploys the latest GitHub revision of a code agent.
Creating an agent also creates its callable model listing. Managed agents are text-only and free, with no fallbacks or per-user RPM. Deleting the agent also deletes its model listing. See Publish an Agent.
Manage API keys
polli keys list # list all keys on the account
polli keys info # details about the CURRENTLY AUTHENTICATED key only (takes no id)
polli keys create --name "my-bot" --type secret --budget 1000 --permissions profile usage # scoped key
polli keys create --name "my-app" --type publishable --redirect-uri https://app.example/callback --earnings
polli keys create --name "server" --models black-forest-labs/flux.1-schnell tongyi-mai/z-image-turbo # only these two models
polli keys update <id> --models text black-forest-labs/flux.1-schnell # every text model plus FLUX.1 Schnell
polli keys revoke <id> # id comes from `keys list --json`
--models takes categories (text, image, video, audio, 3d, embedding, realtime) and model IDs from polli models. A category includes models added later; a model ID allows only that model.
--permissions <perms...> scopes what the new key can do on the account (e.g. profile usage lets it call polli --key <new> usage). Without --permissions, new scoped keys can generate media but cannot read account state — polli --key <new> usage will 403. Include keys to let the new key create, list, and revoke keys itself. Existing keys with account:keys can manage my-models where that invite-only feature is enabled, but still need account:usage for read-only account state. Publishable app keys default developer earnings off; pass --earnings to enable them. To inspect a specific key other than the current one, use polli keys list --json | jq '.[] | select(.id == "<id>")'. keys info is intentionally scoped to the caller's own key.
Connect a coding harness
Polli defaults to openai/gpt-6-sol; --model <id> overrides it. Bloom is key-only and keeps its own model selection.
polli harness --help # supported harnesses
polli harness bloom on # create a dedicated key for Bloom CLI
polli harness dsh on # login if needed, mint key "polli-harness-dsh", write provider + default model
polli harness dsh on --model moonshotai/kimi-k2.6 # use the model ID from `polli models`
polli harness dsh on --no-mcp # configure the provider and skill without MCP tools
polli harness dsh off # restore the config backed up before "on"
polli harness hermes on # login if needed, mint key "polli-harness-hermes", add provider + Polli skill
polli harness hermes on --model deepseek/deepseek-v4-flash # use the model ID from `polli models`
polli harness hermes off # restore the config backed up before "on"
polli harness opencode on # enable the Pollinations OpenCode plugin
polli harness opencode off # remove the plugin setup and stored key
polli harness pi on # login if needed, mint key "polli-harness-pi", configure Pi with Pollinations
polli harness pi on --model moonshotai/kimi-k2.6 # use the model ID from `polli models`
polli harness pi off # restore the Pi config backed up before "on"
polli harness openclaw on # login if needed, mint key "polli-harness-openclaw", add provider + Polli skill
polli harness openclaw off # remove the Pollinations provider, key, and skill
polli harness tgpt on # use tgpt's Pollinations provider with a dedicated key
polli harness tgpt off # restore tgpt's previous configuration
Each adapter checks that its harness can be launched before login, key creation, or configuration. DSH's official launch uses npx, so its adapter checks for npx; Bloom, Hermes Agent, OpenCode, OpenClaw, Pi, and tgpt require their installed commands.
Bloom already uses Pollinations and only needs a dedicated key in $BLOOM_HOME/.env (default ~/.bloom/.env). tgpt's existing Pollinations provider is selected in ~/.config/tgpt/config.conf with a dedicated key and model. The DSH adapter globally configures the Pollinations provider, hosted Pollinations MCP, and this skill under $DSH_HOME (default ~/.dsh). OpenCode enables the existing Pollinations plugin and stores its dedicated key in the plugin config. OpenClaw adds a pollinations provider to ~/.openclaw/openclaw.json, stores a dedicated key in env.vars, and installs this skill under ~/.openclaw/skills/polli/. Pi writes ~/.pi/agent/models.json, auth.json, and settings.json, and installs this skill under ~/.pi/agent/skills/polli/. Guide: polli docs section "Coding Harnesses".
Read API docs
polli docs # full llm.txt reference
polli docs /v1/chat/completions # filter to one endpoint
polli docs --open # open in browser
Output contract
- Default (human mode): varies by command — most emit
key: valuepairs or tab-separated tables with a header row. Exceptions:gen textprints the full response to stdout;gen transcribeprints the transcript as plain text;gen chatruns an interactive REPL. Status/progress messages go to stderr, so pipes stay clean. --json: every command emits machine-parseable JSON to stdout; all human messages go to stderr. Always prefer--jsonwhen piping intojqor parsing — it's the only shape with a stable contract.- Exit codes: 0 on success, non-zero on auth failure, rate limit, network error, or invalid args. Error messages go to stderr.
Agent operating rules
- Run
polli auth status(orpolli whoami) first if you don't know whether the user is logged in. Fail fast with a clear "runpolli auth login" message if not. - Prefer
--jsonwhenever you'll parse the output. Never grep human-formatted tables. - Don't hardcode model IDs. Fetch the live list with
polli models --type <type>. Model availability changes. - Before picking a model for production use, check
polli models --stats. Rule of thumb for "healthy":err%≤ 5,avglatency in a reasonable range for the modality (standard text <5s, image <10s, video <60s), andrequestshigh enough to be statistically meaningful (ignore rows with <10 requests — noise). Filter by capability first, then optimize by health — e.g. for a reasoning task, narrow to models wherereasoning: true(viapolli models --type text --json), then cross-reference against--statsoutput. The healthiest model overall may not support the capability you need. Reasoning models are inherently slower — expect 5–50s, not <5s; when picking among them, prioritize lowerr%and request count over raw latency, and compare latency only within the reasoning-capable subset. - Always pass
--output <path>forgen image,gen audio,gen video— otherwise the file lands in the current directory with a default name. - For stdin-as-context on
gen text, pipe the context and pass the question as the positional argument:cat file | polli gen text "question about the file". - For exact flag lists, run
polli <cmd> --helporpolli gen <cmd> --help. This skill's recipes cover the common path; the CLI's own help is always the source of truth. - Use
polli docs [endpoint]over guessing API shapes. It prints the canonicalllm.txtreference from the live API.
Common pitfalls
- Forgetting
--outputon binary generators (image/audio/video) — the file goes to a default path, which may not be what the user wants. - Using
polli gen text --jsonexpecting OpenAI chat-completions shape — the CLI's--jsonwraps its own structure. Usepolli docs /v1/chat/completionsto see the raw API shape if you need it. - Running commands without auth —
polli auth status/polli whoamitells you who you're logged in as and your balance in one call. gen textstreams to a TTY, buffers when piped. The default now auto-detects — a human at the terminal sees tokens tick in, a pipe/redirect gets the full response once. Force either mode with--streamor--no-stream. For scripts and chains likepolli gen text … | polli gen audio …, you don't need to do anything; buffering happens automatically.- Translating a
polliworkflow into a browser app.gen.pollinations.airequires a bearer token for generation requests, so a plain client-sidefetchwith no auth returns 401 unless it is served from cache. Mint a scoped key withpolli keys createand proxy via your own backend. - Backgrounded
polli gencan fail silently — no output file, empty log, no error (a safety rejection is an explicit 400). Run in the foreground; if backgrounded, check the--outputfile exists and retry once in the foreground before calling it failed. - Content-safety 400s aren't only about violence. Body-focused action phrasing can trip a sexual-content category on some edit models. Rephrase the benign intent neutrally (e.g. sport framing) and retry with a new seed.