Jump to content
McKay Development

Recommended Posts

Posted (edited)

Is it just me or today something happend with rate limits again?

Trade confirmation on all of my bots goes right into 429 error, even though they use different IPs and accounts and it's no more than 4 trade per hour
Same with market confirmations.
I saw on the forum that getTimeOffset was strict before, but I never experienced something like this, hope it's temporary....

And I also have troubles with inventory loading...
On my PC if I load inventory from the browser everything seems ok, but on the same PC using a script I get 429 on the first request...
It used to be the same. Something is not right... I use edited version of inventory loading. Were there any changes in requests?

Edited by Nickers
Posted

I have same issue. Since this morning, community.acceptConfirmationForObject has completely stopped working for me. Every confirmation attempt fails with: Failed to confirm trade offer XXX: HTTP error 429. It was working fine before today, so it looks like something changed on Valve's side.

Posted

I'm having the same issues with my bots.
I can also see that a lot of sites are struggling with trading bots while some have already adapted, likely need an update to steamcommunity package.

Posted

here is easy fix:

const request = require('request');
const SteamCommunity = require('steamcommunity');

let community = new SteamCommunity({
	request: request.defaults({
		headers: {
			'Accept-Encoding': 'gzip, deflate, br'
		}
	})
});

if you use proxy:

request: request.defaults({
	proxy: proxyUrl,
	headers: {
		'Accept-Encoding': 'gzip, deflate, br'
	}
})

 

This is not rate limiting. Steam's WAF now fingerprints the Accept-Encoding request header. The legacy request package (which node-steamcommunity uses internally with gzip: true) sends exactly Accept-Encoding: gzip, deflate — real browsers always include br as well. Steam now rejects the bot-like values outright:

Accept-Encoding sent Response
gzip, deflate (request lib default) 429
gzip 429
gzip, deflate, br 200
gzip, deflate, br, zstd 200

Same IP, same endpoint, seconds apart — only the header changes the outcome. api.steampowered.com is not affected.

Posted
1 hour ago, JVz said:

here is easy fix:

const request = require('request');
const SteamCommunity = require('steamcommunity');

let community = new SteamCommunity({
	request: request.defaults({
		headers: {
			'Accept-Encoding': 'gzip, deflate, br'
		}
	})
});

if you use proxy:

request: request.defaults({
	proxy: proxyUrl,
	headers: {
		'Accept-Encoding': 'gzip, deflate, br'
	}
})

 

This is not rate limiting. Steam's WAF now fingerprints the Accept-Encoding request header. The legacy request package (which node-steamcommunity uses internally with gzip: true) sends exactly Accept-Encoding: gzip, deflate — real browsers always include br as well. Steam now rejects the bot-like values outright:

Accept-Encoding sent Response
gzip, deflate (request lib default) 429
gzip 429
gzip, deflate, br 200
gzip, deflate, br, zstd 200

Same IP, same endpoint, seconds apart — only the header changes the outcome. api.steampowered.com is not affected.

This seems to be a part of the solution. The confirmations are working like 60-70% of the time now but for the other 30-40% error 429 is still getting returned.
Thank you for sharing though.

Posted
46 minutes ago, JVz said:

do you use 1 account per 1 proxy?

Yes, didn't have these 429's yesterday with the same Proxy setup. All proxies are residential.

Posted
26 minutes ago, Devx09 said:

Yes, didn't have these 429's yesterday with the same Proxy setup. All proxies are residential.

do you call other api endpoints on same proxies? inventories, offerslist and so on

Posted (edited)
14 minutes ago, JVz said:

do you call other api endpoints on same proxies? inventories, offerslist and so on

Yes, I do. Using trade offer manager which uses steamcommunity with the new headers defined.
Endpoints like offerslist however don't seem to be returning 429's, only the confirmations endpoints do.
Are you not receiving any 429's yourself when trying to confirm offers?

Edited by Devx09
Posted

For me, this method has not changed anything at all. I consistently receive cookies from the steam-session, setCookies() in steamcommunity and tradeoffer-manager work stably, but as soon as I try to send an exchange from the bot or get a link to the exchange bot getTradeURL(), I get error 429

