nitter icon indicating copy to clipboard operation
nitter copied to clipboard

get_session.py returns Authentication failed every time

Open BojidarTonev opened this issue 2 months ago • 82 comments

I believe there has been an udpate in the twitter API, sicne now everytime i try to use the get_session.py with correct credentials I get Authenticaiton failed. each time - am I the only here stumbling on this issue?

Status Code: 400 'code': 399, 'message': 'LoginError.AttestationDenied'

This is a device attestation error - Twitter is detecting that the request is not coming from a real Android device and blocking it.

BojidarTonev avatar Oct 28 '25 11:10 BojidarTonev

same issue when using the official twitter app on emulator, but working normally on real phone

josephanwar2 avatar Oct 28 '25 11:10 josephanwar2

+1

tlgsn avatar Oct 28 '25 14:10 tlgsn

same issue , +1

ganesh-RW avatar Oct 29 '25 05:10 ganesh-RW

Same here... Musk plan to remove Twitter domain to replace it by X It's maybe related to this ? https://x.com/Safety/status/1981764501947953225

akuma0 avatar Oct 29 '25 10:10 akuma0

Same here... Musk plan to remove Twitter domain to replace it by X It's maybe related to this ? https://x.com/Safety/status/1981764501947953225

No, this issue is related to integrity. Twitter now verifies whether the device it's installed on is trustworthy. It doesn't allow rooted devices or patched apps into the emulator.

tahafatih avatar Oct 29 '25 10:10 tahafatih

Same here... Musk plan to remove Twitter domain to replace it by X It's maybe related to this ? https://x.com/Safety/status/1981764501947953225

No, this issue is related to integrity. Twitter now verifies whether the device it's installed on is trustworthy. It doesn't allow rooted devices or patched apps into the emulator.

yes that's right have you any idea to bypass this verify

josephanwar2 avatar Oct 29 '25 11:10 josephanwar2

Same here... Musk plan to remove Twitter domain to replace it by X It's maybe related to this ? https://x.com/Safety/status/1981764501947953225

No, this issue is related to integrity. Twitter now verifies whether the device it's installed on is trustworthy. It doesn't allow rooted devices or patched apps into the emulator.

yes that's right have you any idea to bypass this verify

You can try Magisk.

tahafatih avatar Oct 29 '25 11:10 tahafatih

Same here... Musk plan to remove Twitter domain to replace it by X It's maybe related to this ? https://x.com/Safety/status/1981764501947953225

No, this issue is related to integrity. Twitter now verifies whether the device it's installed on is trustworthy. It doesn't allow rooted devices or patched apps into the emulator.

yes that's right have you any idea to bypass this verify

You can try Magisk.

tried it with no success did you try it?

josephanwar2 avatar Oct 29 '25 11:10 josephanwar2

It seems they are implementing something like this https://developer.apple.com/documentation/devicecheck/preparing-to-use-the-app-attest-service

I can still grab cookies, but oauth tokens via any method is a struggle to find a solution to. If you're seeing this error and trying to grab cookies; use the 'forgot password' option and re-enter your old password; that will at least re-enable access to the account.

Per a recent tweet, they seem to be actively trying to kill off the old domain (twitter.com) and in the process remove old methods of onboarding. If this is the case, cookies usage may only be the way forward.

cmj avatar Oct 30 '25 03:10 cmj

I got the same issue. Do you have any solution yet?

0xbarchitect avatar Nov 01 '25 15:11 0xbarchitect

Same issue here.

How can I grab cookies?

AZthemute avatar Nov 02 '25 16:11 AZthemute

It seems that trying to pass the anti-bot detection is tough.

I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device.

As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token.

Looking forward to a smarter way...

0xcathiefish avatar Nov 03 '25 09:11 0xcathiefish

It seems that trying to pass the anti-bot detection is tough.

I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device.

As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token.

Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

josephanwar2 avatar Nov 03 '25 11:11 josephanwar2

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret.

It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work.

If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

0xcathiefish avatar Nov 03 '25 12:11 0xcathiefish

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret.

