I’m trying to set up a Twitter profile, designed to be a “bot”, where I can send a request to a particular Twitter API endpoint to have that profile tweet. I started in the documentation today to try, and noticed that after creating my developer account, it brought me to “v2” documentation so I started from there. Under “Twitter API v2”, I found a section under “Essential features” called “Manage Tweets”, which looked right for my use case. I noticed an endpoint in that section called “POST /2/tweets”. This made sense so far because that endpoint looked like the one I would use for creating a tweet with my new bot profile.

When I went to the Manage Tweets docs (the page /en/docs/twitter-api/tweets/manage-tweets/introduction on the Twitter developer site) I noticed it said:

Since you are making requests on behalf of a user with all manage Tweets endpoints, you must authenticate with OAuth 1.0a User Context and use the Access Tokens associated with a user that has authorized your App.

I found this confusing because my goal wasn’t to do anything on behalf of any Twitter user. My use case is to build an automated system that would send out tweets on a schedule. It doesn’t make sense that I’d need to do an OAuth flow to do that. I’m used to working with APIs where I can download a client key and set up any automated system I like to make requests to the API’s endpoints using that client key.

I tried running the sample code, the file twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.js on GitHub (can’t post link - forums say the host isn’t allowed) on GitHub, and it does indeed begin an OAuth flow. It brought me to a page where I was logged in as my personal Twitter profile and it had a prompt for giving the app permission to read my data. This is when I realized I was definitely going in the wrong direction. I went back to the docs to try to look for a simpler version of the “create tweet” API endpoint where you’re just making a tweet from your own bot’s profile, not someone else’s, but I couldn’t find out.

How do I accomplish my use case using the Twitter API?

You’ll have to generate an access token and secret just once for your bot, using your API keys, and then you can reuse the keys over and over to tweet on your bot’s behalf - maybe this will help?

The twurl way in that post works for whatever account you’ll be logged in as.

Thanks for the reply. I ended up finding that forum thread which got me on the right track. It was after I read that thread that I realized I wasn’t thinking about Twitter bots correctly. At first, I was thinking that after creating a developer account, I’d automatically get a “developer profile” so to speak. Now I realize that it’s up to me to create a new Twitter account for my bot if I want to tweet from a dedicated profile for the bot. I did that, went through the docs and sample code some more, and eventually got it working.

I was also misunderstanding how many steps are involved in OAuth. Once I realized I had to do a few steps to create the Authorization header for the OAuth flow for each automated request I want to make, I was able to take some of the sample JS code (just the part that uses the crypto and oauth-1.0a NPM modules) to console log an authorization header value which I could sub into the sample cURL requests in the docs.

My code:

import crypto from 'crypto';
import OAuth from 'oauth-1.0a';

try {
  const oauth = OAuth({
    consumer: {
      key: 'ZR39xkPHqNsjlSX77gZtSGCcH',
      secret: 'qzAXA269DM18DwivbKZzunW5uQesSweHNFNotTzxnlKeGdjgyV'
    },
    signature_method: 'HMAC-SHA1',
    hash_function: (baseString, key) => crypto.createHmac('sha1', key).update(baseString).digest('base64')
  });

  const token = {
    key: '1477507964298739712-VFdoDZug9ifkvA9GX89rEqoywZnblu',
    secret: 'amKCrk4X7XKNEqwdDR901sBRy2WhgXr5CrxKWc5bhRuBf'
  };

  const authHeader = oauth.toHeader(oauth.authorize({
    url: `https://api.twitter.com/2/tweets`,
    method: 'POST'
  }, token));

  console.log(authHeader);
} catch (e) {
  console.log(e);
  process.exit(-1);
}

Output was like:

{
  Authorization: 'OAuth oauth_consumer_key="<redacted>", oauth_nonce="<redacted>", oauth_signature="<redacted>", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1641103564", oauth_token="<redacted>", oauth_version="1.0"'
}

The docs at /en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets were slightly incorrect though. They have a sample cURL:

curl -X POST https://api.twitter.com/2/tweets -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-type: application/json" -d '{"text": "Hello World!"}'

Which I found I had to change to:

curl -X POST https://api.twitter.com/2/tweets -H "Authorization: $ACCESS_TOKEN" -H "Content-type: application/json" -d '{"text": "Hello World!"}'

Otherwise I got an error:

{
  "title": "Unsupported Authentication",
  "detail": "Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint.  Supported authentication types are [OAuth 1.0a User Context, OAuth 2.0 User Context].",
  "type": "https://api.twitter.com/2/problems/unsupported-authentication",
  "status": 403
}

I also found that in order to finish setting up the app and get the token and secret, I had to put some value in for “callback URI” and “website URL” in the portal, which were required, even though I had no application for my users (I have no users). So I used “https://example.com” for both required values.

I think for my use case, it was my inexperience with OAuth that made it hard to implement my use case. I was remembering simple, non-OAuth APIs I’d used in the past where I just needed to get a client key, which I could then use in any request I make until I deactivated the client key.

Perhaps Twitter could make the docs a bit easier for someone like me. The docs could explain that because it’s OAuth based, the developer needs to create a new Twitter account that the tweets will come from, and that they’ll be expected to use mock values for “callback URI” and “website URL” because they aren’t using the Twitter app for authentication purposes, and that they’ll have to complete a step one time to provision their bot that normally users would do each time they sign up to use your app (the authorize step).

Only then will they get all four of the long lived credentials (the consumer key, consumer secret, token, and token secret) that they need to make requests to the Twitter API in an automated way.

EDIT:

Woops, just realized I posted the creds in the latest edit. Too late to redact them, so I just regenerated a new API Key and Secret in the portal. I’m assuming that automatically invalidates all access tokens and secrets generated using the client key.

…yep, looks like it did:

#!/bin/bash

curl -X POST \
  https://api.twitter.com/2/tweets \
  -H 'Authorization: OAuth oauth_consumer_key="ZR39xkPHqNsjlSX77gZtSGCcH", oauth_nonce="O70pkmQmoGcA4IlsemFvRkqLwq1JcehW", oauth_signature="6X7xcqP0rjMi3k5iJPzkq9%2B0Nlw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1641104950", oauth_token="1477507964298739712-VFdoDZug9ifkvA9GX89rEqoywZnblu", oauth_version="1.0"' \
  -H 'Content-Type: application/json' \
  -d '{"text": "Hello World 2!"}'
{
  "title": "Unauthorized",
  "type": "about:blank",
  "status": 401,
  "detail": "Unauthorized"
}
2 Likes

Hey I still don’t understand how you get from authHeader output to the curl statement…

where do you use the authHeader info and where do I get $ACCESS_TOKEN?

I’m having the same issue as you doing the same thing you explain here. I’m baffled that being the owner of both the developer account and the bot account that this is so confusing.