Rate Limits

Per-plan rate limits for the Markdown Anything API.

Rate limits protect the API from abuse and ensure fair usage across all workspaces. Limits are applied per workspace, not per token.

Conversion Rate Limits

The POST /convert endpoint is rate-limited based on your workspace's plan:

PlanConversions per Minute
Free20
Starter60
Professional120
Business240

General API Rate Limit

All other authenticated API endpoints (including GET /conversions/{id}) share a general rate limit of 120 requests per minute per workspace.

Rate Limit Headers

When you approach or exceed the rate limit, the API includes standard rate limit headers in the response:

Prop

Type

429 Too Many Requests

When you exceed the rate limit, the API returns a 429 status code:

{
    "message": "Too Many Attempts."
}

Handling 429 Responses

async function convertWithRetry(form, token, maxRetries = 3) {
    for (let i = 0; i <= maxRetries; i++) {
        const response = await fetch(
            "https://markdownanything.com/api/v1/convert",
            {
                method: "POST",
                headers: { Authorization: `Bearer ${token}` },
                body: form,
            }
        );

        if (response.status !== 429) {
            return response.json();
        }

        const retryAfter = response.headers.get("Retry-After") || 60;
        console.log(`Rate limited. Retrying in ${retryAfter}s...`);
        await new Promise((r) => setTimeout(r, retryAfter * 1000));
    }

    throw new Error("Rate limit exceeded after max retries");
}
import time
import requests

def convert_with_retry(file_path, token, max_retries=3):
    url = "https://markdownanything.com/api/v1/convert"
    headers = {"Authorization": f"Bearer {token}"}

    for i in range(max_retries + 1):
        with open(file_path, "rb") as f:
            response = requests.post(
                url, headers=headers, files={"file": f}
            )

        if response.status_code != 429:
            return response.json()

        retry_after = int(response.headers.get("Retry-After", 60))
        print(f"Rate limited. Retrying in {retry_after}s...")
        time.sleep(retry_after)

    raise Exception("Rate limit exceeded after max retries")

Best Practices

  • Respect Retry-After. Always check the Retry-After header before retrying — it tells you exactly how long to wait.
  • Use exponential backoff. If Retry-After is not present, back off exponentially (e.g. 1s, 2s, 4s, 8s).
  • Use async mode for batch jobs. Instead of sending many sync requests, use async mode with webhooks to queue conversions and receive results as they complete.
  • Upgrade your plan. If you consistently hit rate limits, consider upgrading to a higher plan for increased throughput.

Rate limits are tracked per workspace, not per API token. Multiple tokens on the same workspace share the same rate limit budget.