It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work.

If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation.
I'm very interested in the workaround you mentioned.
If possible, could you please share the Python file?
Thanks in advance!

Mostafa3mad avatar Nov 03 '25 12:11 Mostafa3mad

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret. It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work. If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation. I'm very interested in the workaround you mentioned. If possible, could you please share the Python file? Thanks in advance!

Hi ~

I'll happy if this helps! Please note that you'll need to configure your own CONSUMER_KEY, CONSUMER_SECRET, and path_to_save_token I've also modified the script slightly so that, just like Nitter's official get_session.py, it will automatically append the obtained token to your sessions.jsonl file.

To run it:

  1. Execute the Python script.It will output an authorization URL. Open this link in your browser.
  2. After authorizing the app, you will be given a PIN code.
  3. Copy this PIN code and paste it back into your terminal.It will then fetch all the information you need.

get_session_alternative.py

# -*- coding: utf-8 -*-
# Copyright 2017 Twitter, Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0

# This script generates a user's access tokens which can be used to make requests on behalf of the user, also known as OAuth 1.0a (user context) authentication method
# Docs: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens

import sys
import requests
from requests_oauthlib import OAuth1Session
import json

# You need  use your own key and secret, which you can fetch from official Twitter development portal.
# https://developer.x.com/en/portal/dashboard
CONSUMER_KEY = ' '
CONSUMER_SECRET = ' '

# Request an OAuth Request Token. This is the first step of the 3-legged OAuth flow. This generates a token that you can use to request user authorization for access.
def request_token():

    oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET, callback_uri='oob')

    url = "https://api.twitter.com/oauth/request_token"

    try:
        response = oauth.fetch_request_token(url)
        resource_owner_oauth_token = response.get('oauth_token')
        resource_owner_oauth_token_secret = response.get('oauth_token_secret')
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)
    
    return resource_owner_oauth_token, resource_owner_oauth_token_secret

# Use the OAuth Request Token received in the previous step to redirect the user to authorize your developer App for access.
def get_user_authorization(resource_owner_oauth_token):

    authorization_url = f"https://api.x.com/oauth/authorize?oauth_token={resource_owner_oauth_token}"
    authorization_pin = input(f" \n Send the following URL to the user you want to generate access tokens for. \n → {authorization_url} \n This URL will allow the user to authorize your application and generate a PIN. \n Paste PIN here: ")

    return(authorization_pin)

# Exchange the OAuth Request Token you obtained previously for the user’s Access Tokens.
def get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin):

    oauth = OAuth1Session(CONSUMER_KEY, 
                            client_secret=CONSUMER_SECRET, 
                            resource_owner_key=resource_owner_oauth_token, 
                            resource_owner_secret=resource_owner_oauth_token_secret, 
                            verifier=authorization_pin)
    
    url = "https://api.twitter.com/oauth/access_token"

    try: 
        response = oauth.fetch_access_token(url)
        access_token = response['oauth_token']
        access_token_secret = response['oauth_token_secret']
        user_id = response['user_id']
        screen_name = response['screen_name']
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)

    return(access_token, access_token_secret, user_id, screen_name)

if __name__ == '__main__':
    
    resource_owner_oauth_token, resource_owner_oauth_token_secret = request_token()
    authorization_pin = get_user_authorization(resource_owner_oauth_token)
    access_token, access_token_secret, user_id, screen_name = get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin)
    
    
    print(f"\n User @handle: {screen_name}", f"\n User ID: {user_id}", f"\n User access token: {access_token}", f" \n User access token secret: {access_token_secret} \n")
    
    
    path_to_save_token = ' '
    
    session_entry = {
        "oauth_token": access_token,
        "oauth_token_secret": access_token_secret
    }
    
    try:
        with open(path_to_save_token, "a") as f:
            f.write(json.dumps(session_entry) + "\n")
        print("Authentication successful. Session appended to", path_to_save_token)
    except Exception as e:
        print(f"Failed to write session information: {e}")
        sys.exit(1)

0xcathiefish avatar Nov 03 '25 13:11 0xcathiefish

