SimploAPI

Generate posts in your brand's voice and schedule them from your own tools.

A REST API over HTTPS with JSON bodies. Write captions and pictures that follow your brand profile, save them as posts, and schedule them to Instagram, Facebook and TikTok, from curl, a script, CI or any tool that can send an HTTP request. The base URL is https://api.getsimplo.ai/v1.

Working inside an AI assistant instead? The Simplo MCP server does the same through a chat.

Authentication

In the Simplo app, open Settings → API keys and create a key. It is shown once, so copy it then. Send it on every request:

header
Authorization: Bearer smp_live_…

A key acts for your whole account: every project (brand) you own, and your credits. Keep it on a server or in a CI secret, never in a web page or an app. You can have up to 10 keys and revoke any of them at any time in Settings; a revoked key stops working at once. A request without a valid key answers 401 invalid_api_key.

Quickstart

From nothing to a scheduled Instagram post in five steps. The examples use jq to build JSON safely and to read the answers. Before you start, set up the brand profile in the app (Brand): generating captions or pictures for a project without one answers 400 validation_error ("Brand profile not set up"). Connect the platform you post to there too (Brand → Channels).

setup
# Paste your key from Settings → API keys. jq is used below to build and read JSON.
export SIMPLO_API_KEY="smp_live_…"
BASE="https://api.getsimplo.ai/v1"

1Find your project

Most calls name a project, one per brand. This keeps the first one; if you have several brands, look at the list and set PROJECT_ID to the one whose brand profile is set up. The accounts call tells you which platforms can receive posts.

