Powrót do strony głównej

API WB Ozon: obsługa błędów 429 i 50x

Artykuł opisuje strategie obsługi błędów 429 i 50x przy pracy z API Wildberries i Ozon. Omówiono logikę retry z backoff, batching zapytań po dniach i monitorowanie w tle dla pełnej automatyzacji zbierania danych.

Bez błędów 429 z API marketplace'ów: kod i taktyki
Advertisement 728x90

Obsługa błędów 429 i 50x przy pracy z API Wildberries i Ozon

Podczas parsowania danych z API Wildberries i Ozon programiści często napotykają błędy 429 (zbyt wiele żądań) i 50x (błędy serwerowe). Te problemy powstają z powodu wysokiego obciążenia endpointów. Standardowa odpowiedź wsparcia technicznego — skonfigurować ponowne próby. Rozważmy trzy podejścia: podstawową obsługę z powtórzeniami, optymalizację ilości żądań oraz monitorowanie z doładowaniem.

Podstawowa obsługa ponownych prób

Pierwszy krok — traktować 429 i 50x jako oczekiwane zdarzenia. Zaimplementuj logikę ponawiania z wykładniczymi przerwami i czytaniem nagłówków takich jak X-Ratelimit-Retry. To minimalizuje blokadę i zwiększa odporność.

Przykład funkcji dla żądania POST z ponownymi próbami:

Google AdInline article slot
def get_oz(url, heads, params, max_retries=5, timeout=30):
    result = {}
    for retries in range(1, max_retries + 1):  
        
        try:
            response = requests.post(url, headers=heads, json=params, timeout=timeout)
        except requests.RequestException as e:
            print(f'!!! Błąd sieciowy w próbie {retries}: {e}!!!')
            time.sleep(2 * retries)
            continue  
                
        # Kod 200 otrzymano prawidłową odpowiedź  
        if response.status_code == 200:
            return response.json()
            
        # Kod 429 - Zbyt wiele żądań, robimy inteligentną przerwę i ponownie żądamy
        elif response.status_code == 429:  
            pause_time = 2 ** retries
            
            # X-Ratelimit-Retry - cecha WB, ale może i OZ kiedyś wprowadzi
            if 'X-Ratelimit-Retry' in response.headers:
                pause_time = int(response.headers['X-Ratelimit-Retry']) + 5
            print(f'get_oz: ! -Zbyt wiele żądań- w próbie {retries}. Przerwa {pause_time} sek')
            time.sleep(pause_time)
        
        # Przy błędzie 5xx przez przerwę przechodzimy do następnej próby                    
        elif 500 <= response.status_code < 600:
            pause_time = 2 ** retries
            print(f'get_oz: ! -Błąd {response.status_code}- w próbie {retries}. Przerwa {pause_time} sek')
            time.sleep(pause_time)
        
        # Jeśli inny błąd - wychodzimy z cyklu!                
        else:
            print(f'get_oz: !!! Błąd pobierania danych przez API! code: {response.status_code}')
            return None
    
    print(f'get_oz: !!! Wszystkie {max_retries} próby zawiodły')
  
    return None
  • 5 ponownych prób zwykle wystarczy dla typowych obciążeń.
  • Cykl paginacji (offset/strona) przenieś na wyższy poziom.
  • Przy całkowitej porażce odłóż na godzinę+.

Ta logika integruje się w dowolny potok ETL dla API.

Optymalizacja ilości danych

Duże żądania (np. transakcje z tygodnia) prowokują błędy na późnych stronach paginacji. Dziel na małe niezależne partie: po dniach, po kategoriach lub po filtrach. Zapisz każdą partię od razu w bazie danych — utrata jednej porcji nie resetuje całego postępu.

Zalety:

Google AdInline article slot
  • Mniej stron na żądanie (1–3 zamiast 20).
  • Niezależne ponowne próby po partiach.
  • Częściowe zapisywanie przy awariach.

Przykład ładowania transakcji Ozon po dniach:

date_from = today() - timedelta(days=7)
date_to = today() - timedelta(days=1)
current_date = date_from
delta = timedelta(days=1)

#przechodzimy przez dni od date_from do date_to
while current_date <= date_to:  
    transactions_oneday = pd.DataFrame([])           
    url = "https://api-seller.ozon.ru/v3/finance/transaction/list"
    ipage = 1
    all_pages_data = []
    while True:
        data = {
            "filter": {
                "date": {
                    "from": str(current_date) + "T00:00:00.000Z",
                    "to": str(current_date) + "T23:59:59.999Z"
                }
            },
            "page": ipage,
            "page_size": 1000
            }
        dd = []
        apioz_response = get_oz(url, headers, data, 5)
        if len(apioz_response) > 0 :
            dd = apioz_response['result']['operations']
            all_pages_data.extend(dd)
        else:
            print(f'!!! Pusta odpowiedź od API')   

        # jeśli na stronie mniej niż 1000 wierszy - to ostatnia, wychodzimy z cyklu    
        if len(dd) < 1000: break

        ipage +=1
            
    transactions_oneday = pd.DataFrame(all_pages_data)        
    #natychmiast eksportujemy do bazy, co otrzymaliśmy z API
    export_to_db('oz_transactions', transactions_oneday)

    #przechodzimy do następnego dnia
    current_date += delta 

Mimo zagnieżdżonych cykli, niezawodność rośnie wykładniczo.

Monitorowanie i doładowanie

Dla pełnej automatyzacji dodaj "kołek" — tło monitorujące. Po głównym uruchomieniu (np. o 6–7 rano) sprawdzaj logi/bazę danych co godzinę (8–12 rano).

Google AdInline article slot
  • Analizuj status ostatniego zadania.
  • Sprawdź integralność danych (luki w datach).
  • Uruchamiaj ukierunkowane ponowne próby tylko dla braków.

To redukuje ręczne interwencje do minimum, szczególnie przy dziennych aktualizacjach.

Co jest ważne

  • Używaj wykładniczego backoff z nagłówkami retry dla 429.
  • Dziel duże żądania na partie po dniach/filtrach z natychmiastowym zapisywaniem.
  • Wprowadzaj godzinne monitorowanie po głównym zbiorze dla uzupełnienia.
  • 5 ponownych prób pokrywa 95% awarii; całkowite porażki odłóż na godzinę+.
  • Integruj w ETL bez sztywnych zależności od paginacji.

— Editorial Team

Advertisement 728x90

Czytaj dalej