Developers

Your workspace,
from your own code.

Upload a recording, edit it into short clips, write the captions and post to every channel with an API key, a webhook, or an AI assistant. Included on every paid plan.

Overview

The API is REST over https with JSON in and out, at https://app.ninetytenlabs.com/api/v1. The same actions are available to AI assistants through an MCP server at https://app.ninetytenlabs.com/mcp, and webhooks tell your server when something happens. Everything a key does happens in its own workspace and nowhere else.

Videos

Upload and edit

Resumable uploads up to 5 GB or import from a link, then a quick cut with face-aware captions, edit energy and list templates.

Writing

Scripts and captions

Scripts from an idea, carousels, and a caption written for each platform from what the video says.

Publishing

Post and automate

Post now or schedule, per-post captions and sounds, comment-to-DM automations, and results per post.

Keys and scopes

Make a key in the app under Settings, Developer, and send it on every request: Authorization: Bearer ntl_live_.... A key is shown once, belongs to one workspace, and can be revoked at any time.

ScopeWhat it allowsWho can give it
readVideos, clips, posts, results, credits, offers and guides.Any seat.
writeUploads, imports, edits, scripts, carousels and captions. Paid jobs spend the workspace's credits.Owners, managers and creators, on a paid plan.
publishPosting, scheduling and comment-to-DM automations.Owners and managers. A creator's publish key asks for approval on each post instead.

A key acts with the seat of the person who made it, never more than its scopes: if that person's seat changes, the key changes with it, and if they leave the workspace the key stops working. API and MCP access come with every paid plan. During a trial, keys read only.

Quick start

From a recording on your machine to posted on two channels. Webhooks save you from polling in step 4.

# 1. A slot for your recording (makes the video too)
curl -X POST https://app.ninetytenlabs.com/api/v1/uploads \
  -H "Authorization: Bearer $NTL_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"title":"Pricing talk","contentType":"video/mp4","size":48211337}'

# 2. Send the file to upload.url, then confirm it arrived
curl -X PUT "$UPLOAD_URL" -H "Content-Type: video/mp4" --data-binary @talk.mp4
curl -X POST https://app.ninetytenlabs.com/api/v1/uploads/$UPLOAD_ID/complete -H "Authorization: Bearer $NTL_KEY" \
  -H "Content-Type: application/json" -d '{}'

# 3. See the price, then start the edit at that price
curl https://app.ninetytenlabs.com/api/v1/productions/$VIDEO_ID/quote -H "Authorization: Bearer $NTL_KEY"
curl -X POST https://app.ninetytenlabs.com/api/v1/productions/$VIDEO_ID/edit \
  -H "Authorization: Bearer $NTL_KEY" -H "Content-Type: application/json" \
  -d '{"options":{"energy":"high"},"confirmCredits":120}'

# 4. When production.ready arrives (or status says ready): captions, then post
curl -X POST https://app.ninetytenlabs.com/api/v1/productions/$VIDEO_ID/captions \
  -H "Authorization: Bearer $NTL_KEY" -H "Content-Type: application/json" \
  -d '{"platforms":["instagram","tiktok"]}'
curl -X POST https://app.ninetytenlabs.com/api/v1/productions/$VIDEO_ID/posts \
  -H "Authorization: Bearer $NTL_KEY" -H "Content-Type: application/json" \
  -d '{"timing":"now","posts":[{"accountId":"$IG_ID","platform":"instagram"},{"accountId":"$TT_ID","platform":"tiktok"}]}'

Credits and retries

Paid calls say so in the reference. Each one works out its price first, holds the credits before any work starts, and gives them back if the work fails. The answer includes credits (what it cost) and balance. Send confirmCredits with the price you showed your user: if the price is different, nothing runs and the current price comes back with the code price_changed. Price anything first with GET /api/v1/credits/quote, or an edit with the video's quote.

Every POST accepts an Idempotency-Key header. Retry with the same key and the same body, and you get the first answer back (marked with Idempotent-Replayed: true) instead of the work, or the charge, happening twice. Successful answers are kept for 24 hours; a failed call can be retried with the same key.

