Developer API

Carousel
Generation API

Send text and a theme ID. Get back CDN-hosted carousel image URLs. One endpoint, synchronous response, permanent image URLs.

Ready to subscribe? See API pricing plans.

1 — Overview

Base URL

https://api.bulkinsta.com/v1

Versioning

The API version is part of the URL path (/v1). Breaking changes increment the version to /v2. Old versions receive at minimum 6 months deprecation notice before sunset.

Non-breaking additions — new optional fields, new themes, new error codes — are made to the current version without notice.

Request format

All request bodies must be JSON with Content-Type: application/json. All responses are JSON.

2 — Authentication

Header

Authorization: Bearer YOUR_API_KEY
Key format bik_live_[32 alphanumeric chars]
Obtained from Dashboard > API Keys
Expiry Keys do not expire unless manually revoked
Invalid key 401 invalid_api_key
Revoked key 401 expired_api_key

Security

Never expose your API key in client-side code. Make all requests server-side. If compromised, revoke immediately from the dashboard.

3 — Endpoint

POST /generate-carousel

Generates a fully designed Instagram carousel from plain text and a theme. Synchronous — the connection stays open while slides render and upload to CDN. Returns once all slides are available.

Sync / timeout contract

  • Synchronous. No polling or webhooks required.
  • Max server timeout: 90 seconds. Set your HTTP client timeout to at least 120 s.
  • If server timeout is exceeded: 500 generation_failed. No quota consumed.
  • Typical response time: 5–30 seconds depending on slide count and theme.

Idempotency & retries

  • Not idempotent. Each call creates a new job and consumes one quota unit.
  • Retrying the same payload produces a new id and new image URLs.
  • Safe to retry on 500 — no quota consumed on server failures.
  • Do not retry 400, 401, or 404 — client errors that won't self-resolve.

Request Body

Field Type Required Constraints
text string Yes 1–5000 characters. Plain text. Unicode and emoji supported.
theme_id number Yes Integer. Enum: 1–10, 20–26, 30. See themes section.
brand_name string No 0–60 characters. Brand / account label shown on slides. Defaults to null.

Example Request

curl
curl -X POST https://bulkinsta.com/api/v1/generate-carousel \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "5 habits that changed my mornings forever...",
    "theme_id": 1,
    "brand_name": "Acme Co."
  }'

Response Fields (200 OK)

Field Type Description
id string Unique job ID. Format: gen_ + 10 alphanumeric chars.
slides string[] Ordered array of CDN image URLs. Length matches slide_count.
slide_count number Integer, 1-20. Determined by content length and theme layout.
theme_id number Echoes the theme ID used for this generation.
brand_name string | null Brand name as provided, or null if omitted.
created_at string ISO 8601 UTC timestamp of job creation.

Example Response (200 OK)

JSON
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1744280520

{
  "id": "gen_4f8a2c1d9e",
  "slides": [
    "https://cdn.bulkinsta.com/api-images/gen_4f8a2c1d9e/slide-1.png",
    "https://cdn.bulkinsta.com/api-images/gen_4f8a2c1d9e/slide-2.png",
    "https://cdn.bulkinsta.com/api-images/gen_4f8a2c1d9e/slide-3.png",
    "https://cdn.bulkinsta.com/api-images/gen_4f8a2c1d9e/slide-4.png"
  ],
  "slide_count": 4,
  "theme_id": 1,
  "brand_name": "Acme Co.",
  "created_at": "2026-04-10T09:41:00Z"
}

4 — TypeScript Types

Copy-paste ready types for TypeScript consumers. The ApiError shape is shared across all error responses — branch on error, not message.

TypeScript
// Request
interface GenerateCarouselRequest {
  text: string;        // 1-5000 characters, plain text
  theme_id: number;    // integer; see themes list
  brand_name?: string; // optional, 0-60 characters
}

// Success response  (200 OK)
interface GenerateCarouselResponse {
  id: string;          // "gen_" + 10 alphanumeric chars
  slides: string[];    // ordered CDN URLs, one per slide
  slide_count: number; // 1-20
  theme_id: number;
  brand_name: string | null;
  created_at: string;  // ISO 8601 UTC
}

// Error response  (all 4xx / 5xx)
interface ApiError {
  error: string;   // machine-readable snake_case code
  message: string; // human-readable — do not parse
  status: number;  // mirrors HTTP status
}

