KDPBot AI

KDPBot API — Developer Portal

KDPBot AI turns a book concept into a finished, publish-ready manuscript: outline, chapters, cover art, chapter illustrations, and an export file you can upload to Amazon KDP. Everything the web app does is driven by the same public HTTP JSON API documented on this page — so scripts, pipelines and autonomous agents can drive the whole pipeline without a browser.

API base URL https://kdpbot.com/api/v1 — same-origin and version-pinned. https://bookgen-backend-5zuf.onrender.com/api/v1 reaches the same backend directly.

What KDPBot does, and when to use the KDPBot API

KDPBot AI is a book-production system for self-publishers. A book moves through a fixed lifecycle, and the API exposes every stage of it:

  1. Concept — create a Book record with a title, genre, target audience, chapter count, target word count, trim size and content language.
  2. Outline — an LLM produces the chapter structure and writes it back as real Chapter rows, each with its own title, summary and per-chapter word budget.
  3. Content — a background worker writes every chapter in sequence, updating a progress record you can poll.
  4. Art — AI cover variants for the book and optional illustrations for individual chapters.
  5. Export — the finished manuscript rendered as PDF, DOCX or EPUB, sized to the trim you chose.

Concrete jobs the KDPBot API is good at

Things to know before you build

The KDPBot API surface: REST over HTTPS, and nothing else

So that nobody has to discover it by probing: KDPBot exposes exactly one API surface — a REST API over HTTPS that speaks JSON, except the four export actions, which return binary file bodies.

KDPBot API quickstart

The sequence below takes an account from login to a downloaded PDF using nothing but curl. It runs against the production API base URL. Substitute your own credentials; create an account first at kdpbot.com or via POST /api/auth/register/.

Step 1 — Authenticate and capture a token

BASE="https://kdpbot.com/api/v1"

TOKEN=$(curl -s -X POST "$BASE/auth/login/" \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"your-password"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['tokens']['access'])")

echo "${TOKEN:0:24}..."

The full login response also contains the user object and the user's current subscription, so a client can decide up front whether the account has quota left.

Step 2 — Create a book

BOOK_ID=$(curl -s -X POST "$BASE/books/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "The Complete Guide to Remote Work",
        "subtitle": "Strategies for Distributed Teams",
        "genre": "business",
        "target_audience": "Managers leading distributed teams",
        "description": "A practical playbook for hiring, onboarding and running remote teams.",
        "chapter_count": 10,
        "target_word_count": 30000,
        "trim_size": "6x9",
        "content_language": "en"
      }' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

echo "book id: $BOOK_ID"

If the account is out of book quota this returns 402 with error: "quota_exceeded" and an upgrade object pointing at /pricing. Quota is not consumed at creation time — it is consumed when generation finishes.

Step 3 — Generate the outline

curl -s -X POST "$BASE/books/$BOOK_ID/generate-outline/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Length: 0"

This call is synchronous and may take tens of seconds. It returns the full book object with a populated chapters array and sets status to outline. Note that it replaces any chapters that already exist on the book. To edit titles or summaries before writing content, send your revisions to POST /api/books/{id}/update-outline/.

Step 4 — Start content generation

curl -s -X POST "$BASE/books/$BOOK_ID/generate/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Length: 0"

# => 202 {"message":"Generation started","book_id":123,"task_id":"...","mode":"celery"}

A book must have an outline before this succeeds. Starting a second generation while one is already running returns 400.

Step 5 — Poll progress until it completes

while true; do
  STATUS=$(curl -s "$BASE/books/$BOOK_ID/generation-status/" \
             -H "Authorization: Bearer $TOKEN")
  echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('generation_status'), str(d.get('percentage'))+'%', d.get('status_message',''))"
  echo "$STATUS" | grep -q '"generation_status": *"completed"' && break
  sleep 20
done

Poll no more often than every 10–20 seconds. The response carries completed_chapters, total_chapters, percentage, a human-readable status_message, an outline array showing which chapters already have content, and generation_status, one of running, paused, stopped, completed or error. A book with no generation record yet returns 404.

Step 6 — Generate a cover, then export

# Optional: AI cover variants
curl -s -X POST "$BASE/books/$BOOK_ID/generate-cover/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{}'

# Export — note the UNDERSCORE in export_pdf, and the binary response body
curl -s -X POST "$BASE/books/$BOOK_ID/export_pdf/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"settings":{}}' \
  --output "book-$BOOK_ID.pdf"

file "book-$BOOK_ID.pdf"

Swap export_pdf for export_docx or export_epub for the other formats. Free-tier exports carry a watermark; paid plans do not.

