Quick Start
Get your API key
Go to Settings in the app and create an API key. Keys start with sk_live_ and are shown only once.
Create a project
All generations belong to a project. Create one via the API to get a projectId.
Generate & poll
Send a generation request, receive a jobId, then poll or stream for results.
# Create a project
curl -X POST https://api.storynote.ai/v1/projects \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "My First Project"}'
# Generate an image
curl -X POST https://api.storynote.ai/v1/projects/{projectId}/image \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"directPrompt": "A fox reading a book in a cozy library",
"modelId": "fal-ai/flux-2",
"aspectRatio": "16:9"
}'
# Poll for result
curl https://api.storynote.ai/v1/jobs/{jobId} \
-H "Authorization: Bearer sk_live_your_key_here"Authentication
All API requests require a Bearer token in the Authorization header. You can also pass it via the X-API-Key header.
# Bearer token (recommended)
curl https://api.storynote.ai/v1/models/available \
-H "Authorization: Bearer sk_live_your_key_here"
# X-API-Key header (alternative)
curl https://api.storynote.ai/v1/models/available \
-H "X-API-Key: sk_live_your_key_here"API keys are created and managed in your Settings modal in the app. Keys begin with sk_live_ and are displayed only at creation. Store them securely.
Image Generation
/v1/projects/:projectId/imageGenerate one or more images from a text prompt. Returns job IDs that can be polled for results.
Request Body
| directPrompt* | string | The image prompt. Bypasses LLM rewriting for exact control. |
| modelId | string | Model to use. e.g. "fal-ai/flux-2", "nano-banana" |
| aspectRatio | string | "1:1" | "16:9" | "9:16" | "4:3" | "3:4" |
| numImages | number | Number of images to generate (1–8). Default: 1 |
| seed | number | Reproducibility seed. Each additional image increments by 1. |
Response
// 202 Accepted { "jobId": "663f1a2b...", "jobIds": ["663f1a2b..."] }
Video Generation
/v1/projects/:projectId/videoGenerate a video from a text prompt or an image frame. Supports image-to-video, text-to-video, and keyframe (first+last image) modes.
Request Body
| directPrompt | string | Motion/scene description for the video. |
| firstFrame | object | { imageUrl: "https://..." } — source image for image-to-video. |
| lastFrame | object | { imageUrl: "https://..." } — end frame for keyframe mode. |
| modelId | string | Video model. e.g. "fal-ai/kling-video/v2.6/pro/image-to-video" |
| durationSec | number | Video duration in seconds. Default: 3 |
| aspectRatio | string | "16:9" | "9:16" | "1:1" | "4:3" | "3:4" |
| seed | number | Reproducibility seed. |
Response
// 202 Accepted { "jobId": "663f1a2b..." }
Audio Generation
/v1/projects/:projectId/audioGenerate speech, sound effects, or music. The soundType field determines which parameters are required.
Request Body
| soundType* | string | "speech" | "sfx" | "music" |
| text | string | Text to speak. Required for soundType: speech. |
| prompt | string | Description of sound/music. Required for sfx and music. |
| voiceId | string | ElevenLabs voice ID for speech generation. |
| modelId | string | Audio model override. |
| duration | number | Duration in seconds (sfx/music). |
Response
// 202 Accepted { "jobId": "663f1a2b..." }
Jobs & Polling
/v1/jobs/:idGet the current status of a generation job. Poll until status is 'completed' or 'failed'.
Response
{ "id": "663f1a2b...", "status": "completed", "prompt": "A fox reading a book in a cozy library", "model": "fal-ai/flux-2", "imageUrl": "https://storage.storynote.ai/...", "videoUrl": null, "audioUrl": null, "seed": 42, "error": null, "updatedAt": "2025-05-01T12:00:00Z" }
/v1/jobs/batch?jobIds=id1,id2,id3Fetch multiple jobs in a single request. Up to 256 job IDs per request.
Response
{ "jobs": { "663f1a2b...": { "id": "663f1a2b...", "status": "completed", ... }, "663f1a2c...": { "id": "663f1a2c...", "status": "processing", ... } } }
/v1/jobs/events/streamSSEReal-time Server-Sent Events stream for all your job updates. More efficient than polling — connect once, receive updates for all jobs.
const events = new EventSource(
"https://api.storynote.ai/v1/jobs/events/stream",
{ headers: { "Authorization": "Bearer sk_live_..." } }
);
events.addEventListener("job", (e) => {
const job = JSON.parse(e.data);
console.log(job.id, job.status, job.imageUrl);
});Tip: Use the SSE stream instead of polling individual jobs. It's real-time, uses a single connection, and won't count against your rate limit per job.
Job Statuses
queuedWaiting in queue
processingGeneration in progress
completedResult ready
failedError occurred
Models
/v1/models/availableReturns all models available to your account, organized by type (image, video, audio, tools) with credit costs and allowed parameters.
Response
{ "image": [ { "id": "fal-ai/flux-2", "name": "Flux 2", "credits": 2, "tier": "fast", "allowedAspectRatios": ["1:1", "16:9", "9:16", "4:3", "3:4"] }, ... ], "video": [...], "audio": [...] }
Projects
Every generation must belong to a project. Projects organize your generated assets and can be shared publicly.
/v1/projectsCreate a new project.
Request Body
| name* | string | Project name. |
Response
{ "id": "665a3b1c...", "name": "My Project", "createdAt": "2025-05-01T12:00:00Z" }
/v1/projectsList all your projects.
Response
[ { "id": "665a3b1c...", "name": "My Project", "createdAt": "..." }, { "id": "665a3b1d...", "name": "Campaign Assets", "createdAt": "..." } ]
/v1/projects/:idGet a single project with its full graph data.
Response
{ "id": "665a3b1c...", "name": "My Project", "nodes": [...], "edges": [...], "createdAt": "..." }
Model Pricing
Each generation deducts credits based on the model used. Video models charge per second of output. Purchase credits from the pricing page.
| Type | Model | Cost |
|---|---|---|
| Image | Z-Image Turbo | 1 cr |
| Flux 2 | 2 cr | |
| Seedream v4 | 5 cr | |
| Nano Banana | 6 cr | |
| Recraft V4 | 6 cr | |
| Nano Banana 2 | 12 cr | |
| Nano Banana Pro | 35 cr | |
| Video | Veo 3.1 Lite | 5 cr/s |
| Pixverse v6 | 7 cr/s | |
| Kling v2.6 Pro | 10 cr/s | |
| Audio | ElevenLabs Music | 1 cr |
| ElevenLabs SFX | 2 cr | |
| ElevenLabs TTS | 3 cr | |
| Tools | Background Removal | 3 cr |
| Object Removal | 3 cr | |
| Image Upscale | 5 cr |
Rate Limits
300 req/minAll API endpoints
20 req/minImage, video, audio endpoints
50 req/minFile upload signing
Per user limitConcurrent event streams
When rate limited, the API returns 429 Too Many Requests. Back off and retry with exponential delay.
Errors
All errors return a consistent JSON shape with an error message and HTTP status code.
// Error response format
{
"error": "Insufficient credits",
"code": 402
}| Code | Meaning |
|---|---|
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorized — invalid or missing API key |
| 402 | Payment required — insufficient credits |
| 404 | Resource not found |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
Full Examples
End-to-end examples that create a project, generate an image, and retrieve the result.
// Node.js — generate an image and get the result
const API = "https://api.storynote.ai/v1";
const KEY = "sk_live_your_key_here";
const headers = {
"Authorization": `Bearer ${KEY}`,
"Content-Type": "application/json",
};
// 1. Create a project
const project = await fetch(${API}/projects`, {
method: "POST",
headers,
body: JSON.stringify({ name: "API Demo" }),
}).then(r => r.json());
// 2. Generate an image
const { jobId } = await fetch(${API}/projects/${project.id}/image`, {
method: "POST",
headers,
body: JSON.stringify({
directPrompt: "A fox reading a book in a cozy library, warm lighting",
modelId: "fal-ai/flux-2",
aspectRatio: "16:9",
}),
}).then(r => r.json());
// 3. Poll until complete
let job;
do {
await new Promise(r => setTimeout(r, 2000));
job = await fetch(${API}/jobs/${jobId}`, { headers }).then(r => r.json());
} while (job.status !== "completed" && job.status !== "failed");
if (job.status === "completed") {
console.log("Image URL:", job.imageUrl);
} else {
console.error("Failed:", job.error);
}Ready to build?
Get your API key and start generating images, videos, and audio in minutes.
Get API Key