Generate AI storybooks from your code.
A REST API for Pro and Premium subscribers. Kick off a generation job, poll for status, and the finished book automatically shows up in your ChildrenBooks library alongside anything you created from the dashboard.
Secure by default
API keys are shown once, hashed with SHA-256 at rest. Rotation and instant revoke from the dashboard. All requests require TLS.
Rate-limited, abuse-resistant
Per-minute, per-day and per-month limits protect your quota from leaks. Responses include x-ratelimit-* headers so SDKs can back off gracefully.
One library, one quota
Books you generate via the API are visible in the same library as dashboard-created books. Quota is shared with your Pro or Premium plan.
Quickstart & API reference
Get a Pro or Premium key from the dashboard, then run the examples. Invalid bodies return 400 / VALIDATION_ERROR; enums must match exactly (case-sensitive). Responses use ok, optional requestId, and data or error. Respect x-ratelimit-* and retry-after on 429.
Prerequisites
- 1. Upgrade to Pro or Premium. Free plans cannot issue API keys. See pricing.
- 2. Open the dashboard and click Generate API key. Copy it — you won't see it again.
- 3. Send your first request. Replace cbk_live_XXX with your key:
Request
POST /v1/stories
Creates an asynchronous generation job for an illustrated children's storybook. The server queues the work and responds right away with 202 Accepted; the body returns taskId and bookId so you can poll GET /v1/stories/{bookId} for progress. Each page (cover included) consumes one credit from your plan quota.
Headers
Authentication: x-api-key: cbk_live_… (preferred) or Authorization: Bearer cbk_live_….
| Header | Required | Description |
|---|---|---|
| content-type | Yes | Must be application/json. |
| Idempotency-Key | No | Optional unique key per logical create (e.g. UUID). Same key within 24h with the same JSON body returns the original taskId / bookId (no second book). Same key with a different body returns 409 / IDEMPOTENCY_CONFLICT. Max length 128. Requires API-side Redis; if Redis is down, deduplication is skipped. |
| X-Request-Id | No | If set, echoed in responses and logs for tracing. Max length 64; otherwise the server generates one. |
JSON body
| Field | Type | Constraints | Details & allowed values |
|---|---|---|---|
| storyIdea | string | Required. Length 10–2000 characters. | Plain-language description of the book (plot, characters, setting). Not a model prompt template — write what you want the story to be about. |
| ageRange | string (enum) | Required. Must match exactly one allowed string (case-sensitive). | Target reading age band (vocabulary & themes): 0-3, 4-7, 8-12 |
| style | string (enum) | Required. One enumerated value. Case-sensitive — use 3d, not "3D". | Visual art style for illustrations.
|
| pageCount | integer | Required. Integer 5–15 inclusive. 1 credit per page. | Total pages including the cover. Monthly quota counts each page toward your plan credits. |
| tone | string (enum) | Required. Snake_case enum — send the string exactly as defined. | Narrative mood / genre bucket.
|
| language | string (enum) | Required. Lowercase English keys only (e.g. english, not en). | Spoken/written language of the generated story text: english, spanish, french, german, chinese, japanese, korean, russian, portuguese, italian |
Minimal call using the headers and JSON fields above (replace the key with yours). cURL and TypeScript are the same request — pick a tab. Use an env var for the key on the server; never expose it in a browser bundle.
curl -X POST https://api.childrenbooks.online/v1/stories \
-H "x-api-key: cbk_live_XXXXXXXXXXXX" \
-H "content-type: application/json" \
-d '{
"storyIdea": "A little fox who learns to share carrots with forest friends.",
"ageRange": "4-7",
"style": "watercolor",
"pageCount": 7,
"tone": "heartwarming",
"language": "english"
}'Response
Returned with 202 Accepted when the job is queued — same ok / data envelope as other success paths. Example payload; collapsible field tables (POST 202 & more) live under Response.
{
"ok": true,
"requestId": "01HZXK3M6Q8EXAMPLE00RVW5T9A",
"data": {
"taskId": "550e8400-e29b-41d4-a716-446655440000",
"bookId": "660e8400-e29b-41d4-a716-446655440001",
"status": "pending"
}
}GET /v1/stories/{id}
Retrieves one storybook's metadata, generation status, progress, and any page rows written so far. Use the bookId from POST /v1/stories. You only receive books that belong to the same user as your API key; missing or other users' ids return 404.
Path parameters
| Parameter | In | Rules |
|---|---|---|
| id | path | The bookId returned from POST. Must be a UUID. You only see books owned by the API key's user. |
Polling
Generation is asynchronous — poll with GET /v1/stories/{bookId} until data.status is completed or failed. The full 200 JSON body is shown once under Response. cURL and TypeScript below are the same poll — use the tab to switch.
curl https://api.childrenbooks.online/v1/stories/<bookId> \
-H "x-api-key: cbk_live_XXXXXXXXXXXX"Response
Examples and field reference for success and error envelopes. Field order may vary; null may be explicit or omitted.
{
"ok": true,
"requestId": "efa24798-b66a-405a-b3c9-956893531fe3",
"data": {
"id": "4cf184ed-0a29-4860-a257-0f8998357121",
"title": "Finn's Carrot Patch",
"slug": "finns-carrot-patch",
"coverImage": "https://3fyspdsbp21eloyx.public.blob.vercel-storage.com/stories/4cf184ed-0a29-4860-a257-0f8998357121/storybook-4cf184ed0a294860a2570f8998357121-cover-uHWi4JpCATyXGczHi4uAJa1m24i62q.webp",
"summary": "A little fox named Finn discovers that sharing his bountiful carrot harvest with his hungry forest friends makes him feel even happier than keeping them all for himself.",
"storyIdea": "A little fox who learns to share carrots with forest friends.",
"ageRange": "4-7",
"style": "watercolor",
"pageCount": 5,
"tone": "heartwarming",
"language": "english",
"source": "api",
"storageProvider": null,
"createdAt": "2026-04-19T11:47:16.327Z",
"updatedAt": "2026-04-19T11:58:15.877Z",
"status": "completed",
"progress": 100,
"errorMessage": null,
"completedAt": "2026-04-19T11:48:20.894Z",
"pages": [
{
"index": 0,
"title": null,
"content": "In a sun-dappled corner of Whispering Woods, a little fox named Finn had the most wonderful carrot patch. He watered it every day, and the carrots grew big, orange, and crunchy. One morning, Finn pulled up the very last, perfect carrot. \"My carrots!\" he said proudly, piling them into a big basket. He planned to eat them all himself. Just then, he heard a soft sniffle. His friend Thumper the Rabbit was nearby, looking sadly at his own garden, which was just dry dirt. \"My seeds didn't grow,\" Thumper sighed, his tummy rumbling.",
"imageUrl": "https://3fyspdsbp21eloyx.public.blob.vercel-storage.com/stories/4cf184ed-0a29-4860-a257-0f8998357121/storybook-4cf184ed0a294860a2570f8998357121-0-g8dJ8dzaQbBVfhl5zGc4X74YBwGQqC.webp"
},
{
"index": 1,
"title": null,
"content": "Finn looked at his huge basket and then at his hungry friend. He didn't want to share his special carrots. They were *his* prize. But Thumper's sad eyes made his own heart feel funny. \"Well,\" Finn said slowly, scratching his ear. \"Maybe... maybe one carrot wouldn't be missed.\" He picked out a nice, medium-sized carrot and held it out. Thumper's eyes grew wide. \"For me?\" he squeaked. He took the carrot and munched happily. \"Oh, thank you, Finn! This is the best carrot ever!\" Seeing Thumper's joy made a warm, fizzy feeling bubble up in Finn's chest. It felt even better than crunching the carrot himself!",
"imageUrl": "https://3fyspdsbp21eloyx.public.blob.vercel-storage.com/stories/4cf184ed-0a29-4860-a257-0f8998357121/storybook-4cf184ed0a294860a2570f8998357121-1-HKXNIJm9pHRjGcnJfvlmU5jHFHrb0s.webp"
},
{
"index": 2,
"title": null,
"content": "The warm, fizzy feeling was so nice that Finn wanted to feel it again. He saw Bella the Deer walking slowly. \"Are you okay, Bella?\" Finn asked. \"I'm just a little tired from looking for berries,\" she said. Finn didn't hesitate this time. He picked two more carrots from his basket. \"Here, these will give you energy!\" Bella nuzzled him gratefully. \"You are so kind, Finn.\" Next, Oliver the Owl landed on a branch. \"Ahem, long night of studying the stars,\" he said, yawning. Finn offered him a carrot too. \"For your wisdom!\" Oliver hooted happily. Finn's basket was getting lighter, but his heart felt wonderfully full.",
"imageUrl": "https://3fyspdsbp21eloyx.public.blob.vercel-storage.com/stories/4cf184ed-0a29-4860-a257-0f8998357121/storybook-4cf184ed0a294860a2570f8998357121-2-1qtunfUGjYz83GaofvzvrBin6HiHsB.webp"
},
{
"index": 3,
"title": null,
"content": "Soon, Finn's basket was empty, but he was surrounded by happy friends. Thumper was bouncing, Bella was strong again, and Oliver was wide awake. \"Let's have a picnic!\" suggested Thumper. They all agreed. Finn thought his carrots were gone, but then his friends brought things to share! Thumper found sweet clover, Bella brought juicy blackberries, and Oliver shared shiny nuts. They had the best feast ever, laughing and talking. Finn realized something wonderful. Sharing his carrots didn't mean he had less; it meant he had more—more smiles, more laughter, and the best friends in the whole forest.",
"imageUrl": "https://3fyspdsbp21eloyx.public.blob.vercel-storage.com/stories/4cf184ed-0a29-4860-a257-0f8998357121/storybook-4cf184ed0a294860a2570f8998357121-3-6vOoqqtHK3eyV6VJ3njHaipkGrS1az.webp"
}
]
}
}{
"ok": false,
"requestId": "01HZXK3M6Q8EXAMPLE00RVW5T9A",
"error": {
"code": "VALIDATION_ERROR",
"message": "Request body failed validation.",
"fields": {
"pageCount": "Expected number between 5 and 15"
}
}
}Field tables — POST 202 body (taskId, bookId, status)· expand
| Field | Type | Description |
|---|---|---|
| ok | boolean | Always true for this response. |
| requestId | string | Correlation id (echo of X-Request-Id or server-generated). |
| data.taskId | string (uuid) | Background generation task id (queue correlation). |
| data.bookId | string (uuid) | Storybook id — use as GET /v1/stories/{id} path parameter. |
| data.status | string | Initial job status. Typically pending immediately after accept. Values: pending, processing, completed, failed. |
Field tables — GET 200 data object, pages[] items, and page fields· expand
| Field | Type | Description |
|---|---|---|
| id | string (uuid) | Book id (same as the path id). |
| title | string | null | Generated or edited title. |
| slug | string | null | URL slug for the book on the website, when present. |
| coverImage | string | null | Cover image URL. |
| summary | string | null | Short synopsis. |
| storyIdea | string | null | The story prompt / idea stored on the book. |
| ageRange | string | null | Echo of request input (e.g. 4-7). |
| style | string | null | Illustration style key. |
| pageCount | integer | null | Target page count for the book (includes cover). |
| tone | string | null | Tone / genre key. |
| language | string | null | Story language key. |
| source | string | null | Where the book was created: web, api, hf (or null). |
| storageProvider | string | null | Storage backend identifier for assets, when applicable. |
| createdAt | string | null | ISO 8601 timestamp when the book row was created. |
| updatedAt | string | null | ISO 8601 timestamp of last update to the book record. |
| status | string | Latest generation task status. pending, processing, completed, failed, unknown. unknown if no task row exists yet. |
| progress | integer | Rough completion percentage 0–100 from the latest task (defaults to 0). |
| errorMessage | string | null | Human-readable failure reason when status is failed. |
| completedAt | string | null | ISO 8601 timestamp when generation completed, when set. |
| pages | array | Ordered list of page objects (see next table). Grows as illustrations and text are written. |
data.pages[] — each element
| Field | Type | Description |
|---|---|---|
| index | integer | Page order index from storage (sorted ascending with other pages). |
| title | string | null | Page title or heading. |
| content | string | null | Page body text. |
| imageUrl | string | null | Rendered illustration URL for this page. |
Error body
Use error.code for logic; error.message is display-only.
Field table — error object· expand
| Field | Type | Description |
|---|---|---|
| ok | boolean | Always false. |
| requestId | string | Same correlation id pattern as success responses. |
| error.code | string | Machine-readable error code (see list below). |
| error.message | string | Human-readable message (do not rely on exact wording for logic). |
| error.fields | object | undefined | Optional map of field name → validation message (common for VALIDATION_ERROR). |
| error.meta | object | undefined | Optional extra context from the server (structure may vary). |
error.code values include: UNAUTHORIZED, INVALID_API_KEY, PLAN_REQUIRED, FORBIDDEN, VALIDATION_ERROR, NOT_FOUND, ALREADY_EXISTS, RATE_LIMIT_EXCEEDED, DAILY_CAP_EXCEEDED, INSUFFICIENT_CREDITS, EXTERNAL_SERVICE_ERROR, INTERNAL_ERROR, BAD_REQUEST, METHOD_NOT_ALLOWED
Limits
| Plan | Per minute | Per day (books) | Monthly quota |
|---|---|---|---|
| Pro | 60 req | 50 books | Shared with your plan credits |
| Premium | 200 req | 200 books | Shared with your plan credits |
When a limit is hit the API returns 429 with retry-after seconds. Every response also includes x-ratelimit-remaining so your client can preemptively slow down.
Usage policy
- • API keys are tied to a single user. Don't share them across accounts.
- • Do not generate content that violates our Terms of Service, including sexual content involving minors, real-world violence, or copyrighted characters.
- • Keep secrets in server-side env vars. Never embed an API key in a browser bundle.
- • We may rotate or revoke a key after abusive patterns (burst traffic, cross-continent IP jumps within minutes) and contact you by email.
- • Downgrading to Free instantly revokes the key.
Ready to build?
Log in, activate your API key in the dashboard, and ship your first storybook integration in under 10 minutes.