Works for me:

request: Request.defaults({
    proxy: proxy,
    headers: {
        'Accept-Encoding': 'deflate, br, zstd'
    }
})
Posted

Steam changed its community restrictions yesterday. I added a feature to clear Steam request headers, and it works. It was 1/100 to ~70/100 successful.

Posted (edited)

Here is a cleaner version if you encounter problems.

Still not working %100, randomly works.

As on some steamcommunity versions, that method would cause a crash.

if (this._options.request) {
    return reject(new Error('SteamCommunity.login() is incompatible with node-steamcommunity v3\'s usage of \'request\'. If you need to specify a custom \'request\' instance (e.g. when using a proxy), use https://www.npmjs.com/package/steam-session directly to log onto Steam.'));
}
 

const SteamCommunity = require('steamcommunity');
 
const community = new SteamCommunity();
 
// Patch before confirmations — login doesn't use this.request
community.request = community.request.defaults({
headers: {
'Accept-Encoding': 'gzip, deflate, br'
}
});
 
Edited by Therepower
Posted
22 hours ago, vindisel said:

For me, this method has not changed anything at all. I consistently receive cookies from the steam-session, setCookies() in steamcommunity and tradeoffer-manager work stably, but as soon as I try to send an exchange from the bot or get a link to the exchange bot getTradeURL(), I get error 429

Works for me:

request: Request.defaults({
    proxy: proxy,
    headers: {
        'Accept-Encoding': 'deflate, br, zstd'
    }
})

the workaround is to implement a wrapper with https instead of request due to problem with cookies and redirections.

 

const SteamCommunity = require('steamcommunity');
const https = require('https');
const { URL } = require('url');

function followRedirects(url, jar, depth, callback) {
    if (depth > 8) return callback('Too many redirects');
    const parsed = new URL(url);
    const cookieStr = Object.entries(jar).map(([k, v]) => k + '=' + v).join('; ');
    const options = {
        hostname: parsed.hostname,
        path: parsed.pathname + parsed.search,
        method: 'GET',
        headers: {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Cookie': cookieStr,
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36'
        }
    };
    https.get(options, (res) => {
        if (res.headers['set-cookie']) {
            res.headers['set-cookie'].forEach(sc => {
                const [kv] = sc.split(';');
                const [k, ...v] = kv.split('=');
                jar[k] = v.join('=');
            });
        }
        let body = '';
        res.on('data', d => body += d);
        res.on('end', () => {
            if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
                const loc = res.headers.location.startsWith('http') ? res.headers.location : parsed.origin + res.headers.location;
                followRedirects(loc, jar, depth + 1, callback);
            } else {
                callback(null, body);
            }
        });
    }).on('error', e => callback(e.message));
}

SteamCommunity.prototype.getTradeURL = function(callback) {
    const url = 'https://steamcommunity.com/my/tradeoffers/privacy';
    const cookieStr = this._jar._jar.getCookieStringSync(url);
    const jar = {};
    cookieStr.split('; ').forEach(pair => {
        const [k, ...v] = pair.split('=');
        if (k) jar[k] = v.join('=');
    });
    followRedirects(url, jar, 0, (err, body) => {
        if (err) return callback(new Error(err));
        const match = body.match(/https?:\/\/(www\.)?steamcommunity\.com\/tradeoffer\/new\/?\?partner=\d+(&|&amp;)token=([a-zA-Z0-9_-]+)/);
        if (match) callback(null, match[0], match[3]);
        else callback(new Error('Malformed response'));
    });
};

const community = new SteamCommunity();

// community.login({...})

community.getTradeURL((err, url, token) => {
        if (err) {
            console.error('getTradeURL error:', err.message);
            process.exit(1);
        }
        console.log('Token:', token);
        console.log('URL:', url);
    });

 

Posted

Thanks for the feedback guys!
 

On 7/10/2026 at 3:38 PM, JVz said:

here is easy fix:

const request = require('request');
const SteamCommunity = require('steamcommunity');

let community = new SteamCommunity({
	request: request.defaults({
		headers: {
			'Accept-Encoding': 'gzip, deflate, br'
		}
	})
});

This helped me with trade confirmations when testing, thanks!

