Vercel AI SDK provider
Use PopNow models with the Vercel AI SDK 7. generateImage for text to image and image editing, generateSpeech, transcribe and chat models, billed in USD with one API key.
@popnow/ai-sdk-provider connects PopNow models to the Vercel AI SDK. With one API key you call image generation and image editing models through generateImage, text to speech through generateSpeech, speech to text through transcribe, and chat models through generateText and streamText. Everything is billed in USD from your separate API balance.
The package is open source under the MIT license (GitHub). It requires AI SDK 7 (ai@^7) and Node.js 22 or newer.
At a glance
| AI SDK function | PopNow models | Factory |
|---|---|---|
generateImage | Text to image and image editing: Nano Banana, GPT Image, Seedream, FLUX, Qwen Image and more | popnow.image(id) |
generateSpeech | Text to speech | popnow.speech(id) |
transcribe | Speech to text | popnow.transcription(id) |
generateText, streamText | Chat models through the OpenAI-compatible endpoint | popnow(id), popnow.chat(id) |
Set up
Install the packages
npm install @popnow/ai-sdk-provider aiCreate an API key
Create a key in the API console. Keys start with sk-popnow-. The API balance starts at $0; add funds in API billing before the first paid call.
Put the key in your environment
The default popnow instance reads POPNOW_API_KEY:
export POPNOW_API_KEY="sk-popnow-..."Keep the key on your server. Anyone who can read it can spend your API balance.
Generate an image
import { popnow } from '@popnow/ai-sdk-provider';
import { generateImage } from 'ai';
import fs from 'node:fs';
const { image } = await generateImage({
model: popnow.image('google/nano-banana-2.1/text-to-image'),
prompt: 'A cozy reading nook beside a rain-streaked window, warm lamp light',
aspectRatio: '16:9',
providerOptions: {
popnow: { resolution: '2k' },
},
});
await fs.promises.writeFile('nook.png', image.uint8Array);Provider settings
For other settings, create your own instance with createPopNow:
import { createPopNow } from '@popnow/ai-sdk-provider';
const popnow = createPopNow({
apiKey: process.env.POPNOW_API_KEY, // default: POPNOW_API_KEY
baseURL: 'https://api.popnow.ai', // default: POPNOW_BASE_URL or https://api.popnow.ai
headers: { 'X-PopNow-Tags': 'my-app' }, // optional, sent with every request
});createPopNow also takes a custom fetch function.
Edit an image
Edit models have IDs that end in /edit. Pass the source images, and a mask where the model takes one, in the prompt:
import { popnow } from '@popnow/ai-sdk-provider';
import { generateImage } from 'ai';
const { image } = await generateImage({
model: popnow.image('google/nano-banana-2.1/edit'),
prompt: {
text: 'Change the jacket to deep forest green; keep the face and background',
images: ['https://example.com/portrait.jpg'],
},
});URLs are passed through as they are. Images given as bytes or base64 are sent inline when they are JPEG, PNG, WebP or GIF of at most 1 MiB, and uploaded to PopNow otherwise.
How settings map to PopNow
Every PopNow model validates its input against its own JSON Schema and rejects unknown fields. The provider reads the model's schema (GET /api/v1/models/{model}, cached for ten minutes) and only sends a setting when the model has the matching field:
| AI SDK setting | PopNow input field |
|---|---|
prompt | prompt |
aspectRatio | aspect_ratio |
size | width and height; otherwise reduced to aspect_ratio, with a compatibility warning |
seed | seed |
n | num_outputs or num_images; for models without either, each image is its own task and the AI SDK runs several |
prompt images | image_urls, or image_url for single-image models |
prompt mask | mask_url |
A setting the model does not have is left out and reported in warnings as unsupported. Any other model field goes in providerOptions.popnow, spelled exactly as in the model's schema (snake_case), for example resolution, output_format or quality. These values override the mapped ones.
Text to speech
import { popnow } from '@popnow/ai-sdk-provider';
import { generateSpeech } from 'ai';
import fs from 'node:fs';
const { audio } = await generateSpeech({
model: popnow.speech('openai/tts-1/text-to-speech'),
text: 'Hello from PopNow.',
voice: 'nova',
speed: 1.1,
});
await fs.promises.writeFile('hello.mp3', audio.uint8Array);voice maps to voice_id, outputFormat to output_format, and instructions, speed and language to fields of the same name, when the model has them. Each model's schema lists the voices it accepts.
Transcription
import { popnow } from '@popnow/ai-sdk-provider';
import { transcribe } from 'ai';
import fs from 'node:fs';
const result = await transcribe({
model: popnow.transcription('openai/whisper-1/speech-to-text'),
audio: await fs.promises.readFile('meeting.mp3'),
providerOptions: { popnow: { language: 'en' } },
});
console.log(result.text, result.words);Audio bytes are uploaded to PopNow first (MP3 and WAV, up to 90 MiB). A public https:// URL (audio: new URL('https://...')) is passed to PopNow directly without downloading it.
Chat models
PopNow serves chat models through an OpenAI-compatible API at https://api.popnow.ai/v1. The provider uses @ai-sdk/openai-compatible for them, with the same API key and balance:
import { popnow } from '@popnow/ai-sdk-provider';
import { generateText } from 'ai';
const { text } = await generateText({
model: popnow('anthropic/claude-haiku-5.5/chat'),
prompt: 'Suggest three names for a kite shop by the sea.',
});streamText works with the same model:
import { popnow } from '@popnow/ai-sdk-provider';
import { streamText } from 'ai';
const result = streamText({
model: popnow('anthropic/claude-haiku-5.5/chat'),
prompt: 'Write a short welcome message for a kite shop.',
});
for await (const delta of result.textStream) process.stdout.write(delta);Chat models are priced per token, with no per-request minimum. Chat models lists every model and the parameters it accepts.
Model IDs
Model IDs have the form <maker>/<model>/<task>, for example google/nano-banana-2.1/text-to-image, openai/tts-1/text-to-speech or openai/whisper-1/speech-to-text. The TypeScript types suggest a few; any ID your key can call works. Models lists every model with its parameters and prices. To list them from code, with input schemas:
curl "https://api.popnow.ai/api/v1/models?modality=image&includeSchema=1" \
-H "Authorization: Bearer $POPNOW_API_KEY"modality is image, video or audio. Chat models are listed at GET https://api.popnow.ai/v1/models.
Pricing
Prices are in USD per image, per second or per 1,000 characters, depending on the model, and shown on Models and each model's page. The AI price index lists them too. Each task holds its maximum price from your API balance when it is accepted and settles the actual charge when it finishes. Tasks that fail or expire are not charged. Details: Billing and quotes.
Each image entry in providerMetadata.popnow.images carries the taskId, its costUsd (a USD decimal string) and whether the charge is settled. Speech and transcription results carry the same fields in providerMetadata.popnow.
Tasks, polling and retries
PopNow generation is asynchronous. Each call creates a task, waits up to 60 seconds on the create request, then polls the task (every 2 seconds at first, growing to every 15 seconds) until it is final, and downloads the output files from their signed links. The API key is never sent to those links.
These options go in providerOptions.popnow and are not sent to the model:
| Option | Default | Meaning |
|---|---|---|
pollIntervalMs | 2000 | First delay between polls |
pollTimeoutMs | Task deadline plus two minutes | Stop waiting after this long; the task keeps running |
waitSeconds | 60 | How long the create request is held open (0 to 60) |
idempotencyKey | Generated per call | Stable key for one logical generation |
metadata | None | Your own labels, stored with the task |
Every create request carries an Idempotency-Key. Connection errors, 429 and retryable 5xx responses are retried twice with the same key, so a retry never starts a second task. When you pass your own idempotencyKey, the AI SDK's own retries (maxRetries) reuse it too. See Idempotency.
abortSignal stops waiting at once. A task that was already accepted keeps running on PopNow.
Errors
APICallError(from@ai-sdk/provider) for API errors. The message carries the PopNow business code, such as40201(not enough API balance) or40301(the key may not call this model). See Errors.PopNowTaskErrorwhen a task ends asfailedorexpired. It hastaskId,state,errorCode(for examplecontent_rejected),errorMessageandcostUsd. These tasks are not charged.PopNowTaskTimeoutErrorwhenpollTimeoutMsruns out. It hastaskId; the task keeps running.
import { popnow, PopNowTaskError } from '@popnow/ai-sdk-provider';
import { generateImage } from 'ai';
try {
await generateImage({
model: popnow.image('google/nano-banana-2.1/text-to-image'),
prompt: 'A lighthouse at dawn, watercolor',
});
} catch (error) {
if (PopNowTaskError.isInstance(error)) console.log(error.taskId, error.errorCode);
}Not included
- Video. The AI SDK's video model interface is still experimental (
experimental_generateVideo), so this package does not implement it yet. PopNow video models are available through the task API, the official SDKs and the OpenAI Videos compatible endpoints. - Embeddings.
embeddingModelthrowsNoSuchModelError.
Which AI SDK version does it need?
AI SDK 7 (ai@^7) on Node.js 22 or newer. Install both with npm install @popnow/ai-sdk-provider ai.
Can I call it from the browser?
Not with your API key in the page: anyone who can read the key can spend your API balance. Call the AI SDK from a server route, such as a Next.js route handler or a server action, and send the result to the browser.
Does it support video models?
Not yet, because the AI SDK's video interface is still experimental. Use the task API or the JavaScript SDK for video.
How do I see what a call cost?
Read providerMetadata.popnow: each task reports its taskId, costUsd and whether the charge is settled. The API console shows usage per key.
Related
LangChain
Give LangChain and LangGraph agents tools that create AI images, video, music and speech with PopNow. Each paid call is quoted in USD and runs only after approval.
n8n
Generate AI images, video and audio in n8n with the built-in HTTP Request node and the PopNow API. Importable workflow JSON, credential setup, polling loop, webhooks and fixes for common errors.

