Rate limits
Per-minute throttles + monthly quotas, plus the headers we send so you can self-throttle.
The API enforces two limits in parallel: a per-minute rate limit (throttle) and a monthly conversion quota.
Per-plan limits #
| Plan | Conversions / month | Max file size | Requests / minute | Over quota |
|---|---|---|---|---|
| Free | 25 | 25 MB | 10 | hard stop |
| Hobby | 1,500 | 100 MB | 60 | hard stop |
| Startup | 6,000 | 100 MB | 120 | hard stop |
| Scale | 40,000 | 100 MB | 600 | hard stop |
| Growth | 200,000 | 100 MB | 3,000 | hard stop |
| Enterprise | Custom | Custom | Custom | Custom |
GET /v1/usage returns the live values for your account.
Headers #
Every response includes:
| Header | Description |
|---|---|
X-RateLimit-Limit | Requests permitted per minute on your plan. |
X-RateLimit-Remaining | Requests left in the current 60-second window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
Retry-After | (429 only) Seconds to wait before retrying. |
Use these to self-throttle without ever hitting 429:
result = ctf.convert(file=f, source="png", target="jpg")
if result.rate_limit.remaining and result.rate_limit.remaining < 3:
time.sleep(60)What happens when you exceed a limit? #
Per-minute throttle #
HTTP/1.1 429 Too Many Requests
Retry-After: 28
X-RateLimit-Remaining: 0
{
"error": {
"code": "rate_limited",
"message": "Too many requests in the last 60 seconds.",
"details": { "retry_after": 28 }
}
}The SDKs surface this as RateLimitError, with the Retry-After value exposed as e.retry_after (Python) / e.retryAfter (TypeScript).
Monthly quota #
HTTP/1.1 429 Too Many Requests
{
"error": "Monthly quota exceeded",
"limit": 25,
"used": 25,
"plan": "free",
"message": "Add 50 conversions for $9.99 (one-time, no subscription) at https://changethisfile.com/dashboard#credits, or wait for the monthly reset on the 1st (UTC).",
"upgrade_url": "https://changethisfile.com/dashboard#credits"
}- Every plan hard-stops at its monthly quota — there is no overage billing.
- Prepaid credits, if the account has any, are spent after the monthly allowance, so requests keep succeeding until they run out. 50 conversions cost $9.99, one-time, valid 12 months.
- Otherwise wait for 00:00 UTC on the 1st, or move to a larger plan.
Best practices #
Read the headers — don't backoff blindly
Watching X-RateLimit-Remaining lets you smooth your traffic without wasting 429s. The SDK exposes them on every successful response.
Use jobs for bursty workloads
The 429 throttle is enforced on submitting requests, not on conversion completion. If you need to burst-submit 1000 conversions in 30 seconds, queue them as async jobs and let our worker pool drain at its own pace.
Respect Retry-After
Retry-After is the number of seconds until your window has fresh capacity. The SDKs don't auto-retry — that's an explicit choice so you control your retry policy. Sleep then retry.
Monitor /v1/usage
Surface monthly usage in your own dashboards. Every plan hard-stops at its quota (25/month on Free) — building this visibility upfront prevents "why are conversions failing on the 28th?" tickets.