agentsclimarketplace

Oraclecloud rate limits

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/oraclecloud-pack/skills/oraclecloud-rate-limits

'Handle OCI API rate limits with defensive retry patterns and known limits by service.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill oraclecloud-rate-limits

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

10.1 KB, ~2.2k tokens by cl100k_base, as published. Nobody here has run it

Oracle Cloud Rate Limits

Overview

OCI API rate limits vary by service and are not well documented. A 429 TooManyRequests response kills your automation, and unlike AWS or Azure, OCI does not return a Retry-After header. This skill maps known limits by service, implements exponential backoff with jitter, and provides circuit breaker patterns for bulk operations.

Purpose: Build resilient OCI API clients that handle throttling gracefully without data loss.

Prerequisites

  • OCI Python SDKpip install oci
  • OCI config file at ~/.oci/config with valid credentials (user, fingerprint, tenancy, region, key_file)
  • Python 3.8+
  • Understanding of which OCI service you are calling (limits vary per service)

Instructions

Step 1: Know the Limits

OCI publishes some rate limits, but many are undocumented. Here are the known limits observed in production:

ServiceEndpoint TypeObserved LimitNotes
ComputeList/Get~20 req/secPer-tenancy, not per-user
ComputeCreate/Update/Delete~10 req/secStricter for mutating operations
Object StorageList/Get~100 req/secPer-bucket namespace
Object StoragePut/Delete~50 req/secVaries by region load
IdentityList/Get~10 req/secTenancy-wide shared limit
IdentityCreate/Update~5 req/secVery conservative
DatabaseAll operations~10 req/secShared across DB family
Networking (VCN)All operations~20 req/secPer-compartment
MonitoringPost metrics~50 req/secPer-metric namespace
EventsRule CRUD~10 req/secPer-compartment

Critical: These are observed limits, not guaranteed SLAs. OCI may throttle lower under load.

Step 2: Implement Exponential Backoff with Jitter

OCI returns no Retry-After header on 429 responses, so you must implement your own backoff. The SDK's built-in retry handles some cases, but for bulk operations you need explicit control:

import oci
import time
import random

config = oci.config.from_file("~/.oci/config")

def call_with_retry(fn, max_retries=5, base_delay=1.0):
    """Call an OCI SDK function with exponential backoff and jitter.

    OCI returns 429 TooManyRequests with NO Retry-After header,
    so we implement our own backoff strategy.
    """
    for attempt in range(max_retries):
        try:
            return fn()
        except oci.exceptions.ServiceError as e:
            if e.status == 429:
                # Exponential backoff with full jitter
                delay = base_delay * (2 ** attempt)
                jitter = random.uniform(0, delay)
                wait_time = delay + jitter
                print(f"Rate limited (429). Attempt {attempt + 1}/{max_retries}. "
                      f"Waiting {wait_time:.1f}s")
                time.sleep(wait_time)
            elif e.status >= 500:
                # Server errors — retry with backoff
                delay = base_delay * (2 ** attempt)
                print(f"Server error ({e.status}). Retrying in {delay}s")
                time.sleep(delay)
            else:
                raise  # 4xx errors (except 429) are not retryable
    raise Exception(f"Max retries ({max_retries}) exceeded")

Step 3: Use the SDK's Built-in Retry Configuration

The OCI Python SDK supports retry configuration natively. Use this for simple cases:

import oci
from oci.retry import RetryStrategyBuilder

config = oci.config.from_file("~/.oci/config")

# Build a custom retry strategy
retry_strategy = RetryStrategyBuilder(
    max_attempts_check=True,
    max_attempts=5,
    total_elapsed_time_check=True,
    total_elapsed_time_seconds=300,
    retry_max_wait_between_calls_seconds=30,
    retry_base_sleep_time_seconds=1,
    service_error_check=True,
    service_error_retry_on_any_5xx=True,
    service_error_retry_config={429: []},  # Retry on 429 with any message
    backoff_type=oci.retry.BACKOFF_DECORRELATED_JITTER
).get_retry_strategy()

compute = oci.core.ComputeClient(config, retry_strategy=retry_strategy)

# All calls through this client will automatically retry on 429 and 5xx
instances = compute.list_instances(
    compartment_id="ocid1.compartment.oc1..example"
)

Step 4: Implement a Circuit Breaker for Bulk Operations