Errors

Every error has the same shape, and never passes on another service's wording:

{
  "error": {
    "code": "insufficient_credits",
    "message": "This needs 120 credits. Add credits and try again.",
    "hint": "Add credits in Settings, Billing and credits, then try again."
  }
}
StatusCodes
400invalid_request, url_not_allowed, url_not_video, url_unreachable
401unauthorized
402insufficient_credits
403insufficient_scope, seat_not_allowed, plan_required
404not_found, unknown_route
409conflict, not_ready, price_changed, needs_context, idempotency_in_progress, not_connected, reconnect
422idempotency_mismatch
429rate_limited (wait for Retry-After seconds)
503service_down, uploads_unavailable (nothing was lost or charged; try again)

Endpoints

Also as an OpenAPI 3.1 document for code generators and API clients.

WorkspaceAccountsUploadsVideosWritingPublishingAutomationsResultsCreditsContent

Workspace

GET/api/v1/me

The key and its workspace. The workspace this key belongs to, its scopes, the seat it acts with, whether the plan allows writing, and the rate limit.

scope: read

Returns workspace, key, seat, publishing, plan, rateLimit.

Accounts

GET/api/v1/accounts

Connected accounts. The social accounts connected to this workspace. Use an account id when posting.

scope: read

Returns accounts.

Uploads

POST/api/v1/uploads

Get an upload slot. A signed slot for one recording (MP4, MOV or WebM up to 5 GB). Send the file with one PUT to upload.url, or resumably (tus) to resumable.endpoint with the headers given. Then call complete. Makes a new video when productionId is left out.

scope: write

Parameters and body
FieldTypeWhat it is
productionIdstring (uuid)An existing video waiting for footage. Left out, a new video is made.
titlestringup to 120 characters
template"talking_head" | "list" | "tier_ranking" | "challenge" | "broll"
contentType required"video/mp4" | "video/quicktime" | "video/webm" | "video/x-m4v"
size requiredintegerat most 5368709120Exact size in bytes.

Returns uploadId, productionId, expiresInSeconds, upload, resumable.

POST/api/v1/uploads/{id}/complete

Confirm an upload arrived. Checks the file arrived whole at the size promised and attaches it to its video. Nothing is edited or charged yet; start the edit next.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)

Returns production.

Videos

GET/api/v1/productions

Videos. This workspace's videos, newest first.

scope: read

Parameters
FieldTypeWhat it is
limit queryinteger1 to 100

Returns productions.

POST/api/v1/productions

Make a video, or import one from a link. Makes a video waiting for footage, or, with sourceUrl, imports a recording from a public https link (checked before it is accepted). Choices for the edit are saved; nothing is charged until the edit starts.

scope: write

Parameters and body
FieldTypeWhat it is
title requiredstringup to 120 characters
template"talking_head" | "list" | "tier_ranking" | "challenge" | "broll"default "talking_head"list and tier_ranking add a ranked board with logos.
briefstringup to 4000 charactersWhat the video is about, for the editor.
sourceUrlstring (uri)up to 2048 charactersA public https link to a video file.
timezonestringup to 80 characters
optionsobject
options.engine"auto" | "fast_cut" | "deep"auto picks the quick cut for recordings up to 10 minutes and the full edit for longer ones.
options.energy"calm" | "standard" | "high" | "jumpcut" | "static"How much the picture moves, every move eased. calm (Calm): a slow push only. standard (Smooth, the default): always gently moving, a soft punch-in on key lines. high (Lively): stronger punch-ins on more beats. jumpcut: the framing steps at each cut with a slow creep between. static: no motion.
options.layout"auto" | "fill" | "fit"For horizontal footage: fill crops to vertical, fit keeps the whole frame with text above and below. auto: fit for horizontal, fill for vertical.
options.brollobjectB-roll cutaways. Off unless asked for; adds to the price.
options.broll.imagesboolean
options.broll.videoboolean
options.loopbooleanA Loop: about 8 seconds of the footage that plays on repeat, one big line of text on screen and a sound under it, for footage with no talking. Left out, a recording with no talking, or a brief that asks for a POV, b-roll or text over footage, becomes one on its own.