Do you mean Nitter uses Oauth1 token and token_secret, instead of Oauth2?

0xbarchitect avatar Nov 04 '25 02:11 0xbarchitect

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret. It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work. If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation. I'm very interested in the workaround you mentioned. If possible, could you please share the Python file? Thanks in advance!

Hi ~

I'll happy if this helps! Please note that you'll need to configure your own CONSUMER_KEY, CONSUMER_SECRET, and path_to_save_token I've also modified the script slightly so that, just like Nitter's official get_session.py, it will automatically append the obtained token to your sessions.jsonl file.

To run it:

  1. Execute the Python script.It will output an authorization URL. Open this link in your browser.
  2. After authorizing the app, you will be given a PIN code.
  3. Copy this PIN code and paste it back into your terminal.It will then fetch all the information you need.

get_session_alternative.py

# -*- coding: utf-8 -*-
# Copyright 2017 Twitter, Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0

# This script generates a user's access tokens which can be used to make requests on behalf of the user, also known as OAuth 1.0a (user context) authentication method
# Docs: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens

import sys
import requests
from requests_oauthlib import OAuth1Session
import json

# You need  use your own key and secret, which you can fetch from official Twitter development portal.
# https://developer.x.com/en/portal/dashboard
CONSUMER_KEY = ' '
CONSUMER_SECRET = ' '

# Request an OAuth Request Token. This is the first step of the 3-legged OAuth flow. This generates a token that you can use to request user authorization for access.
def request_token():

    oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET, callback_uri='oob')

    url = "https://api.twitter.com/oauth/request_token"

    try:
        response = oauth.fetch_request_token(url)
        resource_owner_oauth_token = response.get('oauth_token')
        resource_owner_oauth_token_secret = response.get('oauth_token_secret')
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)
    
    return resource_owner_oauth_token, resource_owner_oauth_token_secret

# Use the OAuth Request Token received in the previous step to redirect the user to authorize your developer App for access.
def get_user_authorization(resource_owner_oauth_token):

    authorization_url = f"https://api.x.com/oauth/authorize?oauth_token={resource_owner_oauth_token}"
    authorization_pin = input(f" \n Send the following URL to the user you want to generate access tokens for. \n → {authorization_url} \n This URL will allow the user to authorize your application and generate a PIN. \n Paste PIN here: ")

    return(authorization_pin)

# Exchange the OAuth Request Token you obtained previously for the user’s Access Tokens.
def get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin):

    oauth = OAuth1Session(CONSUMER_KEY, 
                            client_secret=CONSUMER_SECRET, 
                            resource_owner_key=resource_owner_oauth_token, 
                            resource_owner_secret=resource_owner_oauth_token_secret, 
                            verifier=authorization_pin)
    
    url = "https://api.twitter.com/oauth/access_token"

    try: 
        response = oauth.fetch_access_token(url)
        access_token = response['oauth_token']
        access_token_secret = response['oauth_token_secret']
        user_id = response['user_id']
        screen_name = response['screen_name']
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)

    return(access_token, access_token_secret, user_id, screen_name)

if __name__ == '__main__':
    
    resource_owner_oauth_token, resource_owner_oauth_token_secret = request_token()
    authorization_pin = get_user_authorization(resource_owner_oauth_token)
    access_token, access_token_secret, user_id, screen_name = get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin)
    
    
    print(f"\n User @handle: {screen_name}", f"\n User ID: {user_id}", f"\n User access token: {access_token}", f" \n User access token secret: {access_token_secret} \n")
    
    
    path_to_save_token = ' '
    
    session_entry = {
        "oauth_token": access_token,
        "oauth_token_secret": access_token_secret
    }
    
    try:
        with open(path_to_save_token, "a") as f:
            f.write(json.dumps(session_entry) + "\n")
        print("Authentication successful. Session appended to", path_to_save_token)
    except Exception as e:
        print(f"Failed to write session information: {e}")
        sys.exit(1)

Thank you, I successfully generated sessions.jsonl using the python script you provided, but nitter is unable to pull any information through this sessions.jsonl.