For operations that process hundreds of resources, a circuit breaker prevents cascading failures:

import oci
import time
import random

class OCICircuitBreaker:
    """Circuit breaker for bulk OCI API operations."""

    def __init__(self, failure_threshold=5, reset_timeout=60):
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout
        self.last_failure_time = 0
        self.state = "closed"  # closed = normal, open = blocking

    def call(self, fn, max_retries=3, base_delay=1.0):
        if self.state == "open":
            if time.time() - self.last_failure_time > self.reset_timeout:
                self.state = "half-open"
                print("Circuit half-open — testing one request")
            else:
                remaining = self.reset_timeout - (time.time() - self.last_failure_time)
                raise Exception(f"Circuit open. Retry in {remaining:.0f}s")

        try:
            result = call_with_retry(fn, max_retries=max_retries, base_delay=base_delay)
            if self.state == "half-open":
                self.state = "closed"
                self.failure_count = 0
                print("Circuit closed — resuming normal operation")
            return result
        except Exception:
            self.failure_count += 1
            self.last_failure_time = time.time()
            if self.failure_count >= self.failure_threshold:
                self.state = "open"
                print(f"Circuit OPEN after {self.failure_count} failures. "
                      f"Pausing for {self.reset_timeout}s")
            raise

# Usage for bulk listing
breaker = OCICircuitBreaker(failure_threshold=3, reset_timeout=30)
config = oci.config.from_file("~/.oci/config")
compute = oci.core.ComputeClient(config)

compartment_ids = ["ocid1.compartment.oc1..aaa", "ocid1.compartment.oc1..bbb"]
all_instances = []

for cid in compartment_ids:
    result = breaker.call(
        lambda c=cid: compute.list_instances(compartment_id=c)
    )
    all_instances.extend(result.data)
    time.sleep(0.1)  # Courtesy delay between bulk calls

Step 5: Batch Operations with Rate Limiting

For operations that must process many items (e.g., tagging all instances), throttle proactively:

import oci
import time

config = oci.config.from_file("~/.oci/config")
compute = oci.core.ComputeClient(config)

def batch_with_throttle(items, operation, requests_per_second=5):
    """Process items with proactive rate limiting."""
    delay = 1.0 / requests_per_second
    results = []

    for i, item in enumerate(items):
        result = call_with_retry(lambda it=item: operation(it))
        results.append(result)

        if (i + 1) % 50 == 0:
            print(f"Processed {i + 1}/{len(items)}")

        time.sleep(delay)

    return results

Output

Successful implementation produces:

  • A retry wrapper function that handles 429 responses with exponential backoff and jitter
  • SDK-level retry configuration applied to all OCI client calls
  • A circuit breaker that prevents cascading failures during bulk operations
  • Proactive rate limiting for batch processing scripts

Error Handling

ErrorCodeCauseSolution
TooManyRequests429API rate limit exceeded (no Retry-After header)Use exponential backoff with jitter — start at 1s, max 30s
NotAuthenticated401Bad config or expired keyVerify ~/.oci/config credentials
NotAuthorizedOrNotFound404Missing IAM policy or wrong OCIDCheck compartment OCID and IAM policies
InternalError500OCI service errorRetry with backoff; check https://ocistatus.oraclecloud.com
ServiceError (status -1)-1Request timeoutIncrease SDK timeout or reduce request payload
CERTIFICATE_VERIFY_FAILEDSSL certificate issueUpdate CA certificates: pip install certifi

Examples

Quick 429 test — intentionally trigger rate limiting:

import oci

config = oci.config.from_file("~/.oci/config")
compute = oci.core.ComputeClient(config)

# Rapid-fire requests to observe throttling behavior
for i in range(100):
    try:
        compute.list_instances(compartment_id="ocid1.compartment.oc1..example")
    except oci.exceptions.ServiceError as e:
        if e.status == 429:
            print(f"Throttled at request {i + 1}")
            break

Check current OCI SDK retry defaults:

import oci
print(f"SDK version: {oci.__version__}")
# Default retry: NoneRetryStrategy (no retries unless configured)
# Always set explicit retry strategies for production code

Resources

Next Steps

After implementing rate limit handling, see oraclecloud-security-basics for IAM policy patterns, or oraclecloud-observability for monitoring your API call patterns and error rates.

What ships with it: 1 file

1.8 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.