Returns production.

GET/api/v1/productions/{id}

Video status and progress. Where a video is: its status, the edit stage, clips rendered, and any message.

scope: read

Parameters
FieldTypeWhat it is
id path requiredstring (uuid)

Returns production, progress.

GET/api/v1/productions/{id}/quote

Price an edit. What editing this recording will cost, read from the file itself, and which editor it gets.

scope: read

Parameters
FieldTypeWhat it is
id path requiredstring (uuid)

Returns engine, job, credits, balance, seconds.

POST/api/v1/productions/{id}/edit

Start the edit. Starts editing an uploaded or imported recording into short clips. Priced from the recording; the credits are held when it starts and released if it fails.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)
optionsobject
options.engine"auto" | "fast_cut" | "deep"auto picks the quick cut for recordings up to 10 minutes and the full edit for longer ones.
options.energy"calm" | "standard" | "high" | "jumpcut" | "static"How much the picture moves, every move eased. calm (Calm): a slow push only. standard (Smooth, the default): always gently moving, a soft punch-in on key lines. high (Lively): stronger punch-ins on more beats. jumpcut: the framing steps at each cut with a slow creep between. static: no motion.
options.layout"auto" | "fill" | "fit"For horizontal footage: fill crops to vertical, fit keeps the whole frame with text above and below. auto: fit for horizontal, fill for vertical.
options.brollobjectB-roll cutaways. Off unless asked for; adds to the price.
options.broll.imagesboolean
options.broll.videoboolean
options.loopbooleanA Loop: about 8 seconds of the footage that plays on repeat, one big line of text on screen and a sound under it, for footage with no talking. Left out, a recording with no talking, or a brief that asks for a POV, b-roll or text over footage, becomes one on its own.
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns production, credits, balance.

POST/api/v1/productions/{id}/edit/accept

Accept a new edit price. An edit never costs more than the price confirmed. When the recording turns out dearer (longer than it looked, a length that could not be read, or it needs the longer edit), nothing is charged and the video waits with choice set. This starts it at the new price; if it now costs less, the lower price is charged.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)
confirmCredits requiredintegerMust equal choice.credits, or nothing runs and the current price comes back.

Returns production, credits, balance.

POST/api/v1/productions/{id}/edit/keep

Keep a video as it is. Answers a waiting price choice with no. Nothing starts and nothing is charged; the recording stays on the video.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)

Returns production.

GET/api/v1/productions/{id}/clips

Clips and versions. The finished clips of a video. Post one by its id.

scope: read

Parameters
FieldTypeWhat it is
id path requiredstring (uuid)

Returns clips.

POST/api/v1/productions/{id}/clips/{key}/look

Change a clip's look. Changes one quick-cut clip (layout, headline, energy, caption position) and renders just that clip again.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)
key path requiredstring
framing"fill" | "fit"
band"solid" | "blur"
headlinestringup to 80 characters
sublinestringup to 90 characters
bottom"captions" | "subline"
energy"calm" | "standard" | "high" | "jumpcut" | "static"
captionYnumber or null
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns clip, credits, balance.

Writing

POST/api/v1/writing/script

Write a script from an idea. A recordable script (three hooks, body, call to action) written from an idea and this workspace's brand brain.

scope: write

Parameters and body
FieldTypeWhat it is
idea requiredstringup to 6000 characters
audiencestringup to 1000 characters
offerstringup to 1000 characters
template"talking_head" | "list" | "tier_ranking" | "challenge" | "broll"default "talking_head"
purpose"reach" | "connection" | "trust" | "conversion"default "reach"
researchbooleanLook up current facts first.
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns script, credits, balance.

POST/api/v1/writing/carousel

Write a carousel. A carousel (title, caption, 2 to 10 slides) written from an idea and this workspace's brand brain.

scope: write

Parameters and body
FieldTypeWhat it is
idea requiredstringup to 6000 characters
audiencestringup to 1000 characters
offerstringup to 1000 characters
purpose"reach" | "connection" | "trust" | "conversion"default "reach"
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns carousel, credits, balance.

