Error fix

The OpusClip API allows 30 requests a minute per key and 4 running projects on Pro Beta and Max, then answers 429. Past 900 credits in a month it answers 403. clipsdk bills $0.03 per source minute with no monthly cap.

OpusClip API 429 Too Many Requests and the monthly API cap

Three answers look alike and need different fixes: a plain 429 (too many requests this minute), a 429 with X-Cap-Reason: concurrent (too many projects running) and a 403 with API_MONTHLY_CAP_REACHED (the month is used up).

Updated

Or one API call

$0.03per source minute

  • Every clip included, up to 1080p60 and 20 Mb/s
  • $1 free to start, 33 minutes of video
  • Prepaid credit, no subscription
See pricing

What OpusClip sends back

The error
POST https://api.opus.pro/api/clip-projects

429 Too Many Requests
429 Too Many Requests, X-Cap-Reason: concurrent

403 Forbidden
{
  "code": "API_MONTHLY_CAP_REACHED",
  "reset_at": "2026-06-01T00:00:00Z",
  "upgrade_url": "https://www.opus.pro/contact-api"
}
Calls to the core project and clip APIs, like POST /api/clip-projects, on the Pro Beta, Max and Business plans. The 403 body is the example from OpusClip's own docs.

Source: OpusClip API limits, checked 7 November 2026

OpusClip allows 30 requests a minute per API key on the core project and clip APIs. Past 30 in a minute, the next request gets a 429. Polling for clips uses the same key, so every poll counts toward the 30.

Pro Beta and Max run 4 projects at once, Business 50. A project started while all slots are busy also gets a 429, with the header X-Cap-Reason: concurrent. The slot frees as soon as any running project finishes.

Pro Beta and Max include 15 hours, 900 credits, of API use per calendar month per workspace. After that the API answers 403, not 429, so a retry loop stops instead of spinning. It resumes on the first day of the next month (UTC), and the web app keeps working meanwhile.

Every API project costs at least 10 credits, about 10 minutes, so a month of short videos reaches the cap after 90 projects.

OpusClip API limits next to clipsdk

What each limit is and what you get back past it.

OpusClip API limits next to clipsdk
LimitOpusClip API, Pro Beta and Maxclipsdk API
Requests30 a minute per key, then 429No cap on valid keys; 30 failed auth attempts a minute per IP, then 429
Running at once4 projects, 50 on Business, then 429 with X-Cap-Reason: concurrent3 jobs per account, then 429 RATE_LIMITED
Retry-AfterNot documentedOn every 429: 10 s when busy, the seconds to 00:00 UTC past a daily limit, 60 s after too many failed auths, and up to a day after too many failed jobs
Per month900 credits, 15 hours, then 403 API_MONTHLY_CAP_REACHED until the 1st (UTC)No cap; $0.02 a minute past 2,000 minutes
Per dayNone documented for projects200 jobs and 20 GB of uploads
Smallest job10 credits, about 10 minutesNo minimum, billed per second
Longest video10 hours, 30 GB3 hours, 5 GB

clipsdk limits from our limits docs and pricing.

Source: OpusClip API limits, checked 7 November 2026

Fix it: pace, cap, back off, stop

