ReelGuud API

Upload media, generate reels and download the finished MP4s from your own tools. The API is part of the Pro plan.

Authentication

Every API request is authenticated with an API key. Keys start with rg_live_ and are sent as a bearer token in the Authorization header:

Authorization: Bearer rg_live_…

Getting a key

  1. Log in and open Account.
  2. In the API keys card, give the key a name (for example Zapier) and click Create.
  3. Copy the key straight away — it is shown once. We store only a hash, so a lost key can’t be recovered; revoke it and create a new one.

API keys are available on the Pro plan. If you don’t see the card, upgrade to Pro.

Example

List your reels:

curl https://reelguud-backend.guud.ai/api/reels \
  -H "Authorization: Bearer rg_live_YOUR_KEY"

Errors. A key that is malformed, unknown, revoked, expired, or whose account is no longer on Pro is refused with 401 and {"error":"invalid or revoked API key"}. Each key is limited to 60 requests per minute.

Keep keys secret: use them only from your server or automation tool, never in browser or mobile app code. Revoke a key from the API keys card at any time.

Endpoints

All endpoints live under the API base URL https://reelguud-backend.guud.ai and require Authorization: Bearer rg_live_…. Responses are JSON. The typical flow is: upload media, wait for it to be tagged, then generate a reel.

POST /api/upload

Uploads one or more images or video clips to your library. Each file becomes an asset with processing_status: "uploading". ReelGuud then tags it with AI and moves it to ready; only ready assets are used to generate reels.

Auth

Authorization: Bearer rg_live_… (Pro plan).

Request body

Send multipart/form-data. Any field name works, and you can include several files in one request.