POST/api/v1/productions/{id}/captions

Write captions per platform. One caption per platform (and a YouTube title), written from what the video says. The first write for a video is free; rewrites are charged once per call.

scope: write

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)
platforms requiredarray of "instagram" | "facebook" | "tiktok" | "youtube" | "linkedin" | "twitter" | "threads" | "bluesky" | "pinterest" | "reddit" | "googlebusiness" | "snapchat" | "telegram" | "whatsapp" | "discord" | "slack"up to 8 items
versionstringup to 200 characters
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns captions, credits, balance.

Publishing

POST/api/v1/productions/{id}/posts

Post now or schedule. Posts a finished video to chosen accounts, now or at set times, each post with its own caption and sound. A caption left out uses the one saved for that platform. Posting is free on every platform. A key made by a creator seat asks for approval instead of posting.

scope: publishcreator seat: asks for approval

Parameters and body
FieldTypeWhat it is
id path requiredstring (uuid)
timing required"now" | "scheduled"
timezonestringup to 80 characters
posts requiredarray of objectsup to 20 items
posts[].accountId requiredstringup to 100 characters
posts[].platform required"instagram" | "facebook" | "tiktok" | "youtube" | "linkedin" | "twitter" | "threads" | "bluesky" | "pinterest" | "reddit" | "googlebusiness" | "snapchat" | "telegram" | "whatsapp" | "discord" | "slack"
posts[].versionstringup to 200 charactersA clip id from list clips. Left out: the chosen cut, else the first clip.
posts[].atstring (date-time)When to post (scheduled only).
posts[].captionstringup to 2200 characters
posts[].titlestringup to 100 charactersYouTube title.
posts[].delivery"public" | "trial" | "trial_auto"Instagram: public feed, trial reel, or trial that goes public if it performs.
posts[].soundobjectA sound for this post only.
againbooleanPost again to an account this video is already posted or scheduled on. Left out: those posts are refused, so nothing goes out twice by accident.
confirmCreditsintegerThe price you agreed to, in credits. When sent it must equal the current quote, or nothing runs and the new price comes back.

Returns posts, approval, credits, balance.

GET/api/v1/posts

Posts. Scheduled and published posts, newest first.

scope: read

Parameters
FieldTypeWhat it is
limit queryinteger1 to 100

Returns posts.

Automations

GET/api/v1/automations

Comment-to-DM automations. Automations on this workspace's Instagram and Facebook accounts, with their counts.

scope: read

Returns automations.

POST/api/v1/automations

Make a comment-to-DM automation. When someone comments a keyword (or replies to a story), they get a DM with up to three link buttons, plus an optional public reply. Needs a manager or owner seat.

scope: publish

Parameters and body
FieldTypeWhat it is
name requiredstringup to 80 characters
accountId requiredstringup to 80 characters
trigger"comment" | "story_reply"
platformPostIdstringup to 120 charactersOne post only. Left out: every post.
keywordsarray of stringup to 20 items
matchMode"contains" | "word" | "exact"
alsoMatchInDmsboolean
dmMessage requiredstringup to 640 characters
buttonsarray of objectsup to 3 items
buttons[].title requiredstringup to 20 characters
buttons[].url requiredstring (uri)up to 2000 characters
commentRepliesarray of stringup to 5 items
dmDelaySecondsinteger0 to 3600

Returns automation.

PATCH/api/v1/automations/{id}

Arm or pause an automation. Turns an automation on (armed) or off. Needs a manager or owner seat.

scope: publish

Parameters and body
FieldTypeWhat it is
id path requiredstring
active requiredboolean

Returns automation.

Results

GET/api/v1/analytics

Post and account results. Views, likes, comments, shares and saves for recent posts on this workspace's accounts. A number the platform did not report is null, never zero.

scope: read

Returns retrievedAt, totalPosts, posts.

Credits

GET/api/v1/credits

Balance and prices. The credit balance this workspace spends from, its monthly budget, the price of every job (a credit is a cent), and when the credits in the balance expire, soonest first. Plan credits expire at the next monthly grant, top-ups and bonuses 90 days after they land, free credits after 30 days without activity; the soonest to expire are spent first.