The code below does all four. Retrying a 429 at once only adds requests to the minute that is already full.

  1. Tell the three answers apart

    A 429 without X-Cap-Reason means the key sent more than 30 requests in a minute. A 429 with X-Cap-Reason: concurrent means 4 projects are already running, or 50 on Business. A 403 with code: API_MONTHLY_CAP_REACHED means the workspace used its 900 credits this month.

  2. Pace every request to one per 2 seconds

    30 a minute is one request every 2 seconds. Share one pacer across every worker and every polling loop that uses the key, since they all draw from the same 30.

  3. Keep at most 4 projects running

    Count the projects you have in flight and free a slot when one finishes, from your webhook or your poll. A fifth project then waits for a slot instead of collecting 429s.

  4. Back off on 429, with jitter

    Wait 2 seconds and double the wait on every try, up to 60, plus up to 1 second of jitter so workers don't retry in step. On concurrent, start at 15 seconds. OpusClip doesn't document Retry-After, so the code only uses it when a response carries it.

    opus_retry.py
    import os
    import random
    import threading
    import time
    
    import requests
    
    API = os.environ.get("OPUS_API", "https://api.opus.pro/api")
    KEY = os.environ["OPUS_API_KEY"]
    MAX_TRIES = 6
    
    
    class MonthlyCapReached(Exception):
        def __init__(self, body):
            super().__init__(f"OpusClip API cap reached until {body['reset_at']}")
            self.reset_at = body["reset_at"]
            self.upgrade_url = body["upgrade_url"]
    
    
    class Pacer:
        # 30 requests a minute per key is one every 2 s, shared by every thread
        def __init__(self, per_minute):
            self.gap = 60 / per_minute
            self.next_at = 0.0
            self.lock = threading.Lock()
    
        def wait(self):
            with self.lock:
                now = time.monotonic()
                at = max(now, self.next_at)
                self.next_at = at + self.gap
            time.sleep(at - now)
    
    
    pacer = Pacer(30)
    started = time.monotonic()
    
    
    def backoff(attempt, reason, retry_after):
        # OpusClip doesn't document Retry-After, so we only use it when it shows up
        if retry_after and retry_after.isdigit():
            return float(retry_after)
        base = 15.0 if reason == "concurrent" else 2.0
        return min(60.0, base * 2**attempt) + random.uniform(0, 1)
    
    
    def create_project(video_url):
        for attempt in range(MAX_TRIES):
            pacer.wait()
            response = requests.post(
                f"{API}/clip-projects",
                headers={"Authorization": f"Bearer {KEY}"},
                json={"videoUrl": video_url},
                timeout=30,
            )
    
            # The monthly cap is a 403, not a 429: no retry helps before reset_at
            is_json = response.headers.get("Content-Type", "").startswith("application/json")
            if response.status_code == 403 and is_json and response.json().get("code") == "API_MONTHLY_CAP_REACHED":
                raise MonthlyCapReached(response.json())
    
            if response.status_code != 429:
                response.raise_for_status()
                return response.json()
    
            reason = response.headers.get("X-Cap-Reason", "rate")
            seconds = backoff(attempt, reason, response.headers.get("Retry-After"))
            print(f"{time.monotonic() - started:5.1f} s  429 ({reason}), try {attempt + 2} in {seconds:.1f} s")
            time.sleep(seconds)
    
        raise RuntimeError(f"still 429 after {MAX_TRIES} tries")
    
    
    if __name__ == "__main__":
        for _ in range(2):
            try:
                create_project("https://api.clipsdk.com/f/lib/demo/rocket-ranch-podcast.mp4")
                print(f"{time.monotonic() - started:5.1f} s  200, project created")
            except MonthlyCapReached as cap:
                print(f"{time.monotonic() - started:5.1f} s  403 API_MONTHLY_CAP_REACHED, paused until {cap.reset_at}")
  5. Stop on 403 API_MONTHLY_CAP_REACHED

    Don't retry it: nothing changes before reset_at. Park the work until then, upgrade through upgrade_url, or send the overflow to another API. We ran the script against a local stub that answers the way OpusClip's limits page documents: two 429s, a 429 with X-Cap-Reason: concurrent, a 200, then the 403.

    output
    $ python opus_retry.py
      0.0 s  429 (rate), try 2 in 2.4 s
      2.5 s  429 (rate), try 3 in 4.4 s
      6.8 s  429 (concurrent), try 4 in 60.7 s
     67.5 s  200, project created
     69.5 s  403 API_MONTHLY_CAP_REACHED, paused until 2026-12-01T00:00:00Z

The same job on clipsdk

clipsdk has no monthly cap and no per-minute cap on valid keys. Each account runs 3 jobs at once, and a fourth gets 429 RATE_LIMITED with Retry-After: 10, so the same backoff code works with the header read first. A job that fails still comes back as HTTP 200, with job.status set to "failed" and no charge, so check job.status too.

A 429 never creates a job, so retrying it can't double up. To retry a timeout, resend the same body with the same Idempotency-Key and you get the first job back. A different body, or a retry while the first request is still running, gets 409 IDEMPOTENCY_CONFLICT.

This is a real request: a NASA lesson just over 4 minutes long, 3 clips with karaoke captions, back in 80 seconds. It costs $0.13 at $0.03 per source minute, where the OpusClip API counts any project as at least 10 credits.

POST/v1/clipsTry it
curl https://api.clipsdk.com/v1/clips \
  -H "Authorization: Bearer $CLIPSDK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://api.clipsdk.com/f/lib/demo/thermal-energy-lesson.mp4",
    "clips": 3,
    "captions": "karaoke"
  }'
Clip 1, 59 seconds, with karaoke captions. Cut from STEMonstrations: Thermal Energy Transfer, NASA, public domain.clip-1.mp4clip-1.jpg

Why retrying at once makes it worse

A 429 on the rate limit means the minute is already full. Retrying straight away adds another request to that same minute, and with several workers on one key they all retry together and stay locked out.

Backoff spreads the retries out and jitter keeps workers from lining up again. The pacer does the rest: when every request waits its turn, the rate limit is never hit in the first place.

When the monthly cap is the real problem

If you hit API_MONTHLY_CAP_REACHED every month, retries won't help. 900 credits is 15 hours of video, and with the 10-credit minimum it is at most 90 short projects.

For more, OpusClip's Business plans follow a contract. Or send the overflow to an API that bills per use: clipsdk charges $0.03 per source minute, $0.02 past 2,000 minutes in a month, with no cap. New accounts get $1 free.

Questions, answered

Does the OpusClip API send Retry-After on a 429?

Its limits page doesn't say. It says to retry after backoff, so treat a Retry-After header as a bonus: use it when it's there and back off on your own when it isn't.

Why do I get 403 instead of 429?

That is the monthly cap. Past 900 credits on Pro Beta or Max, OpusClip answers 403 with the code API_MONTHLY_CAP_REACHED so retry loops stop. It lifts on the first day of the next month (UTC).

Is the 30 a minute limit per key or per workspace?

Per API key. The monthly 900 credits are per workspace, so more keys raise the request rate but not the month's hours.

Can I still use OpusClip when the API is capped?

Yes. OpusClip says the web app stays fully usable while the API is capped.

Start free

Your back catalog is full of shorts

Your first $1 is free: 33 minutes of video, every clip included up to 1080p60 and 20 Mb/s.

Two studio microphones on a desk in a dark room lit by a red glow