1. ספרייה
  2. HTTP והרשת
  3. קודי סטטוס HTTP

עודכן לפני חודש

שגיאת 429 Too Many Requests היא הדרך שבה האינטרנט אומר: "אני משאב משותף, ויש לי גבולות."

כשאתה נתקל בשגיאת 429, השרת הבין את בקשתך בדיוק. הוא פשוט סירב לעבד אותה כי שלחת יותר מדי בקשות מהר מדי. זה לא באג ולא תצורה שגויה — זו מגבלה מכוונת. הגבלת קצב קיימת כי שרתים הם משאבים משותפים, ובלי מגבלות, לקוח אחד חמדן יכול לקלקל את החוויה לכולם.

תחשוב על זה כמו דלפק של מעדנייה. אתה לוקח מספר, מחכה לתורך. אם מישהו ניסה לבצע 100 הזמנות בדקה, הצוות היה בסופו של דבר אומר "האט" — למען כולם שבתור.

איך נראית תגובת 429

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1729512000

{
    "error": "Too Many Requests",
    "message": "Rate limit exceeded. Please retry after 60 seconds."
}

הכותרת החשובה ביותר היא Retry-After. היא אומרת לך בדיוק מתי מותר לנסות שוב. לקוח מסודר מכבד אותה; לקוח אגרסיבי שמתעלם ממנה מסתכן בחסימה מוחלטת.

הכותרות שמספרות לך היכן אתה עומד

APIs טובים לא רק דוחים אותך — הם מסבירים למה ומתי תהיה מוזמן שוב:

כותרתמשמעות
X-RateLimit-Limitהמכסה שלך (למשל, 100 בקשות לדקה)
X-RateLimit-Remainingכמה בקשות נותרו לך
X-RateLimit-Resetחותמת זמן Unix שבה המכסה מתאפסת
Retry-Afterשניות (או תאריך) עד שאפשר לנסות שוב

לקוחות חכמים עוקבים אחרי כותרות אלה מראש. למה לחכות לשגיאת 429 כשאפשר לראות אותה מגיעה?

מדוע אתה מוגבל בקצב

אתה שולח בקשות מהר מדי. רוב הגבלות הקצב הן "X בקשות ל-Y זמן". חרוג מהן, וכל בקשה נוספת תידחה עד שהחלון מתאפס.

אתה שולח בפרצים. ל-APIs מסוימים יש מגבלות פרץ נפרדות — אולי 100 בקשות לדקה בסך הכל, אבל לא יותר מ-10 בשנייה בודדת. זה מונע עומסי שיא פתאומיים.

יש לך יותר מדי בקשות בו-זמניות. שירותים מסוימים מגבילים כמה בקשות יכולות להיות בטיפול בו-זמנית. שש בקשות מקביליות כשהמגבלה היא חמש? השישית מקבלת 429.

טיפול בשגיאות 429 בקוד שלך

הדרך הלא נכונה:

// זה מתעלם מהגבלות קצב לחלוטין
const response = await fetch('/api/data');

הדרך הנכונה:

async function fetchWithRetry(url, maxRetries = 3) {
    for (let attempt = 0; attempt < maxRetries; attempt++) {
        const response = await fetch(url);
        
        if (response.status !== 429) {
            return response;
        }
        
        // כבד את הנחיות השרת
        const retryAfter = response.headers.get('Retry-After');
        const waitMs = retryAfter 
            ? parseInt(retryAfter) * 1000 
            : Math.pow(2, attempt) * 1000; // Exponential backoff כגיבוי
        
        await new Promise(resolve => setTimeout(resolve, waitMs));
    }
    
    throw new Error('Rate limit exceeded after retries');
}

עוד יותר טוב — עקוב אחרי המגבלות שלך והימנע מלהגיע אליהן:

class ApiClient {
    constructor() {
        this.remaining = Infinity;
        this.resetTime = 0;
    }
    
    async fetch(url) {
        // המתן אם אנחנו יודעים שנגמרה המכסה
        if (this.remaining === 0) {
            const waitMs = (this.resetTime - Date.now() / 1000) * 1000;
            if (waitMs > 0) await new Promise(r => setTimeout(r, waitMs));
        }
        
        const response = await fetch(url);
        
        // עדכן את הבנתנו לגבי המגבלות
        this.remaining = parseInt(response.headers.get('X-RateLimit-Remaining') ?? Infinity);
        this.resetTime = parseInt(response.headers.get('X-RateLimit-Reset') ?? 0);
        
        return response;
    }
}

יישום הגבלת קצב בשרת שלך

הגישה הפשוטה ביותר עם Express:

const rateLimit = require('express-rate-limit');

app.use('/api/', rateLimit({
    windowMs: 60 * 1000,  // דקה אחת
    max: 100,              // 100 בקשות לדקה
    standardHeaders: true, // החזר מידע על הגבלת קצב בכותרות
    handler: (request, response) => {
        response.status(429).json({
            error: 'Too Many Requests',
            retryAfter: Math.ceil((request.rateLimit.resetTime - Date.now()) / 1000)
        });
    }
}));

עבור מערכות ייצור, השתמש ב-Redis כדי שהמגבלות יישמרו לאחר הפעלה מחדש של השרת ויפעלו על פני מופעים מרובים:

const Redis = require('ioredis');
const redis = new Redis();

async function rateLimit(request, response, next) {
    const key = `ratelimit:${request.ip}`;
    const limit = 100;
    const windowSeconds = 60;
    
    const current = await redis.incr(key);
    if (current === 1) await redis.expire(key, windowSeconds);
    
    const ttl = await redis.ttl(key);
    
    response.setHeader('X-RateLimit-Limit', limit);
    response.setHeader('X-RateLimit-Remaining', Math.max(0, limit - current));
    response.setHeader('X-RateLimit-Reset', Math.floor(Date.now() / 1000) + ttl);
    
    if (current > limit) {
        response.setHeader('Retry-After', ttl);
        return response.status(429).json({ error: 'Too Many Requests' });
    }
    
    next();
}

מגבלות שונות למשתמשים שונים

לא כל המשתמשים שווים. לקוח משלם ראוי לקיבולת גבוהה יותר מאשר סורק אנונימי:

const limits = {
    anonymous: { requests: 10, window: 3600 },    // 10 בשעה
    free: { requests: 100, window: 3600 },        // 100 בשעה  
    premium: { requests: 1000, window: 3600 },    // 1,000 בשעה
    enterprise: { requests: 10000, window: 3600 } // 10,000 בשעה
};

תעד את המגבלות האלה בבירור. הפתעות מתסכלות. משתמשים צריכים לדעת את המכסה שלהם לפני שהם פוגעים בה.

אסטרטגיות הגבלת קצב

חלון קבוע: ספור בקשות לפי דקה לפי לוח השנה (00:00-00:59, 01:00-01:59). פשוט, אבל מאפשר פרצים בגבולות — לקוח יכול לשלוח 100 בקשות ב-00:59 ועוד 100 ב-01:00.

חלון הזזה: ספור בקשות ב-60 השניות האחרונות, תמיד. חלק יותר אבל קשה יותר ליישום.

דלי אסימונים: אסימונים מצטברים בקצב קבוע; כל בקשה עולה אסימון אחד. מאפשר פרצים מבוקרים תוך שמירה על מגבלות כוללות.

דלי דולף: בקשות נכנסות לתור ומתנקזות בקצב קבוע. התעבורה החלקה ביותר, אבל מוסיף השהייה.

עבור רוב ה-APIs, חלון הזזה או דלי אסימונים מוצאים את האיזון הטוב ביותר בין הוגנות למורכבות היישום.

שאלות נפוצות על 429 בקשות רבות מדי

מדוע אני מקבל שגיאות 429 כשאני בקושי משתמש ב-API?

ייתכן שאתה חולק הגבלת קצב עם משתמשים אחרים. APIs רבים מגבילים לפי כתובת IP, כך שאם אתה מאחורי NAT ארגוני או פרוקסי משותף, אתה מתחרה עם כולם שמשתמשים באותה IP. אמת את הבקשות שלך — רוב ה-APIs מעניקים למשתמשים מאומתים מכסה עצמאית.

האם כדאי לנסות שוב מיד כשאני מקבל 429?

לא. זה הדבר הגרוע ביותר שאפשר לעשות. השרת בדיוק ביקש ממך להאט; להציף אותו בניסיונות חוזרים יביא כנראה לחסימה מוחלטת. המתן תמיד לפחות כמה שצוין בכותרת Retry-After. אם אין כותרת, השתמש ב-exponential backoff.

מה ההבדל בין 429 ל-503?

שגיאת 429 אומרת שאתה ספציפית שולח יותר מדי בקשות. שגיאת 503 אומרת שהשרת עמוס ואינו יכול לטפל בבקשות של אף אחד כרגע. שניהם מרמזים שכדאי לנסות שוב מאוחר יותר, אבל 429 היא אישית — אתה צריך להאט. 503 אומרת שכולם צריכים לחכות.

האם דף זה היה מועיל?

😔
🤨
😃