Nothing works for inventory requests yet. I'll take a closer look at real site requests later.

Posted

do you have 100% success rate? it's still gives me rate limit from time to time

15 minutes ago, Nickers said:

Thanks for the feedback guys!
 

This helped me with trade confirmations when testing, thanks!

Nothing works for inventory requests yet. I'll take a closer look at real site requests later.

 

Posted
On 7/11/2026 at 10:42 AM, vindisel said:

For me, this method has not changed anything at all. I consistently receive cookies from the steam-session, setCookies() in steamcommunity and tradeoffer-manager work stably, but as soon as I try to send an exchange from the bot or get a link to the exchange bot getTradeURL(), I get error 429

Works for me:

request: Request.defaults({
    proxy: proxy,
    headers: {
        'Accept-Encoding': 'deflate, br, zstd'
    }
})



Error 429 appeared again, this entry fixed the situation, but idk for how long: 'Accept-Encoding': 'br, zstd'
 

Posted

The full fix for me was to actually replace the ancient request library with passing a more modern http client (in my case undici) as a custom request instance. After that every single steamcommunity call worked as expected, I assume that steam just updated their fingerprinting so older http clients like the on used by the library gets flagged and u get back a 429. For the custom request instance you basically just have to copy and implement the functionality that the libraries own request instance implemented but with with a bit help of LLM this is very easily done.

Posted

It's absolutely crazy, every time i delete one parameter, everything starts to work. What is the logic behind this?
 

headers: {
    'Accept-Encoding': 'zstd', // 🤪
},
Posted

This option also turned out to be quite working, for me it does not even require changing the Accept-Encoding, the key is to transfer Accept and sec-fetch-user.
 

headers: {
    //"User-Agent": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
    //"Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "en-US,en;q=0.9",
     "sec-ch-ua": "\"Chromium\";v=\"140\", \"Not=A?Brand\";v=\"24\", \"Google Chrome\";v=\"140\"",
    "sec-ch-ua-mobile": "?1",
     "sec-ch-ua-platform": "\"Android\"",
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "same-origin",
    "sec-fetch-user": "?1",
    "upgrade-insecure-requests": "1"
}
Posted

Hey guys.

 

I have asked gpt5.5 to fix it. (With the samples from this thread) And telling it that it needs to change the headers to prevent akamai WAF.

 

This was the set of headers that works for 95 percent of times:

 

const headers = {
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-origin',
    };

 

Although, some endpoints needed some more tuning. (E.g. getting partner details after creating an offer)

So right now, i use this to change header dynamically:

 

Quote
private static configureSteamRequestHeaders() {
    (Bot.community as any).onPreHttpRequest = (
      _requestID: number,
      _source: string,
      options: any,
      continueRequest: (err?: Error | null) => void,
    ) => {
      options.headers = {
        Accept:
          'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.9',
        'Cache-Control': 'no-cache',
        Pragma: 'no-cache',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-origin',
        ...(options.headers  {}),
      };

      const url = String(options.url  options.uri  '');
      const method = String(options.method  'GET').toUpperCase();

      if (method === 'GET' && url.includes('/tradeoffer/new/')) {
        options.headers['Sec-Fetch-Dest'] = 'document';
        options.headers['Sec-Fetch-Mode'] = 'navigate';
        options.headers['Sec-Fetch-Site'] = 'none';
        options.headers['Upgrade-Insecure-Requests'] = '1';
      }

      if (
        url.includes('/tradeoffer/new/send') ||
        url.includes('/tradeoffer/new/partnerinventory/')
      ) {
        options.headers.Accept = 'application/json, text/javascript, */*; q=0.01';
        options.headers['X-Requested-With'] = 'XMLHttpRequest';
        options.headers['Sec-Fetch-Dest'] = 'empty';
        options.headers['Sec-Fetch-Mode'] = 'cors';
        options.headers['Sec-Fetch-Site'] = 'same-origin';
      }

      continueRequest(null);
      return true;
    };
  }

 

I have been using this for the past 36 hours and it is working fine.

 

Is there anything suspicious about this? Is there a ban risk? (I don't think so myself, but wanted to share it)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...