It shows a 'Fetch error, API: list, errors: (errors: @[(code: couldntAuth, message: "Could not authenticate you"'.

I am using the free version of the API. Does this mean I must use the paid version of the API?

eeccelah avatar Nov 04 '25 09:11 eeccelah

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret. It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work. If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation. I'm very interested in the workaround you mentioned. If possible, could you please share the Python file? Thanks in advance!

Hi ~ I'll happy if this helps! Please note that you'll need to configure your own CONSUMER_KEY, CONSUMER_SECRET, and path_to_save_token I've also modified the script slightly so that, just like Nitter's official get_session.py, it will automatically append the obtained token to your sessions.jsonl file.

To run it:

  1. Execute the Python script.It will output an authorization URL. Open this link in your browser.
  2. After authorizing the app, you will be given a PIN code.
  3. Copy this PIN code and paste it back into your terminal.It will then fetch all the information you need.

get_session_alternative.py

# -*- coding: utf-8 -*-
# Copyright 2017 Twitter, Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0

# This script generates a user's access tokens which can be used to make requests on behalf of the user, also known as OAuth 1.0a (user context) authentication method
# Docs: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens

import sys
import requests
from requests_oauthlib import OAuth1Session
import json

# You need  use your own key and secret, which you can fetch from official Twitter development portal.
# https://developer.x.com/en/portal/dashboard
CONSUMER_KEY = ' '
CONSUMER_SECRET = ' '

# Request an OAuth Request Token. This is the first step of the 3-legged OAuth flow. This generates a token that you can use to request user authorization for access.
def request_token():

    oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET, callback_uri='oob')

    url = "https://api.twitter.com/oauth/request_token"

    try:
        response = oauth.fetch_request_token(url)
        resource_owner_oauth_token = response.get('oauth_token')
        resource_owner_oauth_token_secret = response.get('oauth_token_secret')
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)
    
    return resource_owner_oauth_token, resource_owner_oauth_token_secret

# Use the OAuth Request Token received in the previous step to redirect the user to authorize your developer App for access.
def get_user_authorization(resource_owner_oauth_token):

    authorization_url = f"https://api.x.com/oauth/authorize?oauth_token={resource_owner_oauth_token}"
    authorization_pin = input(f" \n Send the following URL to the user you want to generate access tokens for. \n → {authorization_url} \n This URL will allow the user to authorize your application and generate a PIN. \n Paste PIN here: ")

    return(authorization_pin)

# Exchange the OAuth Request Token you obtained previously for the user’s Access Tokens.
def get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin):

    oauth = OAuth1Session(CONSUMER_KEY, 
                            client_secret=CONSUMER_SECRET, 
                            resource_owner_key=resource_owner_oauth_token, 
                            resource_owner_secret=resource_owner_oauth_token_secret, 
                            verifier=authorization_pin)
    
    url = "https://api.twitter.com/oauth/access_token"

    try: 
        response = oauth.fetch_access_token(url)
        access_token = response['oauth_token']
        access_token_secret = response['oauth_token_secret']
        user_id = response['user_id']
        screen_name = response['screen_name']
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)

    return(access_token, access_token_secret, user_id, screen_name)

if __name__ == '__main__':
    
    resource_owner_oauth_token, resource_owner_oauth_token_secret = request_token()
    authorization_pin = get_user_authorization(resource_owner_oauth_token)
    access_token, access_token_secret, user_id, screen_name = get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin)
    
    
    print(f"\n User @handle: {screen_name}", f"\n User ID: {user_id}", f"\n User access token: {access_token}", f" \n User access token secret: {access_token_secret} \n")
    
    
    path_to_save_token = ' '
    
    session_entry = {
        "oauth_token": access_token,
        "oauth_token_secret": access_token_secret
    }
    
    try:
        with open(path_to_save_token, "a") as f:
            f.write(json.dumps(session_entry) + "\n")
        print("Authentication successful. Session appended to", path_to_save_token)
    except Exception as e:
        print(f"Failed to write session information: {e}")
        sys.exit(1)

