Перейти к основному содержимому

Subtask 2-2: Ozon Seller API — Rate Limits Documentation

Task: Extract rate limit information (global and per-method) Date: 2026-02-10 Status: ✅ COMPLETED


Executive Summary

Comprehensive rate limit documentation for Ozon Seller API, including global limits, per-endpoint restrictions, error handling, and best practices for API integration.


Global Rate Limits

Primary Rate Limit (Per Client ID)

Limit TypeValueScopeEffective Date
Requests Per Second50 RPSAll methods per Client IDMay 19, 2025

Key Details:

  • The 50 requests per second limit applies to all API methods from a single Client ID
  • This is an account-wide limit, not per-endpoint
  • All requests are counted toward this single limit regardless of the endpoint
  • The limit was implemented/increased as of May 19, 2025
  • Previously reported as an "increase" in the request limit

Source: Ozon Seller API Notification - Telegram


Rate Limit Error Responses

HTTP Status Code

HTTP CodeError CodeMessageDescription
4298"You have reached request rate limit per second"Rate limit exceeded

Error Response Format

{
"code": 8,
"message": "You have reached request rate limit per second",
"details": "Too many requests"
}

Source: Ozon for Dev - Error Code 8


Endpoint-Specific Limits

Product Management

OperationLimitNotes
POST /v2/product/importUp to 100 products per requestBatch import limit
POST /v1/product/import/stocksUp to 100 products per requestStock update batch size
POST /v1/product/import/pricesUp to 100 products per requestPrice update batch size
Daily product creation60 PDPs per dayProduct Detail Pages
Daily product editing300 PDPs per dayProduct Detail Pages
Total PDP limit120 PDPsMaximum products allowed

Stock and Inventory Updates

OperationLimitNotes
Stock updates (single product/warehouse)Once per 2 minutesPer SKU per warehouse
Product availability updatesUp to 80 requests/minuteGeneral recommendation
POST /v2/products/stocksUp to 100 products per requestMulti-warehouse stock updates

Barcode Operations

OperationLimitNotes
POST /v1/barcode/addMax 100 products per requestBatch barcode binding
Barcodes per productMax 100 barcodesPer product limit
Method usageMax 20 timesPer time period

Analytics and Reports

CategoryLimitNotes
Performance API100,000 requests per dayFor Ozon Performance accounts
Report generationPer report typeAsync generation with status checking

Rate Limit Headers

Current Status: ❌ No Standard Rate Limit Headers

Important: Unlike many other APIs (Amazon, GitHub, etc.), Ozon Seller API does not provide standard rate limit headers in API responses.

Missing Headers:

  • No X-RateLimit-Limit header
  • No X-RateLimit-Remaining header
  • No X-RateLimit-Reset header
  • No retry-after header in 429 responses

Implications:

  • Cannot proactively monitor remaining quota
  • Must implement client-side rate limiting
  • Must handle 429 errors reactively

Best Practices for Handling Rate Limits

1. Client-Side Rate Limiting

Recommended Approach:

import time
from collections import deque
from threading import Lock

class RateLimiter:
def __init__(self, max_requests=50, time_window=1.0):
self.max_requests = max_requests
self.time_window = time_window # seconds
self.requests = deque()
self.lock = Lock()

def wait_if_needed(self):
with self.lock:
now = time.time()
# Remove old requests outside the time window
while self.requests and self.requests[0] <= now - self.time_window:
self.requests.popleft()

# If at limit, wait
if len(self.requests) >= self.max_requests:
sleep_time = self.time_window - (now - self.requests[0])
if sleep_time > 0:
time.sleep(sleep_time)
# Clean up old requests after sleeping
now = time.time()
while self.requests and self.requests[0] <= now - self.time_window:
self.requests.popleft()

# Add current request
self.requests.append(now)

# Usage
rate_limiter = RateLimiter(max_requests=50, time_window=1.0)

def make_api_request(url, headers, data):
rate_limiter.wait_if_needed()
response = requests.post(url, headers=headers, json=data)
# Handle response...

2. Retry Logic with Exponential Backoff

import time
import random