GET /projects
# One project per brand. This keeps the first one's id; with several brands,
# pick the one whose brand profile is set up in the app.
PROJECT_ID=$(curl -sS "$BASE/projects" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" | jq -r '.[0].id')
echo "$PROJECT_ID"

# Which platforms can receive posts (connected in the Simplo app).
curl -sS "$BASE/projects/$PROJECT_ID/accounts" \
  -H "Authorization: Bearer $SIMPLO_API_KEY"

2Write caption ideas (costs credits)

Ideas in your brand's voice about a topic. The answer is { reply, ideas }; each idea has a hook, caption, hashtags and an imagePrompt.

POST /generate/captions
jq -n --arg projectId "$PROJECT_ID" \
  '{projectId: $projectId, topic: "Our new autumn menu", platforms: ["instagram"], limit: 3}' \
| curl -sS "$BASE/generate/captions" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- | tee ideas.json

3Make the picture (costs credits)

The answer is { generationId, style, format, imageUrls }. The picture is also saved to the project's image history in the app.

POST /generate/image
# The first idea's imagePrompt describes its picture.
jq --arg projectId "$PROJECT_ID" \
  '{projectId: $projectId, prompt: .ideas[0].imagePrompt, format: "instagram-post"}' ideas.json \
| curl -sS "$BASE/generate/image" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- | tee image.json

4Save it as a dated draft

A draft with a scheduledAt is placed on that day in the app's Plan with status draft_scheduled, and never goes out by itself. The answer is the post, with its id.

POST /posts
# The next whole hour, in UTC, e.g. 2026-11-03T13:00:00Z.
AT=$(jq -nr 'now | floor | . - (. % 3600) + 3600 | todate')

jq -n --arg projectId "$PROJECT_ID" --arg at "$AT" \
  --slurpfile ideas ideas.json --slurpfile image image.json \
  '{projectId: $projectId,
    caption: ($ideas[0].ideas[0] | .caption + "\n\n" + (.hashtags // [] | join(" "))),
    imageUrl: $image[0].imageUrls[0],
    platforms: ["instagram"],
    scheduledAt: $at}' \
| curl -sS "$BASE/posts" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- | tee post.json

5Schedule it

This is the call that puts it on your real account: the post's status becomes scheduled and Simplo publishes it at that hour. Run it soon after step 4: if that hour has passed by then, the answer is 400 invalid_schedule_time; pass a later scheduledAt instead. Until it goes out you can delete it (DELETE /posts/{id}) or unschedule it in the app.

POST /posts/{id}/schedule
# An empty body keeps the draft's time; send {"scheduledAt": "…"} to change it.
curl -sS "$BASE/posts/$(jq -r .id post.json)/schedule" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

The same in Node

Node 18 or later has fetch built in, so this needs no packages.

quickstart.mjs
// quickstart.mjs — Node 18 or later, no packages.
// Run with: SIMPLO_API_KEY=smp_live_… node quickstart.mjs
const BASE = "https://api.getsimplo.ai/v1";

async function simplo(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.SIMPLO_API_KEY}`,
      ...(body !== undefined && { "Content-Type": "application/json" }),
    },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const data = res.status === 204 ? null : await res.json();
  if (!res.ok) throw new Error(`${res.status} ${data.error.code}: ${data.error.message}`);
  return data;
}

const [project] = await simplo("GET", "/projects");

const { ideas } = await simplo("POST", "/generate/captions", {
  projectId: project.id,
  topic: "Our new autumn menu",
  platforms: ["instagram"],
  limit: 3,
});
const idea = ideas[0];

const { imageUrls } = await simplo("POST", "/generate/image", {
  projectId: project.id,
  prompt: idea.imagePrompt,
  format: "instagram-post",
});

// The next whole hour, in UTC.
const HOUR = 60 * 60 * 1000;
const scheduledAt = new Date((Math.floor(Date.now() / HOUR) + 1) * HOUR).toISOString();

const draft = await simplo("POST", "/posts", {
  projectId: project.id,
  caption: `${idea.caption}\n\n${(idea.hashtags ?? []).join(" ")}`,
  imageUrl: imageUrls[0],
  platforms: ["instagram"],
  scheduledAt,
});
console.log(draft.id, draft.status); // draft_scheduled: on the calendar, not sent

const scheduled = await simplo("POST", `/posts/${draft.id}/schedule`, {});
console.log(scheduled.status, scheduled.scheduledAt); // scheduled: goes out at that hour

Recipes

GitHub release → post

Every published release becomes a draft post about its notes, on the calendar at a whole hour at least an hour away (generating takes a minute, and the time must still be in the future if you schedule it). Add the key as a repository secret named SIMPLO_API_KEY and your project id as a repository variable named SIMPLO_PROJECT_ID (Settings → Secrets and variables → Actions), and change instagram to the platforms you have connected. To have the post go out on its own, uncomment the last line.

.github/workflows/simplo.yml
# .github/workflows/simplo.yml
# Needs a repository secret SIMPLO_API_KEY and a repository variable SIMPLO_PROJECT_ID.
name: Post releases to Simplo

on:
  release:
    types: [published]

jobs:
  post:
    runs-on: ubuntu-latest
    steps:
      - name: Draft a post about the release
        env:
          SIMPLO_API_KEY: ${{ secrets.SIMPLO_API_KEY }}
          PROJECT_ID: ${{ vars.SIMPLO_PROJECT_ID }}
          # Through the environment, never pasted into the script, so quotes
          # and backticks in the release notes can't break it.
          TOPIC: ${{ github.event.release.body || github.event.release.name || github.event.release.tag_name }}
          IDEMPOTENCY_KEY: release-${{ github.event.release.id }}
        run: |
          set -euo pipefail
          BASE=https://api.getsimplo.ai/v1
          api() {
            curl -sS --fail-with-body "$@" \
              -H "Authorization: Bearer $SIMPLO_API_KEY" \
              -H "Content-Type: application/json"
          }

          # 1. A caption about the release notes.
          jq -n --arg projectId "$PROJECT_ID" --arg topic "$TOPIC" \
            '{projectId: $projectId, topic: $topic, platforms: ["instagram"], limit: 1}' \
            | api "$BASE/generate/captions" --data-binary @- > ideas.json \
            || { cat ideas.json >&2; exit 1; }

          # 2. A picture for it.
          jq --arg projectId "$PROJECT_ID" '{projectId: $projectId, prompt: .ideas[0].imagePrompt}' ideas.json \
            | api "$BASE/generate/image" --data-binary @- > image.json \
            || { cat image.json >&2; exit 1; }

          # 3. A draft on the calendar, never sent by itself. Its time is a whole hour (UTC) at
          #    least an hour away, so it is still in the future if you schedule it below.
          AT=$(date -u -d "@$(( ($(date -u +%s) / 3600 + 2) * 3600 ))" +%Y-%m-%dT%H:%M:%SZ)
          jq -n --arg projectId "$PROJECT_ID" --arg at "$AT" \
            --slurpfile ideas ideas.json --slurpfile image image.json \
            '{projectId: $projectId,
              caption: ($ideas[0].ideas[0] | .caption + "\n\n" + (.hashtags // [] | join(" "))),
              imageUrl: $image[0].imageUrls[0],
              platforms: ["instagram"],
              scheduledAt: $at}' \
            | api "$BASE/posts" -H "Idempotency-Key: $IDEMPOTENCY_KEY" --data-binary @- > post.json \
            || { cat post.json >&2; exit 1; }
          jq '{id, status, scheduledAt}' post.json

          # 4. To publish it at that hour instead of leaving a draft, uncomment:
          # api "$BASE/posts/$(jq -r .id post.json)/schedule" -d '{}'

Idempotency-Key: release-<id> means one release makes one post. Re-running the job writes new captions and a new picture (and spends credits on them), but the post request then differs from the first one, so it is refused with 422 idempotency_key_reused instead of making a second post.

GitHub release → carousel

A carousel is written and drawn in one call and saved as a draft; it posts to Instagram only. Re-running this job in the same hour sends the same request, so the key replays the first carousel instead of paying for another. Re-running it in a later hour changes scheduledAt, so the request differs and is refused with 422 idempotency_key_reused rather than making a second carousel.

If the slides were drawn but saving the draft failed, the answer is 502 upstream_error with the slide URLs in details.slides. Those slides are paid for, so don't retry: use the URLs. A retry with the same key gets the same 502 back and is not charged again.

.github/workflows/simplo-carousel.yml
# .github/workflows/simplo-carousel.yml
# Needs a repository secret SIMPLO_API_KEY and a repository variable SIMPLO_PROJECT_ID.
name: Release carousel on Simplo

on:
  release:
    types: [published]

jobs:
  carousel:
    runs-on: ubuntu-latest
    steps:
      - name: Draft a carousel about the release
        env:
          SIMPLO_API_KEY: ${{ secrets.SIMPLO_API_KEY }}
          PROJECT_ID: ${{ vars.SIMPLO_PROJECT_ID }}
          TOPIC: ${{ github.event.release.body || github.event.release.name || github.event.release.tag_name }}
          IDEMPOTENCY_KEY: release-carousel-${{ github.event.release.id }}
        run: |
          set -euo pipefail
          BASE=https://api.getsimplo.ai/v1
          api() {
            curl -sS --fail-with-body "$@" \
              -H "Authorization: Bearer $SIMPLO_API_KEY" \
              -H "Content-Type: application/json"
          }

          # Writes and draws the slides and saves them as a draft. Its time is a whole hour (UTC)
          # at least an hour away, so it is still in the future if you schedule it below.
          AT=$(date -u -d "@$(( ($(date -u +%s) / 3600 + 2) * 3600 ))" +%Y-%m-%dT%H:%M:%SZ)
          jq -n --arg projectId "$PROJECT_ID" --arg topic "$TOPIC" --arg at "$AT" \
            '{projectId: $projectId, topic: $topic, slideCount: 5, scheduledAt: $at}' \
            | api "$BASE/generate/carousel" -H "Idempotency-Key: $IDEMPOTENCY_KEY" --data-binary @- > carousel.json \
            || { cat carousel.json >&2; exit 1; }
          jq '{postId, status, slides: [.slides[].imageUrl]}' carousel.json

          # To publish it at that hour instead of leaving a draft, uncomment:
          # api "$BASE/posts/$(jq -r .postId carousel.json)/schedule" -d '{}'

Upload a video

Upload an MP4 or MOV (up to 100 MB) as multipart/form-data with the project in the query string, then create the post with the video the upload returned. This continues the quickstart, using its $BASE, $PROJECT_ID and $AT. A video that is already online can skip the upload: pass its link as videoUrl when you create the post.

POST /videos
# 1. Upload the file (MP4 or MOV, up to 100 MB). The type after ; is needed:
#    curl sends an .mp4 as application/octet-stream otherwise. Use video/quicktime for a .mov.
curl -sS "$BASE/videos?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -F "[email protected];type=video/mp4" | tee video.json

# 2. A dated draft with the returned video. Its first frame is the cover.
jq --arg projectId "$PROJECT_ID" --arg at "$AT" \
  '{projectId: $projectId, caption: "A minute with our new feature.",
    video: .video, platforms: ["instagram", "facebook"], scheduledAt: $at}' video.json \
| curl -sS "$BASE/posts" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @-

Times

Every scheduledAt is ISO 8601 with a UTC offset (Z or ±hh:mm); a time without one is refused. Simplo publishes once an hour, on the hour, so scheduling needs a whole hour in the future. A half-hour time zone's whole hours (India, +05:30) are fine.

A draft's date isn't checked until it is scheduled: you can place a draft on any day, and the rules apply when you call schedule.

scheduledAt
2026-11-03T09:00:00-03:00   9 am in São Paulo (12:00 UTC): accepted
2026-11-03T12:00:00Z        the same moment, written in UTC: accepted
2026-11-03T09:00:00         no offset: always 400 validation_error
2026-11-03T09:20:00-03:00   not on the hour: 400 invalid_schedule_time when scheduling (a draft accepts it)
2026-10-01T09:00:00-03:00   in the past: 400 invalid_schedule_time when scheduling (a draft accepts it)

Credits

Three endpoints cost credits, and the reference marks them (the spec's x-costs-credits): POST /generate/captions (1 credit per call), POST /generate/image (per image) and POST /generate/carousel (the slide copy, plus each picture when pictures is on). Reading, creating posts, scheduling and publishing are free. The prices are in the app under Settings → Credit costs, and GET /credits returns your balance. Credits are taken before the work starts; without enough, the call is refused and nothing is charged:

402
HTTP/1.1 402 Payment Required

{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits for this action.",
    "details": { "allowed": false, "reason": "insufficient_credits", "creditsRemaining": 3, "creditsRequired": 5 }
  }
}

Rate limits

  • 60 requests a minute per key, across all endpoints.
  • 10 requests a minute per key on /generate/*, which each run for tens of seconds.
  • 30 requests a minute per IP address for requests without a valid key, so guessing keys goes nowhere.

Over a limit, the answer is 429 rate_limited with a Retry-After header in seconds. Requests with a key also carry RateLimit and RateLimit-Policy headers. JSON bodies are capped at 1 MB.

Idempotency

A network error after a write leaves you not knowing whether it happened. Send an Idempotency-Key header (any string up to 255 characters) on POST /posts, POST /generate/carousel and POST /posts/{id}/publish, and retry with the same key: within 24 hours, the same key and the same request get the first answer back instead of a second post, with the header Idempotent-Replayed: true.

Idempotency-Key (continuing the quickstart)
jq -n --arg projectId "$PROJECT_ID" --slurpfile image image.json \
  '{projectId: $projectId, caption: "Fresh bread every morning.",
    imageUrl: $image[0].imageUrls[0], platforms: ["instagram"]}' \
| curl -sS -i "$BASE/posts" \
  -H "Authorization: Bearer $SIMPLO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bread-2026-11-03" \
  --data-binary @-

# Run it again within 24 hours: the same post comes back, not a second one,
# with this header in the answer:
#   Idempotent-Replayed: true
  • The same key with a different body, or for a different post, answers 422 idempotency_key_reused.
  • While the first request is still running, a repeat answers 409 idempotency_in_progress; retry shortly.
  • Kept for replay: successes, and the 404, 413 and 422 answers and 400s from the request handler (such as invalid_schedule_time), which a retry can't change. A body that fails validation is refused before the key is looked at, so it isn't kept.
  • Not kept, so a retry with the same key really runs again: 401, 402, 403, 409, 429 and 5xx answers, platform_not_available (remove X in the app and retry), a publish where a platform failed (allSuccess: false), and a publish still in progress. The one 5xx that is kept is a carousel's 502 with details.slides, whose slides are already paid for.
  • Keys belong to the API key that sent them.

Publishing is safe to retry even without a key: a platform the post already reached is skipped. A post that is already live answers 200 with alreadyPublished: true; a send that is still going (a video, for instance) answers 202 with pending: true, and GET /posts/{id} shows how it ended.

Errors

Every error has the same shape, with a stable code to branch on and a message for people:

the answer to "platforms": ["x"]
{
  "error": {
    "code": "validation_error",
    "message": "The body is not valid.",
    "details": [
      {
        "code": "invalid_value",
        "values": ["instagram", "tiktok", "facebook"],
        "path": ["platforms", 0],
        "message": "Invalid option: expected one of \"instagram\"|\"tiktok\"|\"facebook\""
      }
    ]
  }
}
  • invalid_api_key401

    The key is missing, mistyped or revoked. Only API keys work here, never an app sign-in token.

  • forbidden403

    The key can't do this.

  • not_found404

    No such project, post or endpoint on this account. Another account's ids answer the same way.

  • validation_error400

    The path, query or body is wrong; details lists the failing fields. Also a body that isn't JSON or is over 1 MB, and a platform that is switched off right now.

  • invalid_schedule_time400

    scheduledAt is missing, in the past, or not on a whole hour. See Times.

  • platform_not_available400

    The post includes X (added in the app), which the API doesn't publish to. Remove X in the app.

  • already_published409

    The post has gone out, so it can't be edited or rescheduled.

  • publishing_in_progress409

    The post is being sent right now. Read it again in a moment.

  • conflict409

    The request clashes with the post's current state.

  • idempotency_key_reused422

    This Idempotency-Key was already used with a different request.

  • idempotency_in_progress409

    The first request with this Idempotency-Key is still running. Retry shortly.

  • insufficient_credits402

    Not enough credits. details has creditsRemaining and creditsRequired.

  • subscription_inactive403

    The account has no active plan or trial; details.reason says why.

  • video_too_large413

    The video is over 100 MB.

  • rate_limited429

    Too many requests. Wait the number of seconds in Retry-After.

  • upstream_error502

    A social platform or an AI provider failed. Retry after a short wait, except for a carousel whose answer has details.slides: those slides were drawn and paid for, and only saving the draft failed. Don't retry it; use the slide URLs.

  • internal_error500

    Something went wrong on Simplo's side.

Platforms

The API posts to Instagram, Facebook and TikTok: instagram, facebook and tiktok in platforms. Accounts are connected in the app, and GET /projects/{projectId}/accounts shows which are ready. A platform switched off on Simplo's side for a while is refused with validation_error.

X isn't available through the API yet. A post made in the app that includes X can't be scheduled or published from here (platform_not_available). Carousels go to Instagram only; a video goes to Instagram as a Reel, to Facebook and to TikTok.

Reference

Generated from the OpenAPI 3.1 document, which you can download as openapi.json to generate a client or import into Postman. The API serves the same document at GET /v1/openapi.json.

get/projects

List projects listProjects

The projects (one per brand) the key owner has. Most other endpoints need a project id from here.

Responses
get/projects/{projectId}/brand

Get the brand profile getBrand

The project's brand profile: voice, audience, products, colors, typography.

Parameters
  • projectId (path)required
    string

    A project id from List projects.

Responses
get/projects/{projectId}/accounts

List connected accounts listAccounts

The social accounts connected to the project and the platforms that are not. A post only goes out to a connected platform that does not need a reconnect. Accounts are connected in the Simplo app.

Parameters
  • projectId (path)required
    string

    A project id from List projects.

Responses
get/credits

Get the credit balance getCredits

The plan and credits. creditsRemaining is everything that can be spent: the plan's credits, used first, plus bonus credits. planCredits describes the plan's monthly allowance only.

Responses
get/posts

List posts listPosts

The project's posts (drafts, scheduled, published, failed, missed), newest first. A draft_scheduled post is a draft placed on its day: it never sends by itself.

Parameters
  • projectId (query)required
    string

    The project whose posts to list.

  • month (query)
    string

    Only posts of this month, as YYYY-MM.

Responses
post/posts

Create a post createPost

Saves a single-image or video post as a draft (the default) or scheduled. A draft with scheduledAt is placed on that day in the plan (draft_scheduled) and never sends by itself. A scheduled post is published automatically at scheduledAt, which must be in the future and on the hour. A video post takes videoUrl (a public MP4 or MOV link, up to 100 MB) or an already uploaded video; its first frame is the cover. Not for X. Pass at most one of imageUrl, video and videoUrl.

Parameters
  • Idempotency-Key (header)
    string · up to 255 characters

    Up to 255 characters. Repeating a request with the same key within 24 hours returns the first answer instead of doing the work again.

JSON body
  • projectIdrequired
    string

    The project that owns the post.

  • captionrequired
    string

    The post text.

  • imageUrl
    string (uri)

    A public picture URL. Mutually exclusive with video and videoUrl.

  • video
    object

    A video already uploaded with Upload a video. Mutually exclusive with imageUrl and videoUrl.

  • videoUrl
    string (uri)

    A public MP4 or MOV link for Simplo to fetch. Mutually exclusive with imageUrl and video.

  • platformsrequired
    array of instagram | tiktok | facebook · at least 1

    Where the post goes. X is not supported.

  • status
    draft | scheduled · default "draft"

    draft saves it; scheduled publishes it at scheduledAt.

  • scheduledAt
    string (date-time)

    ISO 8601 with UTC offset. Scheduling needs a future whole hour; a draft's date isn't checked until it is scheduled.

Responses
get/posts/{id}

Get a post getPost

One post, with its status and any publish error.

Parameters
  • id (path)required
    string
Responses
patch/posts/{id}

Edit a post updatePost

Changes a post's caption, picture, video or platforms; send only what changes. scheduledAt places a draft on a day without scheduling it; a scheduled post's time is changed with the schedule endpoint. Editing a scheduled post changes what goes out at its time. A post that has gone out, or is going out now, cannot be edited. Send at least one field, and at most one of imageUrl, video and videoUrl.

Parameters
  • id (path)required
    string
JSON body
  • caption
    string

    The new post text.

  • imageUrl
    string (uri)

    A public picture URL. Mutually exclusive with video and videoUrl.

  • video
    object

    A video already uploaded with Upload a video. Mutually exclusive with imageUrl and videoUrl.

  • videoUrl
    string (uri)

    A public MP4 or MOV link for Simplo to fetch. Mutually exclusive with imageUrl and video.

  • platforms
    array of instagram | tiktok | facebook · at least 1

    The new list of platforms.

  • scheduledAt
    string (date-time)

    ISO 8601 with UTC offset. Scheduling needs a future whole hour; a draft's date isn't checked until it is scheduled.

Responses
delete/posts/{id}

Delete a post deletePost

Deletes a post from Simplo; this cannot be undone. A published post stays on the social platforms: only Simplo's record and its analytics go. Deleting a scheduled post stops it from going out.

Parameters
  • id (path)required
    string
Responses
post/generate/captionscosts credits

Generate caption ideas generateCaptions

On-brand post ideas (hook, caption, hashtags, image prompt) for a topic, saved as a research session. Costs credits. Needs a completed brand profile.

JSON body
  • projectIdrequired
    string

    The project whose brand the ideas follow.

  • topicrequired
    string

    What the posts are about.

  • platforms
    array of instagram | tiktok | facebook

    Shape the ideas for these platforms.

  • limit
    integer · 1 to 10

    How many ideas, 1 to 10.

Responses
post/generate/imagecosts credits

Generate images generateImage

On-brand images for a post, saved to the project's image history. Costs credits (more for more images). Style "ad-layout" sets real type over a photo; other styles draw text badly. Every model tier currently runs as "standard".

JSON body
  • projectIdrequired
    string

    The project whose brand the images follow.

  • promptrequired
    string

    What the image shows.

  • format
    instagram-post | instagram-story | instagram-portrait | tiktok | x-post | facebook-post | facebook-cover · default "instagram-post"

    Picture sizes, not platforms: x-post is a picture shaped for X, and X itself isn't a platform the API posts to.

  • style
    ad-layout | studio | candid | cinematic | retro-film | flat-illustration | hand-drawn | render-3d | bold-type | minimal · default "studio"
  • adBackground
    image | brand · default "image"
  • modelTier
    fast | standard | premium · default "standard"
  • imageCount
    integer · 1 to 4 · default 1

    How many images, 1 to 4. Each one costs credits.

  • includeLogo
    boolean · default false
Responses
post/generate/carouselcosts credits

Generate a carousel generateCarousel

Writes and draws an Instagram carousel about a topic and saves it as a draft post. Costs credits: the slide copy, plus each picture when pictures are on. It is never published or scheduled here. A slide in overflowSlides has more text than fits. If saving the draft fails after the slides were drawn, the answer is 502 with the slide URLs in details.slides: those slides are paid for, so use the URLs rather than retrying (a retry with the same Idempotency-Key gets the same answer).

Parameters
  • Idempotency-Key (header)
    string · up to 255 characters

    Up to 255 characters. Repeating a request with the same key within 24 hours returns the first answer instead of doing the work again.

JSON body
  • projectIdrequired
    string

    The project that owns the carousel.

  • topicrequired
    string

    What the carousel is about.

  • slideCount
    integer · 2 to 10 · default 5

    How many slides, 2 to 10.

  • style
    editorial | clean | boldPhoto | dark
  • pictures
    boolean · default false

    Draw a picture on each slide. Costs extra credits.

  • caption
    string

    The post text; written for you when left out.

  • scheduledAt
    string (date-time)

    ISO 8601 with UTC offset. Scheduling needs a future whole hour; a draft's date isn't checked until it is scheduled.

Responses
post/posts/{id}/schedule

Schedule a post schedulePost

Schedules a post, or moves a scheduled one to a new time. It is published to the connected accounts automatically at scheduledAt, which must be in the future and on the hour; without it the post keeps the time it has. Works on drafts, scheduled, missed and failed posts. A post that already went out cannot be rescheduled. Refused for a post that includes X.

Parameters
  • id (path)required
    string
JSON body
  • scheduledAt
    string (date-time)

    ISO 8601 with UTC offset. Scheduling needs a future whole hour; a draft's date isn't checked until it is scheduled.

Responses
post/posts/{id}/publish

Publish a post now publishPost

Publishes the post to its platforms immediately, on the real accounts; this cannot be undone. Safe to retry: a platform already published is skipped. The answer lists a result per platform and allSuccess. A send that is still in progress (a video, or a post going through a posting partner) answers 202 with pending: true; read the post for the result. A post that is already live answers 200 with alreadyPublished: true. Refused for a post that includes X.

Parameters
  • id (path)required
    string
  • Idempotency-Key (header)
    string · up to 255 characters

    Up to 255 characters. Repeating a request with the same key within 24 hours returns the first answer instead of doing the work again.

Responses
post/videos

Upload or import a video uploadVideo

Hosts a video for a video post. Send an MP4 or MOV (up to 100 MB) as multipart/form-data in the file field, with projectId in the query string, or send JSON with a public url for Simplo to fetch, with projectId in the body or the query. The upload is streamed, never held in memory. Pass the returned video to Create a post as video.

Parameters
  • projectId (query)
    string

    The project that owns the video. Required for an upload.

JSON body
  • projectId
    string

    Required here or in the query string.

  • urlrequired
    string (uri)

    A public MP4 or MOV link for Simplo to fetch.

Or a multipart/form-data body
  • filerequired
    string (binary)

    MP4 or MOV, up to 100 MB

Responses
get/openapi.jsonno key needed

Get the OpenAPI document getOpenApi

This API described as an OpenAPI 3.1 document. No API key needed.

Responses