Thank you, I successfully generated sessions.jsonl using the python script you provided, but nitter is unable to pull any information through this sessions.jsonl.

It shows a 'Fetch error, API: list, errors: (errors: @[(code: couldntAuth, message: "Could not authenticate you"'.

I am using the free version of the API. Does this mean I must use the paid version of the API?

same for me also, Nitter’s failing to fetch info using those session tokens, and my Twitter account got suspended 😅.

ganesh-RW avatar Nov 04 '25 10:11 ganesh-RW

Do you mean Nitter uses Oauth1 token and token_secret, instead of Oauth2?

Check the official get_session.py. I think it is. It is using oauth_token and oauth_token_secret, and I ask the Gemini to confirm that it is oauth 1.0

0xcathiefish avatar Nov 04 '25 11:11 0xcathiefish

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret. It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work. If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation. I'm very interested in the workaround you mentioned. If possible, could you please share the Python file? Thanks in advance!

Hi ~ I'll happy if this helps! Please note that you'll need to configure your own CONSUMER_KEY, CONSUMER_SECRET, and path_to_save_token I've also modified the script slightly so that, just like Nitter's official get_session.py, it will automatically append the obtained token to your sessions.jsonl file.

To run it:

  1. Execute the Python script.It will output an authorization URL. Open this link in your browser.
  2. After authorizing the app, you will be given a PIN code.
  3. Copy this PIN code and paste it back into your terminal.It will then fetch all the information you need.

get_session_alternative.py

# -*- coding: utf-8 -*-
# Copyright 2017 Twitter, Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0

# This script generates a user's access tokens which can be used to make requests on behalf of the user, also known as OAuth 1.0a (user context) authentication method
# Docs: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens

import sys
import requests
from requests_oauthlib import OAuth1Session
import json

# You need  use your own key and secret, which you can fetch from official Twitter development portal.
# https://developer.x.com/en/portal/dashboard
CONSUMER_KEY = ' '
CONSUMER_SECRET = ' '

# Request an OAuth Request Token. This is the first step of the 3-legged OAuth flow. This generates a token that you can use to request user authorization for access.
def request_token():

    oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET, callback_uri='oob')

    url = "https://api.twitter.com/oauth/request_token"

    try:
        response = oauth.fetch_request_token(url)
        resource_owner_oauth_token = response.get('oauth_token')
        resource_owner_oauth_token_secret = response.get('oauth_token_secret')
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)
    
    return resource_owner_oauth_token, resource_owner_oauth_token_secret

# Use the OAuth Request Token received in the previous step to redirect the user to authorize your developer App for access.
def get_user_authorization(resource_owner_oauth_token):

    authorization_url = f"https://api.x.com/oauth/authorize?oauth_token={resource_owner_oauth_token}"
    authorization_pin = input(f" \n Send the following URL to the user you want to generate access tokens for. \n → {authorization_url} \n This URL will allow the user to authorize your application and generate a PIN. \n Paste PIN here: ")

    return(authorization_pin)

# Exchange the OAuth Request Token you obtained previously for the user’s Access Tokens.
def get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin):

    oauth = OAuth1Session(CONSUMER_KEY, 
                            client_secret=CONSUMER_SECRET, 
                            resource_owner_key=resource_owner_oauth_token, 
                            resource_owner_secret=resource_owner_oauth_token_secret, 
                            verifier=authorization_pin)
    
    url = "https://api.twitter.com/oauth/access_token"

    try: 
        response = oauth.fetch_access_token(url)
        access_token = response['oauth_token']
        access_token_secret = response['oauth_token_secret']
        user_id = response['user_id']
        screen_name = response['screen_name']
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)

    return(access_token, access_token_secret, user_id, screen_name)