FieldTypeDescription
filefile, requiredAn image (image/*) or video (video/*). Repeat for more files. Each file can be up to 200 MB.

Example request

curl -X POST https://reelguud-backend.guud.ai/api/upload \
  -H "Authorization: Bearer rg_live_YOUR_KEY" \
  -F "[email protected]" \
  -F "[email protected]"

Example response — 201 Created

{
  "items": [
    {
      "asset_id": 4821,
      "external_id": "6f1c2e0a-…",
      "owner_user_id": 312,
      "asset_type": "image",
      "mime_type": "image/jpeg",
      "file_name": "beach.jpg",
      "file_extension": "jpg",
      "file_size_bytes": 1843200,
      "url": "https://reelguud-backend.guud.ai/uploads/1727790000000-9c1e4f2ab3d07e65.jpg",
      "thumbnail_url": "",
      "title": "beach.jpg",
      "tags_json": null,
      "processing_status": "uploading",
      "created_at": "2026-10-01T14:05:12.000Z",
      "updated_at": "2026-10-01T14:05:12.000Z"
    }
  ],
  "count": 1
}

Each item is the full asset record (abridged above). Video assets also get a poster frame in thumbnail_url.

Errors

StatusMeaning
400No files were sent, or a file isn’t an image or video.
401The API key is missing, invalid or revoked.
402Your plan’s asset limit is reached: {"error", "limit", "used", "upgrade_tier"}.
413A file is over 200 MB.
429Rate limited. Wait retry_after_s seconds and try again.

POST /api/reels

Queues a reel. ReelGuud picks matching clips from your ready assets and renders an MP4. The reel starts as status: "pending" and moves to processing, then ready (with an output_url) or failed. You need at least 10 ready assets.

Auth

Authorization: Bearer rg_live_… (Pro plan).

Request body

Send application/json. You need title and theme, unless you send batch_count or a template_id that supplies them.

FieldTypeDescription
titlestring, required*Name of the reel.
themestring, required*What the reel is about, for example "summer road trip". Used to choose matching clips.
presetstringTarget platform format: reels (default), tiktok or shorts.
keywordsstring[]Extra keywords to help pick clips.
source_asset_idsnumber[]Specific asset_ids to build the reel from.
batch_countinteger 1–7Generate several reels at once on AI-suggested themes from your library. When set, title and theme are not needed.
template_idintegerA saved template you own or share through your workspace. Fills in any field you leave out.
music_enabledbooleanAdd matching background music. Default true.
overlay_scriptstringTimed captions, one per line as text:seconds.
overlay_fontstringCaption font style name.
overlay_animationstringCaption animation, or none.
effect_idsstring[]Video effects to apply.

An invalid preset, overlay_font, overlay_animation or effect_ids value returns 400 with the list of allowed values.

Example request

curl -X POST https://reelguud-backend.guud.ai/api/reels \
  -H "Authorization: Bearer rg_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Summer road trip","theme":"summer road trip","preset":"reels"}'

Example response — 201 Created

{
  "reel_job_id": 1907,
  "user_id": 312,
  "title": "Summer road trip",
  "theme": "summer road trip",
  "preset": "reels",
  "keywords_json": null,
  "source_asset_ids_json": null,
  "output_url": "",
  "duration_ms": null,
  "status": "pending",
  "error_message": "",
  "music_enabled": 1,
  "template_id": null,
  "created_at": "2026-10-01T14:06:40.000Z",
  "updated_at": "2026-10-01T14:06:40.000Z"
}

The response is the reel record (abridged above). With batch_count the response is {"batch_count": 3, "jobs": [ … ]}, one record per reel. To be notified when a reel is ready, use the reel.ready webhook.

Errors

StatusMeaning
400A required field is missing or a value is invalid.
401The API key is missing, invalid or revoked.
402Your plan’s monthly reel limit is reached: {"error", "limit", "used", "upgrade_tier"}.
403 / 404The template_id isn’t yours, or doesn’t exist.
422Fewer than 10 ready assets: {"error": "Insufficient library", "ready_count": 4}.
429Rate limited. Wait retry_after_s seconds and try again.

GET /api/reels

Lists your reels, newest first, one page at a time.

Auth

Authorization: Bearer rg_live_… (Pro plan).

Query parameters

ParameterTypeDescription
statusstringOnly return reels in this state: pending, processing, ready or failed.
pageintegerPage number, starting at 1 (default).
limitinteger 1–100Reels per page. Default 20; values over 100 are capped.

Example request

curl "https://reelguud-backend.guud.ai/api/reels?status=ready&page=1&limit=20" \
  -H "Authorization: Bearer rg_live_YOUR_KEY"

Example response — 200 OK

{
  "items": [
    {
      "reel_job_id": 1907,
      "user_id": 312,
      "title": "Summer road trip",
      "theme": "summer road trip",
      "preset": "reels",
      "output_url": "https://reelguud-backend.guud.ai/uploads/reels/reel-1907.mp4",
      "duration_ms": 24000,
      "status": "ready",
      "error_message": "",
      "music_enabled": 1,
      "posted_url": null,
      "view_count": 3,
      "download_count": 1,
      "posted": false,
      "created_at": "2026-10-01T14:06:40.000Z",
      "updated_at": "2026-10-01T14:08:02.000Z"
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20
}

Each item is the full reel record (abridged above) plus view_count, download_count and posted. total counts all your reels, whatever the status filter; keep requesting the next page until items is empty.

Errors

StatusMeaning
401The API key is missing, invalid or revoked.
429Rate limited. Wait retry_after_s seconds and try again.

GET /api/reels/{id}

Gets one reel by its reel_job_id. Poll this to follow a reel from pending to ready — or skip polling and use the reel.ready webhook.

Auth

Authorization: Bearer rg_live_… (Pro plan). You can only read your own reels.

Path parameters

ParameterTypeDescription
idinteger, requiredThe reel’s reel_job_id, as returned by POST /api/reels.

Example request

curl https://reelguud-backend.guud.ai/api/reels/1907 \
  -H "Authorization: Bearer rg_live_YOUR_KEY"

Example response — 200 OK

{
  "reel_job_id": 1907,
  "user_id": 312,
  "title": "Summer road trip",
  "theme": "summer road trip",
  "preset": "reels",
  "output_url": "https://reelguud-backend.guud.ai/uploads/reels/reel-1907.mp4",
  "duration_ms": 24000,
  "status": "ready",
  "error_message": "",
  "music_enabled": 1,
  "posted_url": null,
  "posted": false,
  "created_at": "2026-10-01T14:06:40.000Z",
  "updated_at": "2026-10-01T14:08:02.000Z"
}

The full reel record (abridged above). When status is failed, error_message says why.

Errors

StatusMeaning
401The API key is missing, invalid or revoked.
403The reel belongs to another account.
404No reel with this id.
429Rate limited. Wait retry_after_s seconds and try again.

GET /api/reels/{id}/download

Downloads a ready reel as an MP4 file. Each call counts as a download in your reel analytics.

Auth

Authorization: Bearer rg_live_… (Pro plan). You can only download your own reels.

Parameters

ParameterTypeDescription
idinteger, required (path)The reel’s reel_job_id.
variantinteger (query)Download one of the reel’s alternate renders instead of the main one. Index into metadata_json.variants, starting at 0.

Example request

curl -L https://reelguud-backend.guud.ai/api/reels/1907/download \
  -H "Authorization: Bearer rg_live_YOUR_KEY" \
  -o summer-road-trip.mp4

Response — 200 OK (binary)

The body is the MP4 file itself, not JSON:

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="Summer-road-trip.mp4"

<MP4 bytes>

Errors

Errors are JSON: {"error": "…"}.

StatusMeaning
400The id isn’t a number.
401The API key is missing, invalid or revoked.
403The reel belongs to another account.
404No reel with this id, or no such variant.
409The reel isn’t ready yet.
410The file is no longer available.
429Rate limited. Wait retry_after_s seconds and try again.

Webhooks

Instead of polling, ReelGuud can call your server when a reel finishes. Set an https:// URL in the Webhook card on Account. Saving the URL shows a signing secret that starts with whsec_; keep it on your server. Saving again creates a new secret, and the old one stops working.

reel.ready

Sent once when a reel you generated changes to status: "ready". ReelGuud sends a POST to your URL with a JSON body:

{
  "event": "reel.ready",
  "id": 1907,
  "title": "Summer road trip",
  "output_url": "https://reelguud-backend.guud.ai/uploads/reels/reel-1907.mp4",
  "thumbnail_url": ""
}

It is shown formatted here; the actual body is sent as compact JSON on a single line.

FieldTypeDescription
eventstringAlways reel.ready.
idintegerThe reel’s reel_job_id. Use it with GET /api/reels/{id} and GET /api/reels/{id}/download.
titlestringThe reel’s title, or its theme if it has no title.
output_urlstringURL of the finished MP4.
thumbnail_urlstringPoster image URL, or "" if there isn’t one.

Request headers

POST /your/webhook/path HTTP/1.1
Content-Type: application/json
User-Agent: ReelGuud-Webhooks/1
X-ReelGuud-Signature: 5d41402abc4b2a76b9719d911017c592a0f6b3e1c4d7e8f9a0b1c2d3e4f5a6b7

Delivery and retries

  • Reply with any 2xx status within 10 seconds to accept the event.
  • Any other status, a timeout or a network error counts as failed. ReelGuud tries up to 3 times in total, waiting a little longer before each retry.
  • Redirects are not followed. A 3xx counts as failed, so use your final URL.
  • Retries send the same body and the same signature. The same event can arrive more than once, so ignore an id you have already handled.

Verifying the signature

Every request includes an X-ReelGuud-Signature header. Check it before you trust the body; it proves the request came from ReelGuud and wasn’t changed.

  1. Header: read X-ReelGuud-Signature. It is a 64-character lowercase hex string.
  2. Signed payload: the raw request body, byte for byte as you received it. Don’t parse the JSON and serialize it again first: changes to key order or spacing change the digest. No timestamp or other prefix is added.
  3. Digest: compute HMAC-SHA256 of that raw body, keyed with your full webhook secret (including the whsec_ prefix, as UTF-8), and hex-encode it.
  4. Compare: compare your hex digest with the header value using a constant-time comparison. If they don’t match, reply 401 and ignore the request.

Node.js (Express)

const crypto = require('crypto');
const express = require('express');
const app = express();

const SECRET = process.env.REELGUUD_WEBHOOK_SECRET; // whsec_…

// express.raw keeps the exact bytes that were signed.
app.post('/webhooks/reelguud', express.raw({ type: 'application/json' }), (req, res) => {
  const received = req.get('X-ReelGuud-Signature') || '';
  const expected = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  const ok = received.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
  if (!ok) return res.status(401).send('bad signature');

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.event === 'reel.ready') {
    console.log(`Reel ${event.id} is ready: ${event.output_url}`);
  }
  res.sendStatus(200);
});

Python

import hashlib, hmac

def is_valid_reelguud_signature(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

Check a signature by hand

Save a request body to body.json and compare the output with the header:

openssl dgst -sha256 -hmac "whsec_YOUR_SECRET" -hex < body.json

← Back to ReelGuud

Powered by guud.ai