scope: read

Returns balance, monthlyBudget, prices, lots.

GET/api/v1/credits/quote

Price a job. What one job costs before you run it. For an edit of a recording you already uploaded, use the video's quote instead. A month plan is priced by its pieces (pieces, 30 when left out); a caption restyle and a graphics plan by the finished video's length past 10 minutes (sourceMinutes).

scope: read

Parameters
FieldTypeWhat it is
job query required"written_post" | "video_script" | "carousel" | "clip" | "fast_cut" | "fast_cut_rerender" | "long_form" | "review_reply" | "month_plan" | "decode" | "ugc" | "ugc_frame" | "graphics" | "graphics_plan" | "caption_restyle"
sourceMinutes querynumber0 to 600The recording's length for a deep edit, or the finished video's for a caption restyle or a graphics plan.
pieces queryinteger1 to 60A month plan: the pieces in the month (videos, Loops, carousels and posts).

Returns job, credits, balance.

Content

GET/api/v1/offers

Offers. The confirmed offers written in Brand brain.

scope: read

Returns offers.

GET/api/v1/guides

Guides. Published guides and their public links.

scope: read

Returns guides.

Webhooks

Add an https endpoint in Settings, Developer, pick the events it should hear, and keep the signing secret shown when you add it. Each event is a POST with a JSON body. Answer with any 2xx within 10 seconds. Anything else is retried after 1 min, 5 min, 15 min, 1 h, 3 h, 6 h, 12 h: 8 tries in all, about a day. The same event keeps its id across retries, so you can drop repeats. The Send test event button sends a sample marked "test": true.

EventWhen
production.readyAn edit finished and its clips are ready to review and post.
production.failedAn edit stopped and needs attention. Any held credits were released.
post.publishedA post went live on its platform.
post.failedA post could not go out, or its outcome needs checking.
automation.dm_sentA comment-to-DM automation sent new DMs since the last check (checked every few minutes).
credits.lowThe balance dropped below the threshold set on the endpoint.
lead.capturedSomeone started one of your DM flows: their name, handle, the flow and any answers so far.
lead.updatedA lead tapped an answer in a DM flow, or you moved them to a new stage.
{
  "id": "8f0c2d1e-5a3b-4c7d-9e11-2b6f4a1c0d33",
  "type": "production.ready",
  "created": "2026-09-26T15:04:05.000Z",
  "workspace": "ws_7Qm2",
  "data": {
    "productionId": "3a1f9b2c-7d4e-4f60-8a15-c2e9d0b41f77",
    "title": "Pricing talk",
    "state": "review_ready",
    "publicationState": "draft"
  }
}

Every delivery carries x-ntl-event, x-ntl-delivery, x-ntl-timestamp and x-ntl-signature. The signature is an HMAC-SHA256 of timestamp.body with your endpoint's secret. Check it, and refuse timestamps more than five minutes old:

import crypto from "node:crypto";

// Express: app.post("/hooks", express.raw({ type: "application/json" }), handler)
function verified(req, secret) {
  const header = req.get("x-ntl-signature") || "";         // t=1790000000,v1=5f2c...
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const body = req.body.toString("utf8");
  const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + body).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && parts.v1?.length === 64 &&
    crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(parts.v1, "hex"));
}

Endpoints must be https addresses on the public internet. The address is checked when you save it and again on every delivery.

AI assistants (MCP)

The MCP server at https://app.ninetytenlabs.com/mcp gives an AI assistant the same actions as the API, with the same key, scopes and seat. It only lists the tools a key's scopes allow. Paid tools answer first with their price; the assistant tells you, and runs the tool only when it calls again with confirm_credits equal to that price.

Claude Code

claude mcp add --transport http ninetytenlabs https://app.ninetytenlabs.com/mcp \
  --header "Authorization: Bearer ntl_live_YOUR_KEY"

Cursor (~/.cursor/mcp.json)