if __name__ == '__main__':
    
    resource_owner_oauth_token, resource_owner_oauth_token_secret = request_token()
    authorization_pin = get_user_authorization(resource_owner_oauth_token)
    access_token, access_token_secret, user_id, screen_name = get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin)
    
    
    print(f"\n User @handle: {screen_name}", f"\n User ID: {user_id}", f"\n User access token: {access_token}", f" \n User access token secret: {access_token_secret} \n")
    
    
    path_to_save_token = ' '
    
    session_entry = {
        "oauth_token": access_token,
        "oauth_token_secret": access_token_secret
    }
    
    try:
        with open(path_to_save_token, "a") as f:
            f.write(json.dumps(session_entry) + "\n")
        print("Authentication successful. Session appended to", path_to_save_token)
    except Exception as e:
        print(f"Failed to write session information: {e}")
        sys.exit(1)

Thank you, I successfully generated sessions.jsonl using the python script you provided, but nitter is unable to pull any information through this sessions.jsonl.

It shows a 'Fetch error, API: list, errors: (errors: @[(code: couldntAuth, message: "Could not authenticate you"'.

I am using the free version of the API. Does this mean I must use the paid version of the API?

I'm experiencing this too, but with a strange twist. The new accounts I imported yesterday are all showing this error.

However, my old accounts (generated earlier than this issue had been created) is still working perfectly. I even tried re-generating it using my alternative method, and it works without any errors. It seems only the newly imported accounts are failing.

0xcathiefish avatar Nov 04 '25 11:11 0xcathiefish

It seems that trying to pass the anti-bot detection is tough. I reverted to using the earlier website authorization method to get the OAuth token and secret, which requires you to complete the authorization on a real user device. As an alternative, this works but inconvenient. You have to log in to each account manually in Chrome, then authorize it in the website to get the token. Looking forward to a smarter way...

Hi, "then authorize it in the website to get the token." what is the website you talking about?

My method is to self-host an application that uses the official OAuth 1.0 flow. Once you authorize the App with your Twitter account, the backend captures the access token and secret. It's just really inconvenient that you have to open the authorization link on an actual device, like in Chrome, for it to work. If you really need this workaround as an althernative, I'm glad to share this simple Python file I forked from an official Twitter script.

Hi, thanks for the explanation. I'm very interested in the workaround you mentioned. If possible, could you please share the Python file? Thanks in advance!

Hi ~ I'll happy if this helps! Please note that you'll need to configure your own CONSUMER_KEY, CONSUMER_SECRET, and path_to_save_token I've also modified the script slightly so that, just like Nitter's official get_session.py, it will automatically append the obtained token to your sessions.jsonl file.

To run it:

  1. Execute the Python script.It will output an authorization URL. Open this link in your browser.
  2. After authorizing the app, you will be given a PIN code.
  3. Copy this PIN code and paste it back into your terminal.It will then fetch all the information you need.

get_session_alternative.py

# -*- coding: utf-8 -*-
# Copyright 2017 Twitter, Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0

# This script generates a user's access tokens which can be used to make requests on behalf of the user, also known as OAuth 1.0a (user context) authentication method
# Docs: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens

import sys
import requests
from requests_oauthlib import OAuth1Session
import json

# You need  use your own key and secret, which you can fetch from official Twitter development portal.
# https://developer.x.com/en/portal/dashboard
CONSUMER_KEY = ' '
CONSUMER_SECRET = ' '

# Request an OAuth Request Token. This is the first step of the 3-legged OAuth flow. This generates a token that you can use to request user authorization for access.
def request_token():

    oauth = OAuth1Session(CONSUMER_KEY, client_secret=CONSUMER_SECRET, callback_uri='oob')

    url = "https://api.twitter.com/oauth/request_token"

    try:
        response = oauth.fetch_request_token(url)
        resource_owner_oauth_token = response.get('oauth_token')
        resource_owner_oauth_token_secret = response.get('oauth_token_secret')
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)
    
    return resource_owner_oauth_token, resource_owner_oauth_token_secret

# Use the OAuth Request Token received in the previous step to redirect the user to authorize your developer App for access.
def get_user_authorization(resource_owner_oauth_token):

    authorization_url = f"https://api.x.com/oauth/authorize?oauth_token={resource_owner_oauth_token}"
    authorization_pin = input(f" \n Send the following URL to the user you want to generate access tokens for. \n → {authorization_url} \n This URL will allow the user to authorize your application and generate a PIN. \n Paste PIN here: ")

    return(authorization_pin)

