Hey there,

I would like to add/remove twitter followers to my own account using the v2 api. I’m confused as to what you mean by the “User Context” in the docs:

The manage follows endpoints enable you to follow or unfollow users. Since you are making a request on behalf of a user, you must authenticate these endpoints with OAuth 1.0a User Context and use the access tokens associated with the user who you would like the follow/unfollow action to happen on behalf of.

I’m assuming that it means I should be using my access_token and access_secret. I used a similar python code snippet that we use for the twitter v1.1 API:

import urllib
import requests
import oauth2


def get_auth(consumer_key: str, consumer_secret: str, access_token: str, access_token_secret: str, http_method: str, url: str, parameters: str):
	consumer = oauth2.Consumer(key=consumer_key, secret=consumer_secret)
	token = oauth2.Token(key=access_token, secret=access_token_secret)
	client = oauth2.Client(consumer, token)
	req = oauth2.Request.from_consumer_and_token(client.consumer, token=client.token, http_method=http_method, http_url=url, parameters=parameters)
	req.sign_request(client.method, client.consumer, client.token)
	return req


consumer_key = ""
consumer_secret = ""

access_token = ""
access_token_secret = ""

user_id = 1081562931937067009
url = f"https://api.twitter.com/2/users/{user_id}/following"

payload = {
	"target_user_id": 2334614718
}

headers = {
	"content-type": "application/json"
}

req = get_auth(consumer_key, consumer_secret, access_token, access_token_secret, http_method="POST", url=url, parameters=payload)
headers = req.to_header()
body = urllib.parse.urlencode(req.get_nonoauth_parameters())

response = requests.post(url, params=body.encode('utf-8'), headers=headers, timeout=(5, 10))

Unfortunately I keep getting this error:

{“title”:“Unauthorized”,“type”:“about:blank”,“status”:401,“detail”:“Unauthorized”}

Any ideas on what I’m doing wrong? Thanks!

Hi,

Here is some Python code you can use to follow me. You may want to change the user ID to be a different user, since I think you already follow me :slight_smile:

This uses the pin-based method, which might be a bit more lightweight.

from requests_oauthlib import OAuth1Session
import os
import json

# In your terminal please set your environment variables by running the following lines of code.
# export 'CONSUMER_KEY'='<your_consumer_key>'
# export 'CONSUMER_SECRET'='<your_consumer_secret>'

consumer_key = os.environ.get("CONSUMER_KEY")
consumer_secret = os.environ.get("CONSUMER_SECRET")

# Be sure to replace your-user-id with your own user ID or one of an authenticating user
# You can find a user ID by using the user lookup endpoint
id = "your-user-id"

# You can change your payload to be a different ID that isn't mine
payload = {"target_user_id": "15772978"}

# Get request token
request_token_url = "https://api.twitter.com/oauth/request_token"
oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)

try:
    fetch_response = oauth.fetch_request_token(request_token_url)
except ValueError:
    print(
        "There may have been an issue with the consumer_key or consumer_secret you entered."
    )

resource_owner_key = fetch_response.get("oauth_token")
resource_owner_secret = fetch_response.get("oauth_token_secret")
print("Got OAuth token: %s" % resource_owner_key)

# Get authorization
base_authorization_url = "https://api.twitter.com/oauth/authorize"
authorization_url = oauth.authorization_url(base_authorization_url)
print("Please go here and authorize: %s" % authorization_url)
verifier = input("Paste the PIN here: ")

# Get the access token
access_token_url = "https://api.twitter.com/oauth/access_token"
oauth = OAuth1Session(
    consumer_key,
    client_secret=consumer_secret,
    resource_owner_key=resource_owner_key,
    resource_owner_secret=resource_owner_secret,
    verifier=verifier,
)
oauth_tokens = oauth.fetch_access_token(access_token_url)

access_token = oauth_tokens["oauth_token"]
access_token_secret = oauth_tokens["oauth_token_secret"]

# Make the request
oauth = OAuth1Session(
    consumer_key,
    client_secret=consumer_secret,
    resource_owner_key=access_token,
    resource_owner_secret=access_token_secret,
)

# Making the request
response = oauth.post(
    "https://api.twitter.com/2/users/{}/following".format(id), json=payload
)

if response.status_code != 200:
    raise Exception(
        "Request returned an error: {} {}".format(response.status_code, response.text)
    )

print("Response code: {}".format(response.status_code))

