Rate Limits

API usage is controlled by request rate limits, workspace credits, and concurrent job limits.

ProteinIQ uses several controls for public API usage. Request rate limits protect the API, while credits and job concurrency control compute usage.

Account endpoint

Use GET /api/v1/account to inspect the current API key workspace, credits, active job count, concurrency limit, public API rate-limit policy, and key metadata.

Bash
curl -s \
  -H "Authorization: Bearer $PROTEINIQ_API_KEY" \
  "https://proteiniq.io/api/v1/account"

Example response:

JSON
{
  "object": "account",
  "workspace": {
    "id": "team_123",
    "plan": {
      "tier": "pro",
      "paid": true
    },
    "credits": {
      "available": 500
    }
  },
  "limits": {
    "active_jobs": 1,
    "concurrent_jobs": {
      "max": 3,
      "remaining": 2
    },
    "public_api_rate_limit": {
      "limit": 120,
      "window_seconds": 60
    }
  },
  "api_key": {
    "id": "key_123",
    "env": "LIVE",
    "prefix": "pq_live_abc123",
    "scopes": ["jobs:read", "jobs:write", "files:read", "files:write"]
  }
}

Request rate limits

Public API routes are rate limited. Rate-limit failures return rate_limited and may include retry headers.

JSON
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests"
  }
}

When headers are present, use them to schedule retries:

  • X-RateLimit-Limit: Current request limit
  • X-RateLimit-Remaining: Remaining requests in the current window
  • X-RateLimit-Reset: ISO 8601 timestamp for the next reset
  • Retry-After: Suggested seconds to wait before retrying

The current public API policy is 120 requests per 60 seconds. Status polling, result fetches, tool reads, and other public API requests share this budget for the API key.

Job status and result polling also have per-job throttles. For a single API key and job, call GET /api/v1/jobs/{jobId}/status no more than once every 5 seconds. The result endpoint uses the same 5 second per-job pace to protect stored outputs and signed file URL generation.

Polling and backoff

Polling should be paced by job state, not by a fixed sub-second interval. Start status polling at 5 seconds, double the delay after each non-terminal response, and cap the delay at 30 seconds. Add a small random jitter before each poll.

Recommended status polling schedule:

Poll attemptDelay before next poll
15 seconds
210 seconds
320 seconds
4+30 seconds

Clients that poll every 0.5 seconds can use the full public API quota on one job and may be throttled by the per-job polling limit. That leaves no room for result fetches, list calls, retries, or other jobs. Use server-sent events when you need faster status updates.

Credits

Paid jobs require enough workspace credits before they start. Use POST /api/v1/jobs/quote to estimate a job before submission.

Bash
curl -s -X POST \
  -H "Authorization: Bearer $PROTEINIQ_API_KEY" \
  -H "Content-Type: application/json" \
  "https://proteiniq.io/api/v1/jobs/quote" \
  -d '{
    "tool": "esmfold",
    "input": {
      "inputs": [
        {
          "id": "seqs_1",
          "slotId": "protein",
          "kind": "protein",
          "format": "fasta",
          "content": ">seq\nMKT...",
          "source": { "type": "text" }
        }
      ]
    },
    "settings": {}
  }'

Quote responses include available_credits, billable_credits, and blocking_errors.

Concurrent jobs

Workspaces have a maximum number of active concurrent jobs. GET /api/v1/account returns the current active count and remaining concurrency.

If the workspace already has too many active jobs, submission can return concurrent_job_limit_exceeded.

Retry behavior

  • Rate limit reached: Wait until the reset time before retrying.
  • Job still running: Continue polling status with exponential backoff, or use the events endpoint.
  • Result not ready: Respect the Retry-After header before fetching results again.
  • Concurrent job limit reached: Wait until an active job finishes, then submit again.
  • Insufficient credits: Add credits or lower job size before retrying.
  • Unknown submission result: Retry with the same Idempotency-Key to avoid duplicate jobs.