# Exchange the OAuth Request Token you obtained previously for the user’s Access Tokens.
def get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin):

    oauth = OAuth1Session(CONSUMER_KEY, 
                            client_secret=CONSUMER_SECRET, 
                            resource_owner_key=resource_owner_oauth_token, 
                            resource_owner_secret=resource_owner_oauth_token_secret, 
                            verifier=authorization_pin)
    
    url = "https://api.twitter.com/oauth/access_token"

    try: 
        response = oauth.fetch_access_token(url)
        access_token = response['oauth_token']
        access_token_secret = response['oauth_token_secret']
        user_id = response['user_id']
        screen_name = response['screen_name']
    except requests.exceptions.RequestException as e:
            print(e)
            sys.exit(120)

    return(access_token, access_token_secret, user_id, screen_name)

if __name__ == '__main__':
    
    resource_owner_oauth_token, resource_owner_oauth_token_secret = request_token()
    authorization_pin = get_user_authorization(resource_owner_oauth_token)
    access_token, access_token_secret, user_id, screen_name = get_user_access_tokens(resource_owner_oauth_token, resource_owner_oauth_token_secret, authorization_pin)
    
    
    print(f"\n User @handle: {screen_name}", f"\n User ID: {user_id}", f"\n User access token: {access_token}", f" \n User access token secret: {access_token_secret} \n")
    
    
    path_to_save_token = ' '
    
    session_entry = {
        "oauth_token": access_token,
        "oauth_token_secret": access_token_secret
    }
    
    try:
        with open(path_to_save_token, "a") as f:
            f.write(json.dumps(session_entry) + "\n")
        print("Authentication successful. Session appended to", path_to_save_token)
    except Exception as e:
        print(f"Failed to write session information: {e}")
        sys.exit(1)

Thank you, I successfully generated sessions.jsonl using the python script you provided, but nitter is unable to pull any information through this sessions.jsonl. It shows a 'Fetch error, API: list, errors: (errors: @[(code: couldntAuth, message: "Could not authenticate you"'. I am using the free version of the API. Does this mean I must use the paid version of the API?

same for me also, Nitter’s failing to fetch info using those session tokens, and my Twitter account got suspended 😅.

My new accounts group didn't got suspended, but not worked with this error too.

0xcathiefish avatar Nov 04 '25 11:11 0xcathiefish

I think I figured out what's going on. Here's my current hypothesis:

Twitter's OAuth tokens are strictly bound to the specific app that was authorized to generate them. This means only the app that requested the token can actually use it for API access.

In my previous attempts, I was generating an OAuth token using my own self-built app, but Nitter was (by default) using its own app keys. This mismatch is what caused the couldntAuth (Could not authenticate you) error.

To fix this, I modified the Nitter source code here: https://github.com/zedeus/nitter/blob/662ae90e2246c8a01c811f68750b7e5033e0fa69/src/consts.nim#L5-L6

I replaced the default consumerKey and consumerSecret with the keys from my own app (the same one I used to generate the OAuth tokens).

After I rebuilt the Docker image, the couldntAuth error is now gone.

However, there is a new problem:

Fetch error, API: tweetDetail, errors: (errors: @[(code: 453 (invalid data!), message: 
"You currently have access to a subset of X API V2 endpoints and limited v1.1 endpoints (e.g. media post, oauth) only. 
If you need access to this endpoint, you may need a different access level. 
You can learn more here: https://developer.x.com/en/portal/product")])

It seems this is happening because the app I registered in the Twitter developer portal is on the Free tier. This tier doesn't have permission to access the tweetDetail endpoint, so the requests are still failing, just with a different error.

https://devcommunity.x.com/t/forbidden-403-forbidden-453-you-currently-have-access-to-a-subset-of-twitter-api-v2-endpoints-and-limited-v1-1-endpoints-e-g-media-post-oauth-only/203646/7

So, it seems I might need a paid-tier Twitter app to test this theory.

But that makes me wonder, why does zedeus's key (the default one in the repo) work? Is it a paid one?

0xcathiefish avatar Nov 04 '25 12:11 0xcathiefish

But that makes me wonder, why does zedeus's key (the default one in the repo) work? Is it a paid one?

It's the widely-used, "Twitter for Android" app key that's been in circulation for over a decade. Here are some others: https://gist.github.com/cmj/d7a85dce03ed2116616765b2ce9086f7

It's also my conclusion that they are checking a TLS fingerprint on the app itself, so we'll have to wait and see if someone can figure that part out.


I just checked those other keys listed and I do get an oauth_token using the 'Twitter for Mac" bearerToken, but the permissions are exremeley limited; making it unusable in Nitter, as far as I can tell.

Everything else (Android/iPhone) still fail the Attestation verification.

cmj avatar Nov 04 '25 14:11 cmj

By no means is this a solution to the issue, but I did create a proof-of-concept fork that uses cookies (1 account) for personal use obviously. I don't know how to manage multiple accounts, so that's all stripped out.

https://github.com/cmj/nitter/tree/cookie_header?tab=readme-ov-file#installation

So if anyone want's to get a personal instance running, try this for now.

cmj avatar Nov 04 '25 21:11 cmj

By no means is this a solution to the issue, but I did create a proof-of-concept fork that uses cookies (1 account) for personal use obviously. I don't know how to manage multiple accounts, so that's all stripped out.

https://github.com/cmj/nitter/tree/cookie_header?tab=readme-ov-file#installation

So if anyone want's to get a personal instance running, try this for now.

Hi~

Does Nitter support importing cookies? If so, that would make things much simpler, but I don't seem to see this feature in the source code.

Can it support importing cookies for multiple accounts, similar to how session.jsonl works?

0xcathiefish avatar Nov 05 '25 08:11 0xcathiefish

Does Nitter support importing cookies?

Not "officially", no.

There was a brief moment last year, where we couldn't generate tokens, and someone had already been using cookies in conjunction with oauth_tokens. Not fully understanding anything about how token management is handled (cycling through them and keeping track of x-rate-limits,) I just stripped out those features and made it only use one auth_token (cookie) for all requests.

We might be headed in that direction for good at some point? This last round of changes seems pretty challenging.

I would have to leave fixing all the auth_token management for multiple cookie usage up to someone else as it's pretty far out of my league.

If so, that would make things much simpler, but I don't seem to see this feature in the source code.

They are not used at all in the main repo. Only partially used in a few forks and used exclusively in that cookie_header branch.

Can it support importing cookies for multiple accounts, similar to how session.jsonl works?

One could use the jsonl format for auth_token, ct0/x-csrf-token, already in place, for example. The real task is getting Nitter to manage them all. Most other tools I use that require cookies I just keep a list of the auth_tokens and generate a random 32c string for the ct0, however that detail can be looked at later.

All that said, I wouldn't give up on finding a solution to generating oauth session tokens just yet...

cmj avatar Nov 05 '25 12:11 cmj

this link gives you OAuth_Token & OAuth_Token_Secret without password change

It works with me, but any way I deleted it. I use it with fake accounts only

josephanwar2 avatar Nov 06 '25 11:11 josephanwar2

One could use the jsonl format for auth_token, ct0/x-csrf-token, already in place, for example. The real task is getting Nitter to manage them all. Most other tools I use that require cookies I just keep a list of the auth_tokens and generate a random 32c string for the ct0, however that detail can be looked at later.

@cmj I've built a full solution for this already, allowing you to use a mix of both. Wanna test it? I didn't release it because it was unnecessary, but it seems like that's changing now.

zedeus avatar Nov 06 '25 16:11 zedeus

@zedeus Sure, I'm interested in seeing how well cookies in general will hold up. Hopefully it won't go the way of what happened to OldTweetDeck users in the past 24 hours.

cmj avatar Nov 07 '25 02:11 cmj