Authentication in the KDPBot API

KDPBot uses JWT bearer tokens issued by djangorestframework-simplejwt. Send the access token on every authenticated request:

Authorization: Bearer <access_token>

There are no API keys and no OAuth client credentials flow. A token represents a user account, and every request is scoped to that user's own books and chapters.

Getting a token

POST /api/auth/login/
Accepts email or username, plus password. Returns {"user": {...}, "tokens": {"access": "...", "refresh": "..."}}.
POST /api/auth/register/
Creates the account and returns the same nested shape with 201. If email verification is enforced, tokens are withheld and email_verification_required: true is returned instead — verify the address before retrying.
POST /api/auth/google/
Exchanges a Google OAuth2 access token for a KDPBot token pair.

Token lifetimes and refresh

TokenLifetimeBehaviour
access24 hoursSent as Authorization: Bearer … on every protected call. Signed HS256.
refresh30 daysExchanged for a fresh access token. Rotation is enabled, so each refresh also returns a new refresh token — store it and discard the old one.
curl -s -X POST "$BASE/auth/token/refresh/" \
  -H "Content-Type: application/json" \
  -d '{"refresh":"<your_refresh_token>"}'

# => {"access":"<new access>","refresh":"<new refresh>"}   # flat, not nested

For a long-running job, refresh proactively — for example every few hours — rather than waiting for a 401. An expired or malformed token returns 401 with code: "token_not_valid". A logout endpoint exists for symmetry, but tokens are stateless: the practical way to sign out is to discard them client-side.

Full detail lives in /docs/authentication.md.

Machine-readable KDPBot API resources for agents and tooling

If you are an AI agent, an SDK generator or a crawler, start with these files rather than scraping this page. They are static, versioned alongside the site, and served from the same origin:

Machine-readable KDPBot developer resources, all listed in the KDPBot API catalog
ResourceFormatUse it for
/.well-known/api-catalogRFC 9727 linkset, application/linkset+jsonThe index of everything in this table, at a predictable URL. Start here when discovering KDPBot programmatically.
/openapi.jsonOpenAPI 3.1, JSONClient generation, request validation, agent tool definitions, Postman/Insomnia import.
/openapi.yamlOpenAPI 3.1, YAMLThe same contract for YAML-first toolchains and CI linting.
/llms.txtllmstxt.org MarkdownA compact index of what KDPBot is and where its canonical documents live.
/agents.mdMarkdownExtended agent guidance: when to reach for KDPBot, recipes, and constraints to respect.
/docs/mcp.mdMarkdownThe official KDPBot MCP server — install, client configuration, tool reference and a worked example.
/sitemap.xmlXMLEvery indexable KDPBot URL.
/robots.txtTextCrawl rules, including for AI crawlers.

KDPBot AI documentation — human-readable guides

KDPBot API error format

Errors are returned as JSON with an HTTP status code that carries the primary signal. The documented envelope is:

{
  "error": {
    "code": "quota_exceeded",
    "message": "You have used all books included in your current plan.",
    "status": 402,
    "details": {
      "quota_type": "books",
      "limit": 3,
      "used": 3,
      "remaining": 0
    },
    "documentation_url": "https://kdpbot.com/docs/errors.md#quota_exceeded"
  }
}
code
A stable, machine-readable identifier. Branch on this, never on the message text.
message
A human-readable sentence, safe to surface to an end user.
status
The HTTP status code, repeated in the body so it survives logging and message queues.
details
Optional structured context — the offending fields on a validation error, or the quota numbers on a 402.
documentation_url
A deep link into /docs/errors.md for that specific code.

Status codes you should handle

StatusMeaningWhat to do
400Invalid or missing fields, or an invalid state transition — for example generating content on a book with no outline.Fix the request. Do not retry unchanged.
401Missing, expired or malformed access token.Refresh the token once, then retry. If refresh also fails, re-authenticate.
402Plan quota exhausted, or the requested word count exceeds the plan limit.Stop. Upgrade at /pricing or buy credits.
403Authenticated but not permitted — including anti-abuse blocks on registration.Do not retry programmatically. Contact support if unexpected.
404No such resource, or it belongs to another account. Also returned by generation-status when no generation has ever been started for the book.Check the id and the owning account.
429Too many requests.Back off and retry after the interval given by Retry-After.
500Server-side failure, including an upstream model provider error during generation.Retry with exponential backoff. If it persists, email support@kdpbot.com with the request path and timestamp.

Compatibility note. Some older endpoints still answer with a flat {"error": "message text"} body, and framework-level failures may return {"detail": "…"} or a field-keyed validation map such as {"title": ["This field is required."]}. A robust client should read error.code when present, then fall back to error, then detail, and finally treat the object as a field-to-messages map. In all cases the HTTP status code is authoritative.

KDPBot API rate limits

Be a good citizen: run one book generation at a time per account, poll status no more than once every 10–20 seconds, and back off on any 429 or 5xx. Rate-limited responses expose standard headers:

HeaderMeaning
RateLimit-LimitRequests permitted in the current window.
RateLimit-RemainingRequests still available in the current window.
RateLimit-ResetSeconds until the window resets and the allowance is restored.
Retry-AfterSent with 429. Seconds to wait before retrying. Always honour this value over your own schedule.
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 120
RateLimit-Remaining: 0
RateLimit-Reset: 44
Retry-After: 44
Content-Type: application/json

{"error":{"code":"rate_limited","message":"Too many requests. Retry in 44 seconds.","status":429,"details":{"retry_after":44},"documentation_url":"https://kdpbot.com/docs/rate-limits.md"}}

Quotas are separate from rate limits

Rate limits govern request frequency. Quotas govern how much you may produce: books per period, images, and editing operations, all set by your subscription plan. Quota exhaustion is a 402, not a 429, and waiting will not clear it — the period must roll over, the plan must be upgraded, or credits must be purchased. Check your headroom before a long batch with GET /api/auth/subscriptions/current/, GET /api/auth/subscriptions/book-creation-options/ and GET /api/auth/credits/balance/. See /docs/rate-limits.md.

Sandbox — try the KDPBot API without an account

These endpoints are public: no API key, no token, no sign-up. They are the fastest way for an agent or a developer to confirm connectivity, inspect real response shapes, and check the error and rate-limit headers before writing any integration code.

EndpointReturns
GET /api/v1/books/demo/A complete demo book with chapters — the real response shape of a generated book.
GET /api/v1/auth/subscriptions/plans/All subscription plans with quotas and features.
GET /api/v1/auth/credits/packages/Pay-as-you-go credit packages and prices.
GET /api/v1/auth/addons/available/Purchasable add-ons.
GET /api/v1/public-settings/Public feature flags used by the web app.
# No credentials required — copy and run:
curl -s https://kdpbot.com/api/v1/auth/subscriptions/plans/

# Inspect the JSON error envelope on a protected endpoint:
curl -s https://kdpbot.com/api/v1/books/

# Inspect the rate-limit and version headers:
curl -sI https://kdpbot.com/api/v1/auth/subscriptions/plans/

Everything beyond this list requires authentication. There are no separate API keys to provision — see Authentication, or jump straight to the quickstart.

API credentials

KDPBot does not issue static API keys. Access is granted with short-lived JWT bearer tokens minted from the same account you use in the web app, which means a token can never outlive the account’s permissions or plan.

KDPBot API versioning and deprecation

The API is versioned in the URL path. The current version is v1 at https://kdpbot.com/api/v1. The unversioned /api/ prefix is a permanent alias of the current version, kept for existing clients — new integrations should pin /api/v1/.

Every response carries X-API-Version and X-API-Version-Latest. A version scheduled for removal additionally returns Deprecation, Sunset (RFC 8594) and Link: <…>; rel="deprecation", with at least six months of notice. Full policy: versioning and deprecation.

KDPBot API endpoint reference

All paths are relative to https://kdpbot.com/api/v1. The trailing slash is part of the path. “Auth” means an Authorization: Bearer access token is required.

Authentication endpoints

Accounts, sessions and profile
MethodPathAuthDescription
POST/api/auth/register/PublicCreate an account. Returns the user plus a nested tokens pair, unless email verification is required.
POST/api/auth/login/PublicLog in with email or username and password. Returns the user (with subscription) and a token pair.
POST/api/auth/token/refresh/PublicExchange a refresh token for a new access token. Rotation returns a new refresh token too.
POST/api/auth/google/PublicSign in or sign up with a Google OAuth2 access token.
GET/api/auth/profile/JWTThe current user's profile, including the nested subscription and its plan feature map.
PATCH/api/auth/profile/JWTUpdate first_name, last_name or email.
POST/api/auth/change-password/JWTChange the password of the authenticated user.
POST/api/auth/forgot-password/PublicSend a password-reset email.
POST/api/auth/reset-password/{uid}/{token}/PublicComplete a password reset using the uid and token from the emailed link.
GET/api/auth/verify-email/{uid}/{token}/PublicVerify an email address from the registration link.
POST/api/auth/logout/PublicSymmetric logout endpoint. Tokens are stateless — discard them client-side.

Books endpoints

Book lifecycle: create, outline, generate, illustrate, export
MethodPathAuthDescription
GET/api/books/JWTList the caller's books, newest update first. Bare JSON array, with chapters_count and word_count annotated.
POST/api/books/JWTCreate a book. Returns 402 quota_exceeded when the plan's book allowance is spent.
GET/api/books/{id}/JWTRetrieve one book with all chapters, generation progress and cover fields.
PATCH/api/books/{id}/JWTPartially update book metadata.
DELETE/api/books/{id}/JWTDelete a book. Cascades to its chapters, progress record and saved covers.
GET/api/books/dropdown/JWTLightweight selector list: id and title only.
POST/api/books/{id}/generate-outline/JWTSynchronously generate the chapter outline. Replaces existing chapters and sets status to outline.
POST/api/books/{id}/update-outline/JWTPersist your edits to chapter titles and summaries.
POST/api/books/{id}/generate/JWTStart asynchronous generation of all chapter content. Returns 202 with a task id.
GET/api/books/{id}/generation-status/JWTPoll progress: percentage, completed chapters, status message and per-chapter outline state.
POST/api/books/{id}/pause-generation/JWTPause a running generation.
POST/api/books/{id}/generate-cover/JWTGenerate AI cover variants for the book.
POST/api/books/{id}/export_pdf/JWTExport as PDF. Binary application/pdf body. Note the underscore.
POST/api/books/{id}/export_docx/JWTExport as DOCX. Binary body.
POST/api/books/{id}/export_epub/JWTExport as EPUB. Binary body.
GET/api/books/{id}/preview-data/JWTAggregated pages, cover URLs and trim size used by the book preview.
POST/api/books/{id}/duplicate/JWTDeep-copy a book and its chapters into a new draft.
GET/api/books/demo/PublicThe public demo book used on the marketing site — handy for testing a client without an account.

Chapters endpoints

Chapter content, regeneration and illustrations
MethodPathAuthDescription
GET/api/chapters/JWTEvery chapter across all of the caller's books.
GET/api/chapters/{id}/JWTRetrieve a single chapter with its content.
PATCH/api/chapters/{id}/JWTUpdate chapter content or title. This is the editor's save path — use it to write edits back.
POST/api/chapters/{id}/regenerate/JWTSynchronously rewrite this one chapter with the model and save the result.
POST/api/chapters/{id}/generate-image/JWTGenerate an AI illustration for the chapter.

Subscriptions and add-ons endpoints

Plans, quota usage and Stripe billing
MethodPathAuthDescription
GET/api/auth/subscriptions/plans/PublicAll active subscription plans with their limits and features.
GET/api/auth/subscriptions/current/JWTThe caller's subscription, this period's quota usage and the plan's limit map.
GET/api/auth/subscriptions/usage/JWTUsage history: the current period plus recent past periods.
GET/api/auth/subscriptions/book-creation-options/JWTWhether a new book can be started right now, and whether it would draw on plan quota or credits.
POST/api/auth/subscriptions/checkout/JWTCreate a Stripe Checkout session for a paid plan.
POST/api/auth/subscriptions/portal/JWTCreate a Stripe Customer Portal session and return its URL.
GET/api/auth/subscriptions/payments/JWTRecent payment and invoice history.
GET/api/auth/addons/available/PublicActive add-ons, such as Autopilot.
GET/api/auth/addons/my_addons/JWTThe caller's active add-ons. Note the underscore in the path.
POST/api/auth/addons/checkout/JWTPurchase an add-on via Stripe Checkout.

Credits endpoints

Pay-as-you-go credit balance and purchases
MethodPathAuthDescription
GET/api/auth/credits/packages/PublicActive pay-as-you-go credit packages in display order.
GET/api/auth/credits/balance/JWTThe caller's current credit balance.
GET/api/auth/credits/transactions/JWTCredit history — purchases, usage, refunds and bonuses, newest first.
POST/api/auth/credits/checkout/JWTCreate a Stripe Checkout session to buy a credit package.
POST/api/auth/credits/guest-checkout/PublicBuy credits without an account.

Utility endpoints

Statistics, public settings and support
MethodPathAuthDescription
GET/api/stats/JWTDashboard statistics for the authenticated user.
GET/api/public-settings/PublicPublic site settings and feature toggles.
POST/api/chatbot/ask/PublicAsk the KDPBot support assistant a question about the product.

Support and contact

Questions about the KDPBot API, a bug in a response shape, or a quota that looks wrong? Reach a human on any of these channels — email is best for anything that needs a request path, timestamp and payload attached.

Elsewhere on KDPBot