5 — Errors

All errors follow the same JSON shape. Branch on the error field for machine-readable handling. The message field is for humans only — do not parse it.

Field Type Description
error string Machine-readable snake_case code. Stable across versions.
message string Human-readable description. May change without notice.
status number Mirrors the HTTP status code.
Status error code Retryable Quota consumed
400 invalid_request No No
401 invalid_api_key No No
401 expired_api_key No No
402 quota_exceeded After upgrade No
404 theme_not_found No No
429 rate_limit_exceeded Yes (Retry-After) No
500 generation_failed Yes No

Response Examples

400 invalid_request
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "invalid_request",
  "message": "text is required and must be between 1 and 5000 characters",
  "status": 400
}
401 invalid_api_key
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error": "invalid_api_key",
  "message": "The API key provided is not valid",
  "status": 401
}
401 expired_api_key
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error": "expired_api_key",
  "message": "The API key has been revoked. Generate a new key from your dashboard",
  "status": 401
}
402 quota_exceeded
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": "quota_exceeded",
  "message": "Monthly generation quota reached. Upgrade your plan to continue",
  "status": 402
}
404 theme_not_found
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "theme_not_found",
  "message": "theme_id 99 does not exist. See available themes in the documentation",
  "status": 404
}
429 rate_limit_exceeded
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 17

{
  "error": "rate_limit_exceeded",
  "message": "Rate limit of 10 requests/minute exceeded. Retry after 17 seconds",
  "status": 429
}
500 generation_failed
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "error": "generation_failed",
  "message": "Carousel rendering failed. No quota was consumed. Please retry",
  "status": 500
}

6 — Rate Limits

Plan Limit Window
Starter 10 requests per minute
Growth 30 requests per minute
Scale 60 requests per minute

Every response includes rate limit headers. On 429, wait the number of seconds in Retry-After before retrying.

Response headers
X-RateLimit-Limit: 10          # requests allowed per minute
X-RateLimit-Remaining: 7       # requests left in current window
X-RateLimit-Reset: 1744280520  # Unix timestamp when window resets
Retry-After: 17                # seconds to wait (only present on 429)

7 — CDN Behavior

URL format

https://cdn.bulkinsta.com/api-images/{job_id}/slide-{n}.png
Format PNG
Dimensions 1080 x 1440 px (4:5 portrait)
Access Public, no authentication required
Expiry URLs do not expire
Slide index n starts at 1 (slide-1.png, slide-2.png, ...)
Availability Guaranteed by response time — slides are uploaded before response is returned

8 — Edge Cases

Input condition Behavior
text is empty string 400 invalid_request — text must be at least 1 character
text exceeds 5000 characters 400 invalid_request — text is too long
text contains only whitespace 400 invalid_request — text cannot be blank
text with emoji / unicode Supported. Rendered as-is in slides.
theme_id is a string "1" 400 invalid_request — theme_id must be a number
theme_id is a float e.g. 1.5 400 invalid_request — theme_id must be an integer
theme_id valid but wrong type Accepted — no type-vs-content validation
brand_name over 60 characters 400 invalid_request — brand_name too long
brand_name is null explicitly Treated as omitted — null in response
Very short text (1-10 words) Generates 1-2 slides. No error.

9 — Themes

Pass the numeric id as theme_id. The type field indicates intended content format — mixing types is accepted but may produce suboptimal layouts.

ID Name Type
1 Energetic text_carousel
2 Calm text_carousel
3 Editorial text_carousel
4 Bold text_carousel
5 Fresh text_carousel
6 Bold Fun text_carousel
7 Retro text_carousel
8 Groovy text_carousel
9 Neo-Editorial text_carousel
10 Minimal text_carousel
20 Crimson quote_carousel
21 Soft Sage quote_carousel
22 Dusty Lavender quote_carousel
23 Warm Sand quote_carousel
24 Blush Minimal quote_carousel
25 Cool Sky quote_carousel
26 Onyx quote_carousel
30 Bold Editorial comparison

10 — Changelog

v1.0

2026-04-10

Initial release. POST /generate-carousel, 18 themes, brand_name field.

Ready to create?

Ready to integrate?

Get your API key from your dashboard and start generating in minutes.