def make_request_with_retry(url, headers, data, max_retries=5):
for attempt in range(max_retries):
rate_limiter.wait_if_needed()
response = requests.post(url, headers=headers, json=data)

if response.status_code == 429:
# Rate limit exceeded - implement exponential backoff
wait_time = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limit hit. Waiting {wait_time:.2f}s...")
time.sleep(wait_time)
continue

elif response.status_code >= 500:
# Server error - retry with backoff
wait_time = (2 ** attempt) + random.uniform(0, 1)
print(f"Server error {response.status_code}. Retrying in {wait_time:.2f}s...")
time.sleep(wait_time)
continue

else:
# Success or other error - return as-is
return response

raise Exception(f"Max retries exceeded after {max_retries} attempts")

3. Request Batching

Optimize requests by batching:

# Instead of multiple individual calls:
for product in products:
update_stock(product['sku'], product['stock']) # ❌ Inefficient

# Use batch operations:
batch_size = 100
for i in range(0, len(products), batch_size):
batch = products[i:i+batch_size]
update_stocks_batch(batch) # ✅ Efficient

4. Asynchronous Request Processing

import asyncio
import aiohttp

async def fetch_with_rate_limit(session, url, headers, data, semaphore):
async with semaphore: # Limit concurrent requests
async with session.post(url, headers=headers, json=data) as response:
return await response.json()

async def bulk_request(items, max_concurrent=10):
semaphore = asyncio.Semaphore(max_concurrent)
async with aiohttp.ClientSession() as session:
tasks = [
fetch_with_rate_limit(session, url, headers, item, semaphore)
for item in items
]
return await asyncio.gather(*tasks)

Rate Limit Monitoring

Logging Implementation

import logging
from datetime import datetime

class APIRequestLogger:
def __init__(self):
self.request_count = 0
self.rate_limit_hits = 0
self.logger = logging.getLogger('ozon_api')

def log_request(self, url, status_code, response_time):
self.request_count += 1

if status_code == 429:
self.rate_limit_hits += 1

self.logger.info(
f"[{datetime.now().isoformat()}] "
f"Request #{self.request_count} | "
f"Status: {status_code} | "
f"Time: {response_time:.3f}s | "
f"URL: {url}"
)

if status_code == 429:
self.logger.warning(
f"Rate limit hit! Total hits: {self.rate_limit_hits}"
)

Metrics to Track

  1. Total Requests Per Second - Monitor RPS in real-time
  2. Rate Limit Hit Rate - Percentage of requests receiving 429
  3. Average Response Time - Detect performance degradation
  4. Peak Usage Hours - Identify high-traffic periods
  5. Endpoint-Specific Usage - Most frequently called endpoints

Common Rate Limit Scenarios

Scenario 1: Bulk Product Updates

Challenge: Updating stock for 10,000 products

Solution:

products = fetch_products_from_database()  # 10,000 items
batch_size = 100
delay_between_batches = 2.0 # seconds

for i in range(0, len(products), batch_size):
batch = products[i:i+batch_size]

# Update stocks in batch
response = update_stocks_batch(batch)

if response.status_code == 429:
# Wait and retry
time.sleep(5)
response = update_stocks_batch(batch)

# Small delay to avoid hitting rate limit
time.sleep(delay_between_batches)

print(f"Processed {min(i+batch_size, len(products))}/{len(products)}")

Scenario 2: Order Processing Pipeline

Challenge: Processing 1,000 new orders with multiple API calls each

Solution:

async def process_order(order):
# Each order requires 3-4 API calls
try:
await get_order_details(order['id'])
await update_order_status(order['id'], 'processing')
await generate_shipping_label(order['id'])
await notify_customer(order['id'])
except Exception as e:
print(f"Error processing order {order['id']}: {e}")

# Process with concurrency control
orders = fetch_new_orders() # 1,000 orders
await process_orders_with_limit(orders, max_concurrent=20)

Scenario 3: Real-Time Stock Synchronization

Challenge: Keep stock synchronized across multiple warehouses

Solution:

import queue
import threading

request_queue = queue.Queue()

