{
  "openapi": "3.0.1",
  "info": {
    "title": "Bulk Text on Image Generator",
    "description": "Add headlines and captions to batches of photos — text is automatically placed inside Instagram, TikTok, YouTube Shorts and Facebook's own safe zones, clear of where each platform's UI sits.",
    "version": "1.0",
    "x-build-id": "VJ4KxoYRSapberR9D"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/sian.agency~bulk-text-on-image-generator/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-sian.agency-bulk-text-on-image-generator",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/sian.agency~bulk-text-on-image-generator/runs": {
      "post": {
        "operationId": "runs-sync-sian.agency-bulk-text-on-image-generator",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/sian.agency~bulk-text-on-image-generator/run-sync": {
      "post": {
        "operationId": "run-sync-sian.agency-bulk-text-on-image-generator",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "imageUrl": {
            "title": "🖼️ Single Image URL",
            "type": "string",
            "description": "🖼️ **SINGLE IMAGE MODE:** paste one image URL and fill in the Headline Text field below to render it — the fastest way to try the actor. Ignored whenever any bulk mode below is filled in (those take priority).\n\n✅ **Supported:** direct `http(s)` links to JPEG, PNG, WebP, GIF, TIFF or AVIF files, up to 25 MB and 40 megapixels.\n\n❌ **Not supported:** a platform page link (e.g. an Instagram post URL) — only a direct image file link works.",
            "default": "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg"
          },
          "text": {
            "title": "✍️ Headline Text (Single Image Mode)",
            "type": "string",
            "description": "✍️ The headline or caption rendered on top of the Single Image URL above. Use `\\n` for an explicit line break — otherwise the text wraps automatically to fit the safe zone.\n\n💡 Ignored in Bulk Rows or CSV mode — each row supplies its own text there.",
            "default": "SUMMER SALE\n50% OFF"
          },
          "bulkImageUrls": {
            "title": "🚀 Bulk Images (URLs, list file or CSV)",
            "type": "array",
            "description": "🚀 **BULK MODE — the main way to run many images.**\n\n- **Add** / **Bulk edit** — paste image URLs, one at a time or as a list.\n- **Text file** — link a `.txt` of one URL per line, **or a `.csv`** with `imageUrl` + `text` columns. Detected automatically: a CSV gives each image its own headline, a URL list gives them all the Headline Text above.\n\nCSV needs `imageUrl` + `text`; per-row override columns optional (see the README).\n\n📊 FREE renders the first 5 after dedup; PAID renders every one.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "csvFile": {
            "title": "📎 Upload CSV from your computer",
            "type": "string",
            "description": "📎 Use this only when your CSV is a **local file** — if it already lives at a URL, link it in Bulk Images above instead.\n\nRequired columns: `imageUrl`, `text`. Any per-row override column may also be included — an empty cell inherits the global setting below, a filled cell beats it. Unrecognized columns are ignored with a warning.\n\n📊 FREE renders the first 5 rows after dedup; PAID renders every row.\n\n💡 **Takes priority over everything else.**"
          },
          "rows": {
            "title": "📋 Bulk Rows (Multiple Images)",
            "type": "array",
            "description": "📋 **BULK MODE:** one entry per image — each object needs `imageUrl` and `text`. Any per-row override key (see the README's CSV section for the full list) can be set here too; **a value on the row always beats the matching global setting below**, an omitted key inherits it. `outputMode`/`maxOutputEdgePx` are global-only.\n\n📊 FREE renders the first 5 rows after dedup; PAID renders every row.\n\n💡 Ignored when a CSV is uploaded — the CSV takes priority."
          },
          "preset": {
            "title": "🎯 Platform Preset",
            "enum": [
              "instagram-feed-4x5",
              "instagram-square-1x1",
              "instagram-story-reels",
              "tiktok",
              "youtube-shorts",
              "facebook-feed",
              "facebook-story"
            ],
            "type": "string",
            "description": "🎯 The output canvas size and platform safe zone the text is kept inside. Only used when Output Mode is \"Fit to preset canvas\".\n\n- **Instagram Feed (4:5)** — 1080×1350\n- **Instagram Feed (1:1 Square)** — 1080×1080\n- **Instagram Story / Reels (9:16)** — 1080×1920\n- **TikTok (9:16)** — 1080×1920\n- **YouTube Shorts (9:16)** — 1080×1920\n- **Facebook Feed (1.91:1 Landscape)** — 1200×628\n- **Facebook Story (9:16)** — 1080×1920",
            "default": "instagram-feed-4x5"
          },
          "outputMode": {
            "title": "🖼️ Output Mode",
            "enum": [
              "preset",
              "keepOriginal"
            ],
            "type": "string",
            "description": "🖼️ **Fit to preset canvas:** resizes/crops the image to the exact pixel size of the Platform Preset above. **Keep original size:** keeps the source image's own dimensions (downscaled only if its longest edge exceeds Max Output Edge below); the safe zone is then scaled proportionally to match.",
            "default": "preset"
          },
          "fitMode": {
            "title": "✂️ Fit Mode",
            "enum": [
              "cover",
              "contain"
            ],
            "type": "string",
            "description": "✂️ How the source image fills the preset canvas — only used when Output Mode is \"Fit to preset canvas\". **Cover:** crops the image to fill the canvas edge-to-edge. **Contain:** shrinks the image to fit inside the canvas whole, padding any empty space with the Pad Color below.",
            "default": "cover"
          },
          "padColor": {
            "title": "🎨 Pad Color",
            "type": "string",
            "description": "🎨 Hex with or without the `#` (`#ffffff` or `ffffff`) or a CSS named color (e.g. `white`), used to fill empty space when Fit Mode is \"Contain\". Ignored otherwise.",
            "default": "#ffffff"
          },
          "placement": {
            "title": "📍 Text Placement",
            "enum": [
              "top",
              "center",
              "bottom"
            ],
            "type": "string",
            "description": "📍 Where the text box sits inside the platform's safe zone.",
            "default": "bottom"
          },
          "textStyle": {
            "title": "🖌️ Text Style",
            "enum": [
              "none",
              "outline",
              "band",
              "scrim",
              "shadow"
            ],
            "type": "string",
            "description": "🖌️ How the text is rendered against the photo. **None:** plain text, nothing behind it — cleanest over flat or uncluttered images. **Outline:** solid text with a contrasting stroke, the safest choice over a photo you haven't looked at. **Band:** an opaque color block behind the text. **Scrim:** a soft gradient fade behind the text. **Shadow:** an offset drop-shadow behind the text.",
            "default": "none"
          },
          "font": {
            "title": "🔤 Font",
            "enum": [
              "Anton",
              "Bebas Neue",
              "Archivo Black",
              "Lato",
              "PT Serif",
              "Oswald",
              "Montserrat",
              "Poppins",
              "Inter",
              "Roboto",
              "Open Sans",
              "Raleway",
              "Rubik",
              "Work Sans",
              "Nunito",
              "Source Sans 3",
              "Barlow Condensed",
              "Teko",
              "Fjalla One",
              "Josefin Sans",
              "Playfair Display",
              "Merriweather",
              "Lora",
              "Roboto Slab",
              "Abril Fatface",
              "Alfa Slab One",
              "Bungee",
              "Righteous",
              "Staatliches",
              "Permanent Marker",
              "Pacifico"
            ],
            "type": "string",
            "description": "🔤 The typeface your headline is set in. The first five (⚡) are bundled with the actor and render instantly with no lookup; the rest are fetched from Google Fonts at bold weight the first time a row uses them. Every family in this list has been checked to resolve to a real bold face.\n\n💡 Need a family that isn't listed? Type it into **Custom Font Family** below — that field overrides this dropdown.",
            "default": "Anton"
          },
          "customFont": {
            "title": "🔠 Custom Font Family",
            "type": "string",
            "description": "🔠 Any Google Fonts family name (e.g. `Cormorant Garamond`, `Space Grotesk`) — use this when the family you want isn't in the Font dropdown above. Leave empty to use the dropdown.\n\n⚠️ Fetched at bold weight when the row renders. If the family doesn't exist, isn't on Google Fonts, or has no bold static weight, that row fails with a clear error rather than silently substituting a different font."
          },
          "fontFile": {
            "title": "📤 Upload Your Own Font",
            "type": "string",
            "description": "📤 Upload a **`.ttf` or `.otf`** to render every headline in your own brand typeface. Overrides the Font dropdown and Custom Font Family.\n\n❌ Not supported: `.woff`/`.woff2` (convert to TTF/OTF first), `.ttc` collections, and **variable fonts** — export the single static weight you want (e.g. Bold) and upload that.\n\n⚖️ Use only fonts you are licensed to use. Max 8 MB.\n\n💡 A `font` column in a CSV still wins per row."
          },
          "textColor": {
            "title": "🎨 Text Color",
            "type": "string",
            "description": "🎨 Hex with or without the `#` (`#ffffff` or `ffffff`) or a CSS named color (e.g. `white`) for the rendered text.",
            "default": "#ffffff"
          },
          "accentColor": {
            "title": "🎨 Accent Color",
            "type": "string",
            "description": "🎨 Hex with or without the `#` (`#111111` or `111111`) or a CSS named color (e.g. `steelblue`) for the stroke (Outline style), background block (Band), gradient (Scrim) or drop-shadow (Shadow) — whichever Text Style is selected.",
            "default": "#111111"
          },
          "textCase": {
            "title": "🔠 Text Case",
            "enum": [
              "none",
              "upper"
            ],
            "type": "string",
            "description": "🔠 **As typed:** render the text exactly as entered. **UPPERCASE:** convert the text to all caps before rendering.",
            "default": "none"
          },
          "maxTextHeightPct": {
            "title": "📏 Max Text Height (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "number",
            "description": "📏 The largest share of the safe zone's height the text block may fill, as a percentage. Text automatically shrinks to fit within this limit; a row fails if it still doesn't fit at the smallest readable size.",
            "default": 30
          },
          "marginPct": {
            "title": "↔️ Inner Margin (%)",
            "minimum": 0,
            "maximum": 100,
            "type": "number",
            "description": "↔️ Empty space kept clear on every side of the text box, as a percentage of the safe zone. Values above 45 are automatically capped at 45, so the text box never disappears entirely.",
            "default": 5
          },
          "maxOutputEdgePx": {
            "title": "📐 Max Output Edge (px)",
            "minimum": 1,
            "maximum": 2560,
            "type": "integer",
            "description": "📐 When Output Mode is \"Keep original size\", the source image is downscaled so its longest edge never exceeds this many pixels. Ignored when Output Mode is \"Fit to preset canvas\".\n\n💡 Capped at 2560 px. Every platform preset tops out at 1920 px on the long edge, so the default of 2048 already exceeds what any social canvas needs — the headroom above it is for keeping a product photo close to its original resolution.",
            "default": 2048
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}