{
  "mcpServers": {
    "ninetytenlabs": {
      "url": "https://app.ninetytenlabs.com/mcp",
      "headers": {
        "Authorization": "Bearer ntl_live_YOUR_KEY"
      }
    }
  }
}

Claude Desktop (claude_desktop_config.json, needs Node.js)

{
  "mcpServers": {
    "ninetytenlabs": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://app.ninetytenlabs.com/mcp",
        "--header",
        "Authorization:${NTL_AUTH}"
      ],
      "env": {
        "NTL_AUTH": "Bearer ntl_live_YOUR_KEY"
      }
    }
  }
}

Other assistants that can send a header work the same way: Streamable HTTP, POST https://app.ninetytenlabs.com/mcp, Authorization: Bearer with your key.

ToolWhat it doesScope
list_accountsThe social accounts connected to this workspace. Account ids are needed to post.read
get_upload_slotA signed slot to upload one recording (MP4, MOV or WebM up to 5 GB). Upload the file with one PUT to upload.url, then call complete_upload. Makes a new video unless video_id is given.write
complete_uploadAfter the file is uploaded: checks it arrived whole and attaches it to its video. Nothing is charged; create_video starts the edit.write
import_from_urlMakes a video from a public https link to a video file (checked first). Nothing is charged; create_video starts the edit.write
create_videoEdits an uploaded or imported recording into captioned short clips (quick cut for recordings up to 10 minutes). Paid: call without confirm_credits to get the price, tell the person, and call again with confirm_credits only after they agree.write
accept_edit_priceWhen get_video_status shows a choice (the edit would cost more than the price agreed), nothing was charged. Tell the person the new price and the reason, and call this with confirm_credits equal to choice.credits only after they agree.write
keep_video_as_isAnswers a waiting price choice with no: nothing starts and nothing is charged.write
get_video_statusWhere a video is: status (waiting_for_footage, editing, ready, failed, posted...), the edit stage, clips rendered, and any price choice waiting for the person.read
list_videosThis workspace's videos, newest first.read
list_clipsThe finished clips of a video, with preview links. Post a clip by its id.read
restyle_clipChanges one quick-cut clip (layout, headline, energy) and renders it again. Paid: price first, confirm_credits after the person agrees.write
write_scriptA recordable script (three hooks, body, call to action) from an idea, in this workspace's voice. Paid: price first, confirm_credits after the person agrees.write
make_carouselA carousel (title, caption, 2 to 10 slides) from an idea. Paid: price first, confirm_credits after the person agrees.write
write_captionsOne caption per platform (and a YouTube title) written from what the video says, saved for posting. The first write for a video is free; rewrites are paid (price first).write
schedule_postSchedules a finished video to chosen accounts, each post at its own time with its own caption and sound. Posting is free on every platform. From a creator seat this asks a manager for approval instead.publish
post_nowPosts a finished video now to chosen accounts, each with its own caption and sound. Always confirm with the person before posting. Posting is free on every platform. From a creator seat this asks a manager for approval instead.publish
list_postsScheduled and published posts, newest first.read
list_comment_to_dmAutomations on this workspace's Instagram and Facebook accounts, with how many DMs each sent.read
create_comment_to_dmWhen someone comments a keyword, they get a DM with up to three link buttons. Needs a manager or owner seat. Confirm the wording with the person first.publish
set_comment_to_dmTurns an automation on or off. Needs a manager or owner seat.publish
get_analyticsViews, likes, comments, shares and saves for recent posts. A number the platform did not report is null.read
get_creditsThe credit balance, monthly budget and the price of every job.read
quoteWhat a job costs before running it. With video_id, what editing that recording costs (read from the file).read

Limits

Requests120 a minute per key, API and MCP together. Headers show what is left: X-RateLimit-Remaining, X-RateLimit-Reset.
Keys10 active keys per workspace.
UploadsMP4, MOV or WebM up to 5 GB. An upload slot is good for two hours.
Request bodiesUp to 64 KB of JSON.
Idempotency keys1 to 255 characters, answers kept for 24 hours.
Webhooks10 endpoints per workspace, 8 delivery tries per event.

Build on it.
Your key is one click away.

Get a key