def worker():
while True:
item = request_queue.get()
try:
update_stock_item(item)
except Exception as e:
print(f"Error: {e}")
# Requeue for retry
request_queue.put(item)
finally:
request_queue.task_done()

# Start multiple worker threads
num_workers = 10
threads = []
for _ in range(num_workers):
t = threading.Thread(target=worker, daemon=True)
t.start()
threads.append(t)

# Enqueue stock updates
for stock_update in stock_updates:
request_queue.put(stock_update)

# Wait for queue to empty
request_queue.join()

Rate Limits by API Category

Products (Товары)

  • General operations: Subject to 50 RPS global limit
  • Batch operations: Max 100 items per request
  • Product creation: 60 PDPs per day
  • Product editing: 300 PDPs per day

Orders (Заказы)

  • Order list retrieval: Subject to 50 RPS global limit
  • Order status updates: Recommended to batch requests
  • FBO/FBS operations: No specific limits beyond global

Finance (Финансы)

  • Transaction lists: Subject to 50 RPS global limit
  • Financial reports: Async generation, no sync limits

Analytics (Аналитика)

  • Data retrieval: Subject to 50 RPS global limit
  • Performance API: 100,000 requests per day (special accounts)

Stocks and Prices (Склад и Цены)

  • Stock updates: 80 requests/minute recommended
  • Single SKU updates: Once per 2 minutes per warehouse
  • Batch updates: Up to 100 products per request

Advanced Rate Limiting Strategies

1. Token Bucket Algorithm

class TokenBucket:
def __init__(self, rate, capacity):
self.rate = rate # Tokens per second
self.capacity = capacity # Max tokens
self.tokens = capacity
self.last_time = time.time()

def consume(self, tokens=1):
now = time.time()
elapsed = now - self.last_time
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_time = now

if self.tokens >= tokens:
self.tokens -= tokens
return True
return False

# Usage: 50 tokens per second, bucket capacity of 100
bucket = TokenBucket(rate=50, capacity=100)

def make_request():
while not bucket.consume():
time.sleep(0.01) # Wait for tokens
# Make API request

2. Distributed Rate Limiting (Multi-Instance)

import redis

class DistributedRateLimiter:
def __init__(self, redis_client, key_prefix, limit, window):
self.redis = redis_client
self.key_prefix = key_prefix
self.limit = limit
self.window = window

def is_allowed(self, client_id):
key = f"{self.key_prefix}:{client_id}"
current = self.redis.incr(key)

if current == 1:
self.redis.expire(key, self.window)

return current <= self.limit

Official Documentation Sources

Rate Limit References

Community Resources


Recommendations for Integration

1. Stay Under the Limit

  • Target 40-45 RPS instead of full 50 RPS
  • Account for network delays and retries
  • Build in buffer for unexpected spikes

2. Implement Graceful Degradation

  • Queue requests when rate limit is hit
  • Process queued requests during off-peak hours
  • Provide user feedback about delays

3. Monitor and Alert

  • Set up alerts for high rate-limit hit rates
  • Track RPS metrics in dashboards
  • Review logs regularly

4. Optimize Request Patterns

  • Use batch operations where possible
  • Cache frequently accessed data
  • Prioritize critical operations

5. Plan for Growth

  • Design for horizontal scaling
  • Use connection pooling
  • Implement request priority queues

Summary

Key Points:

  • Global Limit: 50 requests per second per Client ID (as of May 19, 2025)
  • Error Response: HTTP 429 with error code 8
  • No Rate Limit Headers: Must implement client-side rate limiting
  • Per-Endpoint Limits: Various limits for stock updates, product creation, etc.
  • Batch Operations: Use batching to minimize request count
  • Retry Strategy: Implement exponential backoff for 429 errors
  • Monitoring: Log requests and track rate limit hits

Implementation Checklist:

  • Implement client-side rate limiter (50 RPS max)
  • Add retry logic with exponential backoff
  • Use batch operations where available
  • Set up request logging and monitoring
  • Create alerts for high rate-limit hit rates
  • Test with production-like load
  • Document rate limit handling in integration guide

Rate limit documentation complete and verified. Ready for integration into final 01_OZON_API_RUS.md document.