# Saving the response as JSON
json_response = response.json()
print(json.dumps(json_response, indent=4, sort_keys=True))

Hope this helps!
Jess

2 Likes

Hey Jessica - thanks for sharing your solution, and yes I’m definitely following you!

Unfortunately I wasn’t able to get your solution to work. Would it be possible for you to send me a DM, so I could reply with my code and keys/tokens to test?

I’m able to make other requests, it’s just this one endpoint. Unfortunately the error message is not helpful:

‘{“title”:“Unauthorized”,“type”:“about:blank”,“status”:401,“detail”:“Unauthorized”}’

Thanks!

1 Like

Hi,

So I just double-checked the code and I don’t think it’s related to the sample. Have you tried regenerating the tokens? Have been able to make a request to other v2 endpoints with the same conditionals?

You may want to double-check, you have properly set up your Twitter app, then chances are that you aren’t handling number two properly. If so, please consider using an oauth library (example), using Insomnia, or try using Twurl.

Unfortunately, due to security concerns, we don’t test tokens via DM but I do have a few follow-up questions.

1 Like

Hi,

Thanks for the follow-up!

I followed your code example verbatim:

from requests_oauthlib import OAuth1Session

my_consumer_key = ""
my_consumer_secret = ""

user_id = ""  # my user_id, which matches the first part of the access_token
payload = {"target_user_id": "15772978"}  # Jessica's user_id

# Get request token
request_token_url = "https://api.twitter.com/oauth/request_token"
oauth = OAuth1Session(my_consumer_key, client_secret=my_consumer_secret)

fetch_response = oauth.fetch_request_token(request_token_url)

resource_owner_key = fetch_response.get("oauth_token")
resource_owner_secret = fetch_response.get("oauth_token_secret")
print("Got OAuth token: %s" % resource_owner_key)

# Get authorization
base_authorization_url = "https://api.twitter.com/oauth/authorize"
authorization_url = oauth.authorization_url(base_authorization_url)
print("Please go here and authorize: %s" % authorization_url)
verifier = input("Paste the PIN here: ")

# Get the access token
access_token_url = "https://api.twitter.com/oauth/access_token"
oauth = OAuth1Session(
    my_consumer_key,
    client_secret=my_consumer_secret,
    resource_owner_key=resource_owner_key,
    resource_owner_secret=resource_owner_secret,
    verifier=verifier,
)
oauth_tokens = oauth.fetch_access_token(access_token_url)

access_token = oauth_tokens["oauth_token"]
access_token_secret = oauth_tokens["oauth_token_secret"]

# Make the request
oauth = OAuth1Session(
    my_consumer_key,
    client_secret=my_consumer_secret,
    resource_owner_key=access_token,
    resource_owner_secret=access_token_secret,
)

assert access_token.split("-")[0] == str(user_id)

# Making the request
response = oauth.post(
    "https://api.twitter.com/2/users/{}/following".format(user_id), json=payload
)

if response.status_code != 200:
    raise Exception(
        "Request returned an error: {} {}".format(response.status_code, response.text)
    )

print("Response code: {}".format(response.status_code))

I also added an assert statement, which validates that the first part of the access_token matches my user_id. But the error message is still the same:

Exception: Request returned an error: 401 {"title":"Unauthorized","type":"about:blank","status":401,"detail":"Unauthorized"}

I wish the error message was more useful :slight_smile:

To answer your questions specifically:

Have you tried regenerating the tokens?

  • Yes, and I added the assert statement to validate the access_token

Have been able to make a request to other v2 endpoints with the same conditionals?

  • Yes, I made a search request using these keys which worked correctly. Unfortunately most of the requests in the v2 API require the bearer token, not a user-context. But search worked correctly:
response = oauth.get("https://api.twitter.com/2/tweets/search/recent?query=from%3Atwitterdev%20new%20-is%3Aretweet&max_results=10")

assert response.status_code == 200

You may want to double-check, you have properly set up your Twitter app, then chances are that you aren’t handling number two properly. If so, please consider using an oauth library (example ), using Insomnia, or try using Twurl.

  • I am using requests_oauthlib and not sure what you mean by "handling number two properly*.

Thanks!

The problem I am having is that after clicking the button to authorize the app, I get redirected to redirect_url without being able to see the PIN.

Hey I have the same problem with Java Scribe library is appeared that users/me request work just fine with oauth1 but follow, and another requests fails with 401 Unauthorized