Automate Your Self-Publishing Pipeline with APIs

7 min read

Self-Publishing Does Not Scale

Publishing one book is a project. Publishing ten is a business. Publishing a hundred is a logistics problem that most toolchains were never designed for.

For a single title, the manual workflow is manageable. Write the manuscript in Word or Scrivener. Export to PDF for print, EPUB for ebook. Design the cover in Canva or Photoshop. Create a cover spread with spine calculations done by hand in a template. Build an Amazon A+ Content banner in a separate tool. Resize the cover for Instagram. Make a story-format promo for TikTok. Upload each asset to the correct platform with the correct dimensions.

That is five or six different tools for one book. Now multiply by your catalog. Every new title needs the same set of assets. Every rebranding, every series update, every price change means touching every asset for every affected title. The work scales linearly with your catalog size, and none of it is the creative work you actually care about.

The self-publishing ecosystem has a tool for everything and a pipeline for nothing.

Five API Calls, One Pipeline

Iteration Layer provides the building blocks to replace that entire workflow with code. Two APIs cover the full pipeline:

  • Document Generation API — generates print-ready PDFs and EPUBs from structured content. Custom page sizes, proper typography, table of contents, page breaks.
  • Image Generation API — composes images from layers: backgrounds, text, images, barcodes. Any dimension, any layout.

For a single title — say, The Long Silence by Elizabeth Ashworth — the pipeline looks like this:

  1. Generate the manuscript PDF (Document Generation)
  2. Generate the front cover image (Image Generation)
  3. Generate the print cover spread with spine (Image Generation)
  4. Generate an Amazon A+ Content banner (Image Generation)
  5. Generate a social media story promo (Image Generation)

Five API calls. Five assets. No Photoshop, no Canva, no manual resizing. And because it is code, you can run the same pipeline for every title in your catalog by changing the input data.

Step 1: Generate the Manuscript

The manuscript is where most self-publishing pipelines start to break. KDP requires a PDF at a specific trim size — 6x9 inches is the most common — with specific margin requirements that differ for left and right pages. Most word processors export PDFs that are close enough to pass validation but not close enough to look professional.

The Document Generation API takes structured content and produces a PDF with exact page dimensions. The custom page size accepts points directly — 432x648 pt is exactly 6x9 inches. The left margin is wider than the right to account for the binding gutter, which is a KDP requirement that trips up authors who use symmetric margins.

Request
curl -X POST https://api.iterationlayer.com/document-generation/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "pdf",
    "document": {
      "metadata": {
        "title": "The Long Silence",
        "author": "Elizabeth Ashworth"
      },
      "page": {
        "size": {
          "custom": {
              "width_in_pt": 432,
              "height_in_pt": 648,
          }
        },
        "margins": {
          "top_in_pt": 72,
          "right_in_pt": 63,
          "bottom_in_pt": 72,
          "left_in_pt": 81
        }
      },
      "styles": {
        "text": {
          "font_family": "Lora",
          "font_size_in_pt": 11.0,
          "line_height": 1.6,
          "color": "#1A1A1A"
        },
        "headline": {
          "font_family": "PlayfairDisplay",
          "font_size_in_pt": 28.0,
          "color": "#111111",
          "is_bold": true
        }
      },
      "footer": [
        {
          "type": "paragraph",
          "runs": [
            {
              "text": "— {{page_number}} —"
            }
          ]
        }
      ],
      "content": [
        {
          "type": "headline",
          "level": "h1",
          "text": "The Long Silence"
        },
        {
          "type": "paragraph",
          "runs": [
            {
                "text": "Elizabeth Ashworth",
                "is_bold": true,
            }
          ]
        },
        {
          "type": "separator"
        },
        {
          "type": "paragraph",
          "runs": [
            {
              "text": "Copyright © 2026 Elizabeth Ashworth. All rights reserved."
            }
          ]
        },
        {
          "type": "page-break"
        },
        {
          "type": "table-of-contents",
          "levels": ["h1"],
          "leader": "dots"
        },
        {
          "type": "page-break"
        },
        {
          "type": "headline",
          "level": "h1",
          "text": "Chapter 1: The Letter"
        },
        {
          "type": "paragraph",
          "markdown": "The envelope arrived on a Tuesday, which Eleanor would later consider the cruelest part..."
        },
        {
          "type": "page-break"
        },
        {
          "type": "headline",
          "level": "h1",
          "text": "Chapter 2: Meridian Street"
        },
        {
          "type": "paragraph",
          "markdown": "The drive from campus to Millhaven took three hours..."
        }
      ]
    }
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "JVBERi0xLjQKJcOkw7zD...",
    "mime_type": "application/pdf"
  }
}
Request
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({
  apiKey: "YOUR_API_KEY"
});

const manuscript = await client.generateDocument({
  format: "pdf",
  document: {
    metadata: {
      title: "The Long Silence",
      author: "Elizabeth Ashworth",
    },
    page: {
      size: { custom: {
        width_in_pt: 432,
        height_in_pt: 648,
      } },
      margins: {
        top_in_pt: 72,
        right_in_pt: 63,
        bottom_in_pt: 72,
        left_in_pt: 81,
      },
    },
    styles: {
      text: {
        font_family: "Lora",
        font_size_in_pt: 11.0,
        line_height: 1.6,
        color: "#1A1A1A",
      },
      headline: {
        font_family: "PlayfairDisplay",
        font_size_in_pt: 28.0,
        color: "#111111",
        is_bold: true,
      },
    },
    footer: [
      {
        type: "paragraph",
        runs: [{ text: "— {{page_number}} —" }],
      },
    ],
    content: [
      {
        type: "headline",
        level: "h1",
        text: "The Long Silence",
      },
      {
        type: "paragraph",
        runs: [{
            text: "Elizabeth Ashworth",
            is_bold: true,
        }],
      },
      {
          type: "separator"
      },
      {
        type: "paragraph",
        runs: [{
            text: "Copyright © 2026 Elizabeth Ashworth. All rights reserved."
        }],
      },
      {
          type: "page-break"
      },
      {
        type: "table-of-contents",
        levels: ["h1"],
        leader: "dots",
      },
      {
          type: "page-break"
      },
      {
        type: "headline",
        level: "h1",
        text: "Chapter 1: The Letter",
      },
      {
        type: "paragraph",
        markdown: "The envelope arrived on a Tuesday, which Eleanor would later consider the cruelest part...",
      },
      {
          type: "page-break"
      },
      {
        type: "headline",
        level: "h1",
        text: "Chapter 2: Meridian Street",
      },
      {
        type: "paragraph",
        markdown: "The drive from campus to Millhaven took three hours...",
      },
    ],
  },
});
Response
{
  "success": true,
  "data": {
    "buffer": "JVBERi0xLjQKJcOkw7zD...",
    "mime_type": "application/pdf"
  }
}
Request
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

manuscript = client.generate_document(
    format="pdf",
    document={
        "metadata": {
            "title": "The Long Silence",
            "author": "Elizabeth Ashworth",
        },
        "page": {
            "size": {"custom": {
                "width_in_pt": 432,
                "height_in_pt": 648,
            }},
            "margins": {
                "top_in_pt": 72,
                "right_in_pt": 63,
                "bottom_in_pt": 72,
                "left_in_pt": 81,
            },
        },
        "styles": {
            "text": {
                "font_family": "Lora",
                "font_size_in_pt": 11.0,
                "line_height": 1.6,
                "color": "#1A1A1A",
            },
            "headline": {
                "font_family": "PlayfairDisplay",
                "font_size_in_pt": 28.0,
                "color": "#111111",
                "is_bold": True,
            },
        },
        "footer": [
            {
                "type": "paragraph",
                "runs": [{"text": "{{page_number}}"}],
            },
        ],
        "content": [
            {
                "type": "headline",
                "level": "h1",
                "text": "The Long Silence",
            },
            {
                "type": "paragraph",
                "runs": [{
                    "text": "Elizabeth Ashworth",
                    "is_bold": True,
                }],
            },
            {
                "type": "separator"
            },
            {
                "type": "paragraph",
                "runs": [{
                    "text": "Copyright © 2026 Elizabeth Ashworth. All rights reserved."
                }],
            },
            {
                "type": "page-break"
            },
            {
                "type": "table-of-contents",
                "levels": ["h1"],
                "leader": "dots",
            },
            {
                "type": "page-break"
            },
            {
                "type": "headline",
                "level": "h1",
                "text": "Chapter 1: The Letter",
            },
            {
                "type": "paragraph",
                "markdown": "The envelope arrived on a Tuesday, which Eleanor would later consider the cruelest part...",
            },
            {
                "type": "page-break"
            },
            {
                "type": "headline",
                "level": "h1",
                "text": "Chapter 2: Meridian Street",
            },
            {
                "type": "paragraph",
                "markdown": "The drive from campus to Millhaven took three hours...",
            },
        ],
    },
)
Response
{
  "success": true,
  "data": {
    "buffer": "JVBERi0xLjQKJcOkw7zD...",
    "mime_type": "application/pdf"
  }
}
Request
package main

import il "github.com/iterationlayer/sdk-go"

func main() {
    client := il.NewClient("YOUR_API_KEY")

    result, err := client.GenerateDocument(il.GenerateDocumentRequest{
        Format: "pdf",
        Document: il.DocumentDefinition{
            Metadata: il.DocumentMetadata{
                Title:  "The Long Silence",
                Author: "Elizabeth Ashworth",
            },
            Page: il.PageConfig{
                Size: il.PageSize{Custom: &il.CustomPageSize{
                    WidthInPt:  432,
                    HeightInPt: 648,
                }},
                Margins: il.Margins{
                    TopInPt: 72, RightInPt: 63, BottomInPt: 72, LeftInPt: 81,
                },
            },
            Content: []il.ContentBlock{
                il.NewHeadlineBlock("h1", "The Long Silence"),
                il.ParagraphBlock{
      Type: "paragraph",
      Markdown: "**Elizabeth Ashworth**",
    },
                il.NewSeparatorBlock(),
                il.NewPageBreakBlock(),
                il.NewHeadlineBlock("h1", "Chapter 1: The Letter"),
                il.ParagraphBlock{
      Type: "paragraph",
      Markdown: "The envelope arrived on a Tuesday...",
    },
                il.NewPageBreakBlock(),
                il.NewHeadlineBlock("h1", "Chapter 2: Meridian Street"),
                il.ParagraphBlock{
      Type: "paragraph",
      Markdown: "The drive from campus to Millhaven took three hours...",
    },
            },
        },
    })
    if err != nil {
        panic(err)
    }
}
Response
{
  "success": true,
  "data": {
    "buffer": "JVBERi0xLjQKJcOkw7zD...",
    "mime_type": "application/pdf"
  }
}

The serif body font (Lora) at 11pt with 1.6 line height produces comfortable reading density for a print novel. See the full Generate PDF Manuscript recipe for the complete request including all styles and chapter content.

Step 2: Generate the Front Cover

The front cover is a standalone image — the ebook thumbnail, the Amazon listing image, the thing readers see first. The Image Generation API composes the cover from layers: a gradient background, the title text, the author name, and optionally a cover image or decorative elements. One API call, one image.

The key dimensions are 1600x2560 for Amazon’s high-resolution requirement. Here is a minimal cover with a gradient background, title, and author name:

Request
curl -X POST https://api.iterationlayer.com/image-generation/v1/render \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": {
        "width": 1600,
        "height": 2560,
    },
    "output_format": "png",
    "layers": [
      {
        "index": 0,
        "type": "gradient",
        "gradient_type": "linear",
        "angle_in_degrees": 180.0,
        "colors": [
          {
              "hex_color": "#1B1464",
              "position": 0.0,
          },
          {
              "hex_color": "#6C63FF",
              "position": 100.0,
          }
        ],
        "position": {
            "x": 0.0,
            "y": 0.0,
        },
        "dimensions": {
            "width": 1600,
            "height": 2560,
        }
      },
      {
        "index": 1,
        "type": "solid-color",
        "hex_color": "#A8A29E",
        "position": {
            "x": 200.0,
            "y": 700.0,
        },
        "dimensions": {
            "width": 1200,
            "height": 2,
        }
      },
      {
        "index": 2,
        "type": "text",
        "text": "THE LONG\nSILENCE",
        "font_name": "PlayfairDisplay",
        "font_weight": "Bold",
        "font_size_in_px": 140,
        "text_color": "#FFFFFF",
        "text_align": "center",
        "position": {
            "x": 100.0,
            "y": 800.0,
        },
        "dimensions": {
            "width": 1400,
            "height": 500,
        }
      },
      {
        "index": 3,
        "type": "text",
        "text": "A Novel",
        "font_name": "PlayfairDisplay",
        "font_size_in_px": 42,
        "text_color": "#A8A29E",
        "text_align": "center",
        "position": {
            "x": 100.0,
            "y": 1350.0,
        },
        "dimensions": {
            "width": 1400,
            "height": 80,
        }
      },
      {
        "index": 4,
        "type": "solid-color",
        "hex_color": "#A8A29E",
        "position": {
            "x": 200.0,
            "y": 1900.0,
        },
        "dimensions": {
            "width": 1200,
            "height": 2,
        }
      },
      {
        "index": 5,
        "type": "text",
        "text": "ELIZABETH ASHWORTH",
        "font_name": "Lato",
        "font_size_in_px": 46,
        "text_color": "#D6D3D1",
        "text_align": "center",
        "position": {
            "x": 100.0,
            "y": 2000.0,
        },
        "dimensions": {
            "width": 1400,
            "height": 80,
        }
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({
  apiKey: "YOUR_API_KEY"
});

const frontCover = await client.generateImage({
  dimensions: {
    width_in_px: 1600,
    height_in_px: 2560,
  },
  output_format: "png",
  layers: [
    {
      index: 0,
      type: "gradient",
      gradient_type: "linear",
      angle_in_degrees: 180.0,
      colors: [
        {
            hex_color: "#1B1464",
            position: 0.0,
        },
        {
            hex_color: "#6C63FF",
            position: 100.0,
        },
      ],
      position: {
        x: 0.0,
        y: 0.0,
      },
      dimensions: {
        width_in_px: 1600,
        height_in_px: 2560,
      },
    },
    {
      index: 1,
      type: "solid-color",
      hex_color: "#A8A29E",
      position: {
        x: 200.0,
        y: 700.0,
      },
      dimensions: {
        width_in_px: 1200,
        height_in_px: 2,
      },
    },
    {
      index: 2,
      type: "text",
      text: "THE LONG\nSILENCE",
      font_name: "PlayfairDisplay",
      font_weight: "Bold",
      font_size_in_px: 140,
      text_color: "#FFFFFF",
      text_align: "center",
      position: {
        x: 100.0,
        y: 800.0,
      },
      dimensions: {
        width_in_px: 1400,
        height_in_px: 500,
      },
    },
    {
      index: 3,
      type: "text",
      text: "A Novel",
      font_name: "PlayfairDisplay",
      font_size_in_px: 42,
      text_color: "#A8A29E",
      text_align: "center",
      position: {
        x: 100.0,
        y: 1350.0,
      },
      dimensions: {
        width_in_px: 1400,
        height_in_px: 80,
      },
    },
    {
      index: 4,
      type: "solid-color",
      hex_color: "#A8A29E",
      position: {
        x: 200.0,
        y: 1900.0,
      },
      dimensions: {
        width_in_px: 1200,
        height_in_px: 2,
      },
    },
    {
      index: 5,
      type: "text",
      text: "ELIZABETH ASHWORTH",
      font_name: "Lato",
      font_size_in_px: 46,
      text_color: "#D6D3D1",
      text_align: "center",
      position: {
        x: 100.0,
        y: 2000.0,
      },
      dimensions: {
        width_in_px: 1400,
        height_in_px: 80,
      },
    },
  ],
});

const frontCoverBase64 = frontCover.data.buffer;
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

front_cover = client.generate_image(
    dimensions={
        "width_in_px": 1600,
        "height_in_px": 2560,
    },
    output_format="png",
    layers=[
        {
            "index": 0,
            "type": "gradient",
            "gradient_type": "linear",
            "angle_in_degrees": 180.0,
            "colors": [
                {
                    "hex_color": "#1B1464",
                    "position": 0.0,
                },
                {
                    "hex_color": "#6C63FF",
                    "position": 100.0,
                },
            ],
            "position": {
                "x": 0.0,
                "y": 0.0,
            },
            "dimensions": {
                "width_in_px": 1600,
                "height_in_px": 2560,
            },
        },
        {
            "index": 1,
            "type": "solid-color",
            "hex_color": "#A8A29E",
            "position": {
                "x": 200.0,
                "y": 700.0,
            },
            "dimensions": {
                "width_in_px": 1200,
                "height_in_px": 2,
            },
        },
        {
            "index": 2,
            "type": "text",
            "text": "THE LONG\nSILENCE",
            "font_name": "PlayfairDisplay",
            "font_weight": "Bold",
            "font_size_in_px": 140,
            "text_color": "#FFFFFF",
            "text_align": "center",
            "position": {
                "x": 100.0,
                "y": 800.0,
            },
            "dimensions": {
                "width_in_px": 1400,
                "height_in_px": 500,
            },
        },
        {
            "index": 3,
            "type": "text",
            "text": "A Novel",
            "font_name": "PlayfairDisplay",
            "font_size_in_px": 42,
            "text_color": "#A8A29E",
            "text_align": "center",
            "position": {
                "x": 100.0,
                "y": 1350.0,
            },
            "dimensions": {
                "width_in_px": 1400,
                "height_in_px": 80,
            },
        },
        {
            "index": 4,
            "type": "solid-color",
            "hex_color": "#A8A29E",
            "position": {
                "x": 200.0,
                "y": 1900.0,
            },
            "dimensions": {
                "width_in_px": 1200,
                "height_in_px": 2,
            },
        },
        {
            "index": 5,
            "type": "text",
            "text": "ELIZABETH ASHWORTH",
            "font_name": "Lato",
            "font_size_in_px": 46,
            "text_color": "#D6D3D1",
            "text_align": "center",
            "position": {
                "x": 100.0,
                "y": 2000.0,
            },
            "dimensions": {
                "width_in_px": 1400,
                "height_in_px": 80,
            },
        },
    ],
)

front_cover_base64 = front_cover["data"]["buffer"]
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
package main

import il "github.com/iterationlayer/sdk-go"

func main() {
    client := il.NewClient("YOUR_API_KEY")

    result, err := client.GenerateImage(il.GenerateImageRequest{
        Dimensions:   il.Dimensions{
    WidthInPx: 1600,
    HeightInPx: 2560,
  },
        OutputFormat: "png",
        Layers: []il.Layer{
            il.NewGradientLayer(0, "linear", []il.GradientColor{
                {
      HexColor: "#1B1464",
      Position: 0.0,
    },
                {
      HexColor: "#6C63FF",
      Position: 100.0,
    },
            }, il.Position{
     X: 0.0,
     Y: 0.0,
   }, il.Dimensions{
     WidthInPx: 1600,
     HeightInPx: 2560,
   }),
            il.NewRectangleLayer(1, "#A8A29E",
                il.Position{
      X: 200.0,
      Y: 700.0,
    },
                il.Dimensions{
      WidthInPx: 1200,
      HeightInPx: 2,
    }),
            il.NewTextLayer(2, "THE LONG\nSILENCE", "PlayfairDisplay", 140, "#FFFFFF",
                il.Position{
      X: 100.0,
      Y: 800.0,
    },
                il.Dimensions{
      WidthInPx: 1400,
      HeightInPx: 500,
    }),
            il.NewTextLayer(3, "A Novel", "PlayfairDisplay", 42, "#A8A29E",
                il.Position{
      X: 100.0,
      Y: 1350.0,
    },
                il.Dimensions{
      WidthInPx: 1400,
      HeightInPx: 80,
    }),
            il.NewRectangleLayer(4, "#A8A29E",
                il.Position{
      X: 200.0,
      Y: 1900.0,
    },
                il.Dimensions{
      WidthInPx: 1200,
      HeightInPx: 2,
    }),
            il.NewTextLayer(5, "ELIZABETH ASHWORTH", "Lato", 46, "#D6D3D1",
                il.Position{
      X: 100.0,
      Y: 2000.0,
    },
                il.Dimensions{
      WidthInPx: 1400,
      HeightInPx: 80,
    }),
        },
    })
    if err != nil {
        panic(err)
    }

    frontCoverBase64 := result.Data.Buffer
}
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}

The cover output feeds into subsequent steps as an image layer. See the Generate Front Book Cover recipe for more variations including cover images and custom fonts.

Step 3: Generate the Print Cover Spread

For print-on-demand, you need a single image that wraps around the entire book: back cover, spine, front cover. The spine width depends on the page count — KDP uses approximately 0.75 pixels per page at 300 DPI. Getting the spine width wrong by even a few pixels means KDP rejects the file. Calculating it in code from the page count eliminates the guesswork.

Request
curl -X POST https://api.iterationlayer.com/image-generation/v1/render \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": {
        "width": 3826,
        "height": 2775,
    },
    "output_format": "png",
    "layers": [
      {
        "index": 0,
        "type": "solid-color",
        "hex_color": "#1B1464"
      },
      {
        "index": 1,
        "type": "image",
        "buffer": "<base64-encoded-front-cover>",
        "position": {
            "x": 1988.0,
            "y": 0.0,
        },
        "dimensions": {
            "width": 1838,
            "height": 2775,
        }
      },
      {
        "index": 2,
        "type": "text",
        "text": "The Long Silence",
        "font_name": "PlayfairDisplay",
        "font_size_in_px": 72,
        "text_color": "#FFFFFF",
        "font_weight": "Bold",
        "text_align": "center",
        "position": {
            "x": 150.0,
            "y": 325.0,
        },
        "dimensions": {
            "width": 1575,
            "height": 150,
        }
      },
      {
        "index": 3,
        "type": "text",
        "text": "A rogue AI researcher discovers that the optimization algorithm she created has already escaped the lab.",
        "font_name": "Lora",
        "font_size_in_px": 28,
        "text_color": "#CBD5E1",
        "text_align": "center",
        "position": {
            "x": 200.0,
            "y": 600.0,
        },
        "dimensions": {
            "width": 1475,
            "height": 400,
        }
      },
      {
        "index": 4,
        "type": "solid-color",
        "hex_color": "#FFFFFF",
        "position": {
            "x": 225.0,
            "y": 2250.0,
        },
        "dimensions": {
            "width": 450,
            "height": 350,
        }
      },
      {
        "index": 5,
        "type": "barcode",
        "value": "9781234567890",
        "format": "ean13",
        "position": {
            "x": 250.0,
            "y": 2275.0,
        },
        "dimensions": {
            "width": 400,
            "height": 250,
        },
        "foreground_hex_color": "#000000",
        "background_hex_color": "#FFFFFF"
      },
      {
        "index": 6,
        "type": "text",
        "text": "The Long Silence",
        "font_name": "Montserrat",
        "font_size_in_px": 24,
        "text_color": "#FFFFFF",
        "font_weight": "Bold",
        "text_align": "center",
        "vertical_align": "center",
        "rotation_in_degrees": -90,
        "position": {
            "x": 1838.0,
            "y": 375.0,
        },
        "dimensions": {
            "width": 150,
            "height": 2025,
        }
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({
  apiKey: "YOUR_API_KEY"
});

const BLEED_IN_PX = 38;
const TRIM_WIDTH_IN_PX = 1800;
const TRIM_HEIGHT_IN_PX = 2700;
const PAGE_COUNT = 280;
const SPINE_WIDTH_IN_PX = Math.round(PAGE_COUNT * 0.75);
const COVER_WIDTH_IN_PX = TRIM_WIDTH_IN_PX + BLEED_IN_PX;
const TOTAL_WIDTH_IN_PX = COVER_WIDTH_IN_PX * 2 + SPINE_WIDTH_IN_PX;
const TOTAL_HEIGHT_IN_PX = TRIM_HEIGHT_IN_PX + BLEED_IN_PX * 2;
const SPINE_X_IN_PX = COVER_WIDTH_IN_PX;
const FRONT_COVER_X_IN_PX = COVER_WIDTH_IN_PX + SPINE_WIDTH_IN_PX;

const coverSpread = await client.generateImage({
  dimensions: {
    width_in_px: TOTAL_WIDTH_IN_PX,
    height_in_px: TOTAL_HEIGHT_IN_PX,
  },
  output_format: "png",
  layers: [
    {
      index: 0,
      type: "solid-color",
      hex_color: "#1B1464",
    },
    {
      index: 1,
      type: "image",
      buffer: frontCoverBase64,
      position: {
        x: FRONT_COVER_X_IN_PX,
        y: 0,
      },
      dimensions: {
        width_in_px: COVER_WIDTH_IN_PX,
        height_in_px: TOTAL_HEIGHT_IN_PX,
      },
    },
    {
      index: 2,
      type: "text",
      text: "The Long Silence",
      font_name: "PlayfairDisplay",
      font_size_in_px: 72,
      text_color: "#FFFFFF",
      font_weight: "Bold",
      text_align: "center",
      position: {
        x: 150.0,
        y: 325.0,
      },
      dimensions: {
        width_in_px: 1575,
        height_in_px: 150,
      },
    },
    {
      index: 3,
      type: "text",
      text: "A rogue AI researcher discovers that the optimization algorithm she created has already escaped the lab.",
      font_name: "Lora",
      font_size_in_px: 28,
      text_color: "#CBD5E1",
      text_align: "center",
      position: {
        x: 200.0,
        y: 600.0,
      },
      dimensions: {
        width_in_px: 1475,
        height_in_px: 400,
      },
    },
    {
      index: 4,
      type: "solid-color",
      hex_color: "#FFFFFF",
      position: {
        x: 225.0,
        y: 2250.0,
      },
      dimensions: {
        width_in_px: 450,
        height_in_px: 350,
      },
    },
    {
      index: 5,
      type: "barcode",
      value: "9781234567890",
      format: "ean13",
      position: {
        x: 250.0,
        y: 2275.0,
      },
      dimensions: {
        width_in_px: 400,
        height_in_px: 250,
      },
      foreground_hex_color: "#000000",
      background_hex_color: "#FFFFFF",
    },
    {
      index: 6,
      type: "text",
      text: "The Long Silence",
      font_name: "Montserrat",
      font_size_in_px: 24,
      text_color: "#FFFFFF",
      font_weight: "Bold",
      text_align: "center",
      vertical_align: "center",
      rotation_in_degrees: -90,
      position: {
        x: SPINE_X_IN_PX,
        y: 375.0,
      },
      dimensions: {
        width_in_px: SPINE_WIDTH_IN_PX,
        height_in_px: 2025,
      },
    },
  ],
});
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
import math

from iterationlayer import IterationLayer
client = IterationLayer(api_key="YOUR_API_KEY")

bleed_in_px = 38
trim_width_in_px = 1800
trim_height_in_px = 2700
page_count = 280
spine_width_in_px = round(page_count * 0.75)
cover_width_in_px = trim_width_in_px + bleed_in_px
total_width_in_px = cover_width_in_px * 2 + spine_width_in_px
total_height_in_px = trim_height_in_px + bleed_in_px * 2
spine_x_in_px = cover_width_in_px
front_cover_x_in_px = cover_width_in_px + spine_width_in_px

cover_spread = client.generate_image(
    dimensions={
        "width_in_px": total_width_in_px,
        "height_in_px": total_height_in_px,
    },
    output_format="png",
    layers=[
        {
            "index": 0,
            "type": "solid-color",
            "hex_color": "#1B1464",
        },
        {
            "index": 1,
            "type": "image",
            "buffer": front_cover_base64,
            "position": {
                "x": front_cover_x_in_px,
                "y": 0,
            },
            "dimensions": {
                "width_in_px": cover_width_in_px,
                "height_in_px": total_height_in_px,
            },
        },
        {
            "index": 2,
            "type": "text",
            "text": "The Long Silence",
            "font_name": "PlayfairDisplay",
            "font_size_in_px": 72,
            "text_color": "#FFFFFF",
            "font_weight": "Bold",
            "text_align": "center",
            "position": {
                "x": 150.0,
                "y": 325.0,
            },
            "dimensions": {
                "width_in_px": 1575,
                "height_in_px": 150,
            },
        },
        {
            "index": 3,
            "type": "text",
            "text": "A rogue AI researcher discovers that the optimization algorithm she created has already escaped the lab.",
            "font_name": "Lora",
            "font_size_in_px": 28,
            "text_color": "#CBD5E1",
            "text_align": "center",
            "position": {
                "x": 200.0,
                "y": 600.0,
            },
            "dimensions": {
                "width_in_px": 1475,
                "height_in_px": 400,
            },
        },
        {
            "index": 4,
            "type": "solid-color",
            "hex_color": "#FFFFFF",
            "position": {
                "x": 225.0,
                "y": 2250.0,
            },
            "dimensions": {
                "width_in_px": 450,
                "height_in_px": 350,
            },
        },
        {
            "index": 5,
            "type": "barcode",
            "value": "9781234567890",
            "format": "ean13",
            "position": {
                "x": 250.0,
                "y": 2275.0,
            },
            "dimensions": {
                "width_in_px": 400,
                "height_in_px": 250,
            },
            "foreground_hex_color": "#000000",
            "background_hex_color": "#FFFFFF",
        },
        {
            "index": 6,
            "type": "text",
            "text": "The Long Silence",
            "font_name": "Montserrat",
            "font_size_in_px": 24,
            "text_color": "#FFFFFF",
            "font_weight": "Bold",
            "text_align": "center",
            "vertical_align": "center",
            "rotation_in_degrees": -90,
            "position": {
                "x": spine_x_in_px,
                "y": 375.0,
            },
            "dimensions": {
                "width_in_px": spine_width_in_px,
                "height_in_px": 2025,
            },
        },
    ],
)
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
package main

import (
    "math"

    il "github.com/iterationlayer/sdk-go"
)

func main() {
    client := il.NewClient("YOUR_API_KEY")

    bleedInPx := 38
    trimWidthInPx := 1800
    trimHeightInPx := 2700
    pageCount := 280
    spineWidthInPx := int(math.Round(float64(pageCount) * 0.75))
    coverWidthInPx := trimWidthInPx + bleedInPx
    totalWidthInPx := coverWidthInPx*2 + spineWidthInPx
    totalHeightInPx := trimHeightInPx + bleedInPx*2
    spineXInPx := coverWidthInPx
    frontCoverXInPx := coverWidthInPx + spineWidthInPx

    result, err := client.GenerateImage(il.GenerateImageRequest{
        Dimensions:   il.Dimensions{
    WidthInPx: totalWidthInPx,
    HeightInPx: totalHeightInPx,
  },
        OutputFormat: "png",
        Layers: []il.Layer{
            il.NewSolidColorBackgroundLayer(0, "#1B1464"),
            il.NewStaticImageLayer(1, frontCoverBase64,
                il.Position{
      XInPx: float64(frontCoverXInPx),
      YInPx: 0,
    },
                il.Dimensions{
      WidthInPx: coverWidthInPx,
      HeightInPx: totalHeightInPx,
    }),
            il.NewTextLayer(2, "The Long Silence", "PlayfairDisplay", 72, "#FFFFFF",
                il.Position{
      XInPx: 150,
      YInPx: 325,
    },
                il.Dimensions{
      WidthInPx: 1575,
      HeightInPx: 150,
    }),
            il.NewTextLayer(3,
                "A rogue AI researcher discovers that the optimization algorithm she created has already escaped the lab.",
                "Lora", 28, "#CBD5E1",
                il.Position{
      XInPx: 200,
      YInPx: 600,
    },
                il.Dimensions{
      WidthInPx: 1475,
      HeightInPx: 400,
    }),
            il.NewRectangleLayer(4, "#FFFFFF",
                il.Position{
      XInPx: 225,
      YInPx: 2250,
    },
                il.Dimensions{
      WidthInPx: 450,
      HeightInPx: 350,
    }),
            il.NewBarcodeLayer(5, "9781234567890", "ean13",
                il.Position{
      XInPx: 250,
      YInPx: 2275,
    },
                il.Dimensions{
      WidthInPx: 400,
      HeightInPx: 250,
    },
                "#000000", "#FFFFFF"),
            il.NewTextLayerWithRotation(6, "The Long Silence", "Montserrat", 24, "#FFFFFF",
                il.Position{
      XInPx: float64(spineXInPx),
      YInPx: 375,
    },
                il.Dimensions{
      WidthInPx: spineWidthInPx,
      HeightInPx: 2025,
    }, -90),
        },
    })
    if err != nil {
        panic(err)
    }
}
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}

The TypeScript and Python examples show the spine width calculation from page count — the part that causes the most headaches manually. See the full Generate Book Cover Spreads recipe for the complete layout including author name on spine and back cover details.

Step 4: Generate the A+ Content Banner

Amazon A+ Content (formerly Enhanced Brand Content) lets authors add rich media to their product listings. The standard image module is 970x600 pixels. Most authors skip it because creating another asset in another tool for another platform feels like too much work.

When it is just another API call in your pipeline, there is no reason to skip it. The front cover image from Step 2 feeds directly into the banner as an image layer:

Request
curl -X POST https://api.iterationlayer.com/image-generation/v1/render \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": {
        "width": 970,
        "height": 600,
    },
    "output_format": "png",
    "layers": [
      {
        "index": 0,
        "type": "gradient",
        "gradient_type": "linear",
        "angle_in_degrees": 135.0,
        "colors": [
          {
              "hex_color": "#1B2838",
              "position": 0.0,
          },
          {
              "hex_color": "#2D3A4A",
              "position": 100.0,
          }
        ],
        "position": {
            "x": 0.0,
            "y": 0.0,
        },
        "dimensions": {
            "width": 970,
            "height": 600,
        }
      },
      {
        "index": 1,
        "type": "image",
        "buffer": "<base64-encoded-front-cover>",
        "position": {
            "x": 60.0,
            "y": 60.0,
        },
        "dimensions": {
            "width": 320,
            "height": 480,
        }
      },
      {
        "index": 2,
        "type": "text",
        "text": "THE ASHWORTH CHRONICLES",
        "font_name": "Montserrat",
        "font_size_in_px": 14,
        "text_color": "#94A3B8",
        "font_weight": "Bold",
        "position": {
            "x": 440.0,
            "y": 80.0,
        },
        "dimensions": {
            "width": 480,
            "height": 24,
        }
      },
      {
        "index": 3,
        "type": "text",
        "text": "The Long Silence",
        "font_name": "PlayfairDisplay",
        "font_size_in_px": 44,
        "text_color": "#FFFFFF",
        "font_weight": "Bold",
        "position": {
            "x": 440.0,
            "y": 120.0,
        },
        "dimensions": {
            "width": 480,
            "height": 110,
        }
      },
      {
        "index": 4,
        "type": "text",
        "text": "Elizabeth Ashworth",
        "font_name": "Lora",
        "font_size_in_px": 20,
        "text_color": "#CBD5E1",
        "position": {
            "x": 440.0,
            "y": 250.0,
        },
        "dimensions": {
            "width": 480,
            "height": 32,
        }
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
const aplusBanner = await client.generateImage({
  dimensions: {
    width_in_px: 970,
    height_in_px: 600,
  },
  output_format: "png",
  layers: [
    {
      index: 0,
      type: "gradient",
      gradient_type: "linear",
      angle_in_degrees: 135.0,
      colors: [
        {
            hex_color: "#1B2838",
            position: 0.0,
        },
        {
            hex_color: "#2D3A4A",
            position: 100.0,
        },
      ],
      position: {
        x: 0.0,
        y: 0.0,
      },
      dimensions: {
        width_in_px: 970,
        height_in_px: 600,
      },
    },
    {
      index: 1,
      type: "image",
      buffer: frontCoverBase64,
      position: {
        x: 60.0,
        y: 60.0,
      },
      dimensions: {
        width_in_px: 320,
        height_in_px: 480,
      },
    },
    {
      index: 2,
      type: "text",
      text: "THE ASHWORTH CHRONICLES",
      font_name: "Montserrat",
      font_size_in_px: 14,
      text_color: "#94A3B8",
      font_weight: "Bold",
      position: {
        x: 440.0,
        y: 80.0,
      },
      dimensions: {
        width_in_px: 480,
        height_in_px: 24,
      },
    },
    {
      index: 3,
      type: "text",
      text: "The Long Silence",
      font_name: "PlayfairDisplay",
      font_size_in_px: 44,
      text_color: "#FFFFFF",
      font_weight: "Bold",
      position: {
        x: 440.0,
        y: 120.0,
      },
      dimensions: {
        width_in_px: 480,
        height_in_px: 110,
      },
    },
    {
      index: 4,
      type: "text",
      text: "Elizabeth Ashworth",
      font_name: "Lora",
      font_size_in_px: 20,
      text_color: "#CBD5E1",
      position: {
        x: 440.0,
        y: 250.0,
      },
      dimensions: {
        width_in_px: 480,
        height_in_px: 32,
      },
    },
  ],
});
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
aplus_banner = client.generate_image(
    dimensions={
        "width_in_px": 970,
        "height_in_px": 600,
    },
    output_format="png",
    layers=[
        {
            "index": 0,
            "type": "gradient",
            "gradient_type": "linear",
            "angle_in_degrees": 135.0,
            "colors": [
                {
                    "hex_color": "#1B2838",
                    "position": 0.0,
                },
                {
                    "hex_color": "#2D3A4A",
                    "position": 100.0,
                },
            ],
            "position": {
                "x": 0.0,
                "y": 0.0,
            },
            "dimensions": {
                "width_in_px": 970,
                "height_in_px": 600,
            },
        },
        {
            "index": 1,
            "type": "image",
            "buffer": front_cover_base64,
            "position": {
                "x": 60.0,
                "y": 60.0,
            },
            "dimensions": {
                "width_in_px": 320,
                "height_in_px": 480,
            },
        },
        {
            "index": 2,
            "type": "text",
            "text": "THE ASHWORTH CHRONICLES",
            "font_name": "Montserrat",
            "font_size_in_px": 14,
            "text_color": "#94A3B8",
            "font_weight": "Bold",
            "position": {
                "x": 440.0,
                "y": 80.0,
            },
            "dimensions": {
                "width_in_px": 480,
                "height_in_px": 24,
            },
        },
        {
            "index": 3,
            "type": "text",
            "text": "The Long Silence",
            "font_name": "PlayfairDisplay",
            "font_size_in_px": 44,
            "text_color": "#FFFFFF",
            "font_weight": "Bold",
            "position": {
                "x": 440.0,
                "y": 120.0,
            },
            "dimensions": {
                "width_in_px": 480,
                "height_in_px": 110,
            },
        },
        {
            "index": 4,
            "type": "text",
            "text": "Elizabeth Ashworth",
            "font_name": "Lora",
            "font_size_in_px": 20,
            "text_color": "#CBD5E1",
            "position": {
                "x": 440.0,
                "y": 250.0,
            },
            "dimensions": {
                "width_in_px": 480,
                "height_in_px": 32,
            },
        },
    ],
)
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
result, err := client.GenerateImage(il.GenerateImageRequest{
    Dimensions:   il.Dimensions{
   WidthInPx: 970,
   HeightInPx: 600,
 },
    OutputFormat: "png",
    Layers: []il.Layer{
        il.NewGradientLayer(0, "linear", []il.GradientColor{
            {
     HexColor: "#1B2838",
     Position: 0.0,
   },
            {
     HexColor: "#2D3A4A",
     Position: 100.0,
   },
        }, il.Position{
    X: 0.0,
    Y: 0.0,
  }, il.Dimensions{
    WidthInPx: 970,
    HeightInPx: 600,
  }),
        il.NewStaticImageLayer(1, frontCoverBase64,
            il.Position{
     X: 60.0,
     Y: 60.0,
   },
            il.Dimensions{
     WidthInPx: 320,
     HeightInPx: 480,
   }),
        il.NewTextLayer(2, "THE ASHWORTH CHRONICLES", "Montserrat", 14, "#94A3B8",
            il.Position{
     X: 440.0,
     Y: 80.0,
   },
            il.Dimensions{
     WidthInPx: 480,
     HeightInPx: 24,
   }),
        il.NewTextLayer(3, "The Long Silence", "PlayfairDisplay", 44, "#FFFFFF",
            il.Position{
     X: 440.0,
     Y: 120.0,
   },
            il.Dimensions{
     WidthInPx: 480,
     HeightInPx: 110,
   }),
        il.NewTextLayer(4, "Elizabeth Ashworth", "Lora", 20, "#CBD5E1",
            il.Position{
     X: 440.0,
     Y: 250.0,
   },
            il.Dimensions{
     WidthInPx: 480,
     HeightInPx: 32,
   }),
    },
})
if err != nil {
    panic(err)
}
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}

See the full Generate A+ Content Banner recipe for the complete layer stack including the genre badge and availability badge.

Step 5: Generate the Social Media Promo

TikTok and Instagram Stories use 1080x1920 vertical images. BookTok is a real sales channel for fiction — authors who post consistently see measurable spikes in sales. But creating story-format promotional images for every title is yet another manual step.

The hook text at the top — the teaser question — is the kind of thing that drives engagement on BookTok. When it is a text layer in an API call, A/B testing your hooks means changing a string, not opening Canva:

Request
curl -X POST https://api.iterationlayer.com/image-generation/v1/render \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dimensions": {
        "width": 1080,
        "height": 1920,
    },
    "output_format": "png",
    "layers": [
      {
        "index": 0,
        "type": "gradient",
        "gradient_type": "linear",
        "angle_in_degrees": 180.0,
        "colors": [
          {
              "hex_color": "#0F0A1A",
              "position": 0.0,
          },
          {
              "hex_color": "#1B1464",
              "position": 50.0,
          },
          {
              "hex_color": "#2D1B69",
              "position": 100.0,
          }
        ],
        "position": {
            "x": 0.0,
            "y": 0.0,
        },
        "dimensions": {
            "width": 1080,
            "height": 1920,
        }
      },
      {
        "index": 1,
        "type": "text",
        "text": "What if the person you trusted most\nwas the one keeping the secret?",
        "font_name": "Lora",
        "font_size_in_px": 36,
        "text_color": "#E2E8F0",
        "font_style": "Italic",
        "text_align": "center",
        "position": {
            "x": 80.0,
            "y": 140.0,
        },
        "dimensions": {
            "width": 920,
            "height": 200,
        }
      },
      {
        "index": 2,
        "type": "image",
        "buffer": "<base64-encoded-front-cover>",
        "position": {
            "x": 240.0,
            "y": 420.0,
        },
        "dimensions": {
            "width": 600,
            "height": 900,
        }
      },
      {
        "index": 3,
        "type": "text",
        "text": "The Long Silence",
        "font_name": "PlayfairDisplay",
        "font_size_in_px": 48,
        "text_color": "#FFFFFF",
        "font_weight": "Bold",
        "text_align": "center",
        "position": {
            "x": 80.0,
            "y": 1450.0,
        },
        "dimensions": {
            "width": 920,
            "height": 70,
        }
      },
      {
        "index": 4,
        "type": "text",
        "text": "by Elizabeth Ashworth",
        "font_name": "Lora",
        "font_size_in_px": 22,
        "text_color": "#94A3B8",
        "text_align": "center",
        "position": {
            "x": 80.0,
            "y": 1540.0,
        },
        "dimensions": {
            "width": 920,
            "height": 36,
        }
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
const storyPromo = await client.generateImage({
  dimensions: {
    width_in_px: 1080,
    height_in_px: 1920,
  },
  output_format: "png",
  layers: [
    {
      index: 0,
      type: "gradient",
      gradient_type: "linear",
      angle_in_degrees: 180.0,
      colors: [
        {
            hex_color: "#0F0A1A",
            position: 0.0,
        },
        {
            hex_color: "#1B1464",
            position: 50.0,
        },
        {
            hex_color: "#2D1B69",
            position: 100.0,
        },
      ],
      position: {
        x: 0.0,
        y: 0.0,
      },
      dimensions: {
        width_in_px: 1080,
        height_in_px: 1920,
      },
    },
    {
      index: 1,
      type: "text",
      text: "What if the person you trusted most\nwas the one keeping the secret?",
      font_name: "Lora",
      font_size_in_px: 36,
      text_color: "#E2E8F0",
      font_style: "Italic",
      text_align: "center",
      position: {
        x: 80.0,
        y: 140.0,
      },
      dimensions: {
        width_in_px: 920,
        height_in_px: 200,
      },
    },
    {
      index: 2,
      type: "image",
      buffer: frontCoverBase64,
      position: {
        x: 240.0,
        y: 420.0,
      },
      dimensions: {
        width_in_px: 600,
        height_in_px: 900,
      },
    },
    {
      index: 3,
      type: "text",
      text: "The Long Silence",
      font_name: "PlayfairDisplay",
      font_size_in_px: 48,
      text_color: "#FFFFFF",
      font_weight: "Bold",
      text_align: "center",
      position: {
        x: 80.0,
        y: 1450.0,
      },
      dimensions: {
        width_in_px: 920,
        height_in_px: 70,
      },
    },
    {
      index: 4,
      type: "text",
      text: "by Elizabeth Ashworth",
      font_name: "Lora",
      font_size_in_px: 22,
      text_color: "#94A3B8",
      text_align: "center",
      position: {
        x: 80.0,
        y: 1540.0,
      },
      dimensions: {
        width_in_px: 920,
        height_in_px: 36,
      },
    },
  ],
});
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
story_promo = client.generate_image(
    dimensions={
        "width_in_px": 1080,
        "height_in_px": 1920,
    },
    output_format="png",
    layers=[
        {
            "index": 0,
            "type": "gradient",
            "gradient_type": "linear",
            "angle_in_degrees": 180.0,
            "colors": [
                {
                    "hex_color": "#0F0A1A",
                    "position": 0.0,
                },
                {
                    "hex_color": "#1B1464",
                    "position": 50.0,
                },
                {
                    "hex_color": "#2D1B69",
                    "position": 100.0,
                },
            ],
            "position": {
                "x": 0.0,
                "y": 0.0,
            },
            "dimensions": {
                "width_in_px": 1080,
                "height_in_px": 1920,
            },
        },
        {
            "index": 1,
            "type": "text",
            "text": "What if the person you trusted most\nwas the one keeping the secret?",
            "font_name": "Lora",
            "font_size_in_px": 36,
            "text_color": "#E2E8F0",
            "font_style": "Italic",
            "text_align": "center",
            "position": {
                "x": 80.0,
                "y": 140.0,
            },
            "dimensions": {
                "width_in_px": 920,
                "height_in_px": 200,
            },
        },
        {
            "index": 2,
            "type": "image",
            "buffer": front_cover_base64,
            "position": {
                "x": 240.0,
                "y": 420.0,
            },
            "dimensions": {
                "width_in_px": 600,
                "height_in_px": 900,
            },
        },
        {
            "index": 3,
            "type": "text",
            "text": "The Long Silence",
            "font_name": "PlayfairDisplay",
            "font_size_in_px": 48,
            "text_color": "#FFFFFF",
            "font_weight": "Bold",
            "text_align": "center",
            "position": {
                "x": 80.0,
                "y": 1450.0,
            },
            "dimensions": {
                "width_in_px": 920,
                "height_in_px": 70,
            },
        },
        {
            "index": 4,
            "type": "text",
            "text": "by Elizabeth Ashworth",
            "font_name": "Lora",
            "font_size_in_px": 22,
            "text_color": "#94A3B8",
            "text_align": "center",
            "position": {
                "x": 80.0,
                "y": 1540.0,
            },
            "dimensions": {
                "width_in_px": 920,
                "height_in_px": 36,
            },
        },
    ],
)
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}
Request
result, err = client.GenerateImage(il.GenerateImageRequest{
    Dimensions:   il.Dimensions{
   WidthInPx: 1080,
   HeightInPx: 1920,
 },
    OutputFormat: "png",
    Layers: []il.Layer{
        il.NewGradientLayer(0, "linear", []il.GradientColor{
            {
     HexColor: "#0F0A1A",
     Position: 0.0,
   },
            {
     HexColor: "#1B1464",
     Position: 50.0,
   },
            {
     HexColor: "#2D1B69",
     Position: 100.0,
   },
        }, il.Position{
    X: 0.0,
    Y: 0.0,
  }, il.Dimensions{
    WidthInPx: 1080,
    HeightInPx: 1920,
  }),
        il.NewTextLayer(1,
            "What if the person you trusted most\nwas the one keeping the secret?",
            "Lora", 36, "#E2E8F0",
            il.Position{
     X: 80.0,
     Y: 140.0,
   },
            il.Dimensions{
     WidthInPx: 920,
     HeightInPx: 200,
   }),
        il.NewStaticImageLayer(2, frontCoverBase64,
            il.Position{
     X: 240.0,
     Y: 420.0,
   },
            il.Dimensions{
     WidthInPx: 600,
     HeightInPx: 900,
   }),
        il.NewTextLayer(3, "The Long Silence", "PlayfairDisplay", 48, "#FFFFFF",
            il.Position{
     X: 80.0,
     Y: 1450.0,
   },
            il.Dimensions{
     WidthInPx: 920,
     HeightInPx: 70,
   }),
        il.NewTextLayer(4, "by Elizabeth Ashworth", "Lora", 22, "#94A3B8",
            il.Position{
     X: 80.0,
     Y: 1540.0,
   },
            il.Dimensions{
     WidthInPx: 920,
     HeightInPx: 36,
   }),
    },
})
if err != nil {
    panic(err)
}
Response
{
  "success": true,
  "data": {
    "buffer": "iVBORw0KGgoAAAANSUhEUg...",
    "mime_type": "image/png"
  }
}

Different hooks for different platforms, different campaigns, different audience segments. See the full Generate Social Media Book Promo recipe for all layers including series branding and CTA button.

Why This Matters at Scale

A single author with five titles needs 25 assets (5 per book). A small press with fifty titles needs 250. A publishing operation that releases new titles monthly needs to regenerate marketing assets for backlist titles whenever branding changes.

The manual approach — Scrivener, Photoshop, Canva, a spreadsheet tracking which assets exist for which titles — breaks somewhere between 10 and 20 titles. Not because any individual step is hard, but because the coordination overhead grows faster than the catalog.

An API pipeline inverts this. Adding a title means adding a row to your database. Regenerating assets for a series rebrand means running the script with updated colors and fonts. The marginal cost of the 100th title is the same as the 1st.

There are real constraints to understand. The Image Generation API produces RGB output — if your printer requires CMYK, you will need a conversion step. The Document Generation API handles page breaks and typography, but does not do automatic hyphenation. These are solvable problems, but they are worth knowing upfront.

Get Started

The full API references cover everything you need:

The recipes linked throughout this post are copy-paste ready:

Sign up for a free account — no credit card required. Start with the manuscript or the cover, see how the structured approach works, then connect the pieces into a pipeline.

Start building in minutes

Free trial included. No credit card required.