How to get a count of followers from Twitter API and trendline - twitter

I am in the process of writing some reports for the number of followers over time for Twitter, however after substantial searches and trial and error, I have not being able to get the number of followers over time - particularly past number of followers.
I know there is an API to get the individual userIds for the followers, but thats an overkill for what I need and I would have to call it everyday. Ideally it would be great if I can pass a date and it could return the number of followers.
Does anyone have any experience with this and what the API might be!
Thanks

While there is no direct API to get the trendline, getting the followers count is fairly easy, access via the URL:
http://api.twitter.com/1/users/show.json?user_id=12345
The documentation has it all # https://dev.twitter.com/docs/api/1/get/users/show
To get the trendline, seems like I will need to query it on a daily basis!
Updated to Twitter API v1.1
https://api.twitter.com/1.1/users/show.json?user_id=12345
Documentation at https://dev.twitter.com/docs/api/1.1/get/users/show
Updated 31-May-2018
The new API end point is at
https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-show

Here is a simple PHP exemple using CURL, with no library involved, to get the followers_count of a selected profile (here #TwitterFrance) using v2 API and the bearer token (bearer token is some kind of simplified method to access public data through an OAuth 2.0 API)
$authorization = "Authorization: Bearer YOUREXTRALONGBEARERYOUREXTRALONGBEARERYOUREXTRALONGBEARERYOUREXTRALONGBEARERYOUREXTRALONGBEARERYOUREXTRALONGBEAR";
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', $authorization));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, "https://api.twitter.com/2/users/by/username/TwitterFrance?user.fields=public_metrics");
$result = curl_exec($ch);
curl_close($ch);
if (is_string($result)) {
echo (json_decode($result)->data->public_metrics->followers_count);
die();
}

I know this is an old question but I want to give an answer for those who still looking for an alternative way to get Twitter followers count. After I spent some time on documentation, I found that you’ll need to apply for Elevated access via the Developer Portal, in order to get more than Essential information. You need to apply for additional access within the developer portal for this access level. You can check Access Levels from here.
https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#v2-access-level
After some googling, I found this. No authentication is needed but I can't guarantee that it will not be taken down in the future. Just provide the screen_name of any account you want. E.g.
https://cdn.syndication.twimg.com/widgets/followbutton/info.json?screen_names=binance
...which will give you something like this:
[
{
"following": false,
"id": "877807935493033984",
"screen_name": "binance",
"name": "Binance",
"protected": false,
"followers_count": 6959348,
"formatted_followers_count": "6.96M followers",
"age_gated": false
}
]

In Swift 4.2 and Xcode 10.1 to get twitter followers_count
Here you need to integrate twitter SDK into you app and follow integration details https://github.com/twitter/twitter-kit-ios
//This is complete url
https://api.twitter.com/1.1/users/show.json?screen_name=screenName
func getStatusesUserTimeline(accessToken:String) {
let userId = "109*************6"
let twitterClient = TWTRAPIClient(userID: userId)
twitterClient.loadUser(withID: userId) { (user, error) in
print(userId)
print(user ?? "Empty user")
if user != nil {
var request = URLRequest(url: URL(string: "https://api.twitter.com/1.1/users/show.json?screen_name=screenName")!)
request.httpMethod = "GET"
request.setValue("Bearer "+accessToken, forHTTPHeaderField: "Authorization")
print(request)
let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do {
let response = try JSONSerialization.jsonObject(with: data, options: []) as! Dictionary<String,Any>
print(response)
// print((response["statuses"] as! Array<Any>).count)
} catch let error as NSError {
print(error)
}
}
task.resume()
} else {
print(error?.localizedDescription as Any)
}
}
}

Using Python:
import tweepy
twitter = tweepy.Client(bearer_token="your bearer token")
Single user:
print(twitter.get_user(username="twitter username", user_fields=["public_metrics"]).data.public_metrics['followers_count'])
Multiple users:
for i in twitter.get_users(usernames=twitterProfiles, user_fields=["public_metrics"]).data:
print(i.public_metrics['followers_count'])

Related

Apple Music API getting 403 'Forbidden' when trying to fetch recent tracks

Hey there people of the great world!
I am attempting to retrieve all the recent tracks from the Apple Music API using the following HTTP request:
static let AMRecentTracks = "https://api.music.apple.com/v1/me/recent/played/tracks"
Shamefully I keep receiving an error 403 forbidden. This makes no sense as I am able to successfully do a search request using this:
static let AMMusicURL = "https://api.music.apple.com/v1/catalog/"
Any help would be greatly appreciated. It seems there is little information about this specific case. I've checked.
I am making the request with a Bearer and a token.
static let prefix = "Bearer "
to it I am appending the developer token.
Below is my code:
func fetchRecentPlayedResources(_ complition: #escaping (Result<[Song], Error>)->Void) {
let suffix = "?types=songs"
guard let searchURL = URL(string: Request.AMRecentTracks + suffix) else { return }
var musicRequest = URLRequest(url: searchURL)
musicRequest.httpMethod = HTTPRequest.GET
musicRequest.addValue(HTTPRequest.prefix + self.developerToken, forHTTPHeaderField: HTTPRequest.authorization)
URLSession.shared.dataTask(with: musicRequest) { [weak self] (data, response, error) in
guard let self = self else { return }
if let error = error {
complition(.failure(error))
} else {
if let data = data {
self.parseSongs(with: data) { songs in
complition(.success(songs))
}
}
}
}.resume()
}
You need to add two tokens when sending personalised requests, the example from the iTunes API documentation shows this:
curl -v -H 'Music-User-Token: [music user token]' -H 'Authorization: Bearer [developer token]' "https://api.music.apple.com/v1/catalog/us/songs/203709340"
So maybe just adding the developer token as Authorisation isn't enough and this is why you are getting a 403 error. Again, as the documentation states - this is due to invalid or insufficient authorisation.

HTTP DELETE Works From Browser But Not From Postman or IOS App

When attempting an http request to my rest api, I continually get a 401 error when using the following code. I don not get this error making any other type of request. I have provided the function that makes the request below.
func deleteEvent(id: Int){
eventUrl.append(String(id))
let request = NSMutableURLRequest(url: NSURL(string: eventUrl)! as URL)
request.httpMethod = "DELETE"
print(eventUrl)
eventUrl.removeLast()
print(self.token!)
request.allHTTPHeaderFields = ["Authorization": "Token \(self.token)"]
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
if error != nil {
print("error=\(String(describing: error))")
//put variable that triggers error try again view here
return
}
print("response = \(String(describing: response))")
}
task.resume()
}
When sending the delete request with postman, the rest api just returns the data I want to delete but does not delete it. For reference I have posted the view and permissions classes associated with this request Any help understanding why this may be resulting in an error is greatly appreciated!
Views.py
class UserProfileFeedViewSet(viewsets.ModelViewSet):
"""Handles creating, reading and updating profile feed items"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.ProfileFeedItemSerializer
queryset = models.ProfileFeedItem.objects.all()
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
Permissions.py
class UpdateOwnStatus(permissions.BasePermission):
"""Allow users to update their own status"""
def has_object_permission(self, request, view, obj):
"""Check the user is trying to update their own status"""
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile.id == request.user.id
HEADER SENT WITH DELETE REQUEST VIA POSTMAN
Preface: You leave out too much relevant information from the question for it to be properly answered. Your Swift code looks, and please don't be offended, a bit beginner-ish or as if it had been migrated from Objective-C without much experience.
I don't know why POSTMAN fails, but I see some red flags in the Swift code you might want to look into to figure out why your iOS app fails.
I first noticed that eventUrl seems to be a String property of the type that contains the deleteEvent function. You mutate it by appending the event id, construct a URL from it (weirdly, see below), then mutate it back again. While this in itself is not necessarily wrong, it might open the doors for racing conditions depending how your app works overall.
More importantly: Does your eventUrl end in a "/"? I assume your DELETE endpoint is of the form https://somedomain.com/some/path/<id>, right? Now if eventUrl just contains https://somedomain.com/some/path your code constructs https://somedomain.com/some/path<id>. The last dash is missing, which definitely throws your backend off (how I cannot say, as that depends how the path is resolved in your server app).
It's hard to say what else is going from from the iOS app, but other than this potential pitfall I'd really recommend using proper Swift types where possible. Here's a cleaned up version of your method, hopefully that helps you a bit when debugging:
func deleteEvent(id: Int) {
guard let baseUrl = URL(string: eventUrl), let token = token else {
// add more error handling code here and/or put a breakpoint here to inspect
print("Could not create proper eventUrl or token is nil!")
return
}
let deletionUrl = baseUrl.appendingPathComponent("\(id)")
print("Deletion URL with appended id: \(deletionUrl.absoluteString)")
var request = URLRequest(url: deletionUrl)
request.httpMethod = "DELETE"
print(token) // ensure this is correct
request.allHTTPHeaderFields = ["Authorization": "Token \(token)"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Encountered network error: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse {
// this is basically also debugging code
print("Endpoint responded with status: \(httpResponse.statusCode)")
print(" with headers:\n\(httpResponse.allHeaderFields)")
}
// Debug output of the data:
if let data = data {
let payloadAsSimpleString = String(data: data, encoding: .utf8) ?? "(can't parse payload)"
print("Response contains payload\n\(payloadAsSimpleString)")
}
}
task.resume()
}
This is obviously still limited in terms of error handling, etc., but a little more swifty and contains more console output that will hopefully be helpful.
The last important thing is that you have to ensure iOS does not simply block your request due to Apple Transport Security: Make sure your plist has the expected entries if needed (see also here for a quick intro).

login user with GET request swift

I have created a screen with a text field called customer_number text field and another screen with a text field called password text field. I want to integrate my app with an existing API made by the backend developers. I am new to IOS Development and I don't know how to go about it. How do I make a get request and pass the login credentials for the user to login?
I want to get the customer number from the API and pass it to the app and enable the customer to log in.
I think this question is too big and complex to be replied exhaustively. You didn't tell us about the API. What kind of input does it take? What kind of response?
Supposing the simplest case. You API expects JSON objects as input and respond with another JSON object containing the information you request.
I usually do tasks like this using the NSURLRequest.
let js = ["Username":username, "Password":password]
let session = URLSession.init(configuration: .default)
let url = URL(...)
var req = URLRequest.init(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10)
req.httpMethod = "POST"
// Add some header key-value pairs
req.addValue(..., forHTTPHeaderField: ...)
...
let task = session.dataTask(with: request) { (data, response, error) in
guard error == nil else { return }
guard let responseData = data else { return }
let code = (response as! HTTPURLResponse).statusCode
// Checking for code == 200 states for authorised user. Generally log-in APIs should return some 4xx code if not allowed or non-authorised user.
if code == 200 {
// Now we try to convert returned data as a JSON object
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
// use your json object here, for example checking if contains the user number...
} catch {
// handle errors
}
}
}
task.resume()
I coded this very quickly, please check the correctness of al mechanism!

POST API call using APIGateway returns "Internal Server Error" in swift 4, but works everywhere else

I created a nodejs lambda function in AWS and exposed it using APIGateway with methods GET, PUT, POST, and DELETE (all setup with proxy). All methods have been tested and work in AWS using APIGateway, and then outside of AWS using Postman.
First, I called the GET method for the endpoint in my Swift 4 project, and it is successful.
BUT I have tried just about everything to call the POST method in swift and cannot get it to execute successfully. This is what I am currently trying after researching online:
let awsEndpoint: String = "https://host/path"
guard let awsURL = URL(string: awsEndpoint) else {
print("Error: cannot create URL")
return
}
var postUrlRequest = URLRequest(url: awsURL)
postUrlRequest.httpMethod = "POST"
postUrlRequest.addValue("John Doe", forHTTPHeaderField: "name")
postUrlRequest.addValue("imageurl.com", forHTTPHeaderField: "imageUrl")
URLSession.shared.dataTask(with: postUrlRequest) { data, response, error in
guard let data = data else { return }
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: data,
options: []) as? [String: Any] else {
print("Error")
return
}
} catch let err{
print(err)
}
}.resume()
The response I get is ["message":"Internal Server Error"]. When I look at the logs in CloudWatch they are not very descriptive. The error log for the post call is:
"Execution failed due to configuration error: Malformed Lambda proxy response"
After researching this issue aws suggests to format the response in a specific way and I have updated my nodejs lambda function to mimmic this.
case "POST":
pool.getConnection(function(err, connection) {
const groupName = event.headers.name;
const imageUrl = event.headers.imageUrl;
var group = {Name: groupName, ImageUrl: imageUrl, IsActive:true, Created:date, Updated:date};
var query = "INSERT INTO Groups SET ?";
connection.query(query,group, function (error, results, fields) {
var responseBody = {
"key3": "value3",
"key2": "value2",
"key1": "value1"
};
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": true
};
if (error) callback(error);
else callback(null, response)
connection.release();
});
});
break;
Like I said previously, this works when testing everywhere except swift 4. My GET call works with swift 4, so I do not think it is an issue with allowing anything in the info.plist but I could be wrong. I have tried just about everything, but cannot seem to get past this error.
I fixed this issue myself. After allowing ALL log output in API Gateway for that endpoint, I found that somewhere along the way my headers were being converted to all lowercase.
'imageUrl' became 'imageurl'
It was throwing an error because in my lambda function, it could not find 'imageUrl'
I think this is a conversion that is happening in APIGateway because I have never come across this issue with swift.

Cannot find serverAuthCode for user using Google Client Library in Swift

So the problem that I cam experincrein is that I am trying to authenticate with Azure using client side auth with the Google API Client Library. I can get the refresh, access and ID tokens but the serverAuthCode is nil. I need the serverAuthCode in order to create the HTTP request to call the Azure authentication endpoint. The Azure SDK for iOS doesn't support client side authentication for Google (I've spoken to multiple engineers at Microsoft all of them have suggested not using their SDK for authentication because they don't support it). I don't know what to do besides try wrapping my head around AWS. Any Help?
Also, here is the piece of code
func viewController(vc : UIViewController, finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
let azureGoogleServerAuthToken = authResult.userData.serverAuthCode
let azureGoogleIdToken = authResult.parameters["id_token"] as! String
let request = NSMutableURLRequest(URL: NSURL(string: "https://retip-ios.azurewebsites.net/.auth/login/google")!)
request.HTTPMethod = "POST"
let postString = "authorization_code=\(azureGoogleServerAuthToken)&id_token=\(azureGoogleIdToken)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
It fails with an exception saying that it is finding nil every time. I have isolated it out to verify that this is what is causing the exception.
Thanks in advance.
After speaking with Azure support directly and working with them for over a week to find a solution, it appears as if their documentation is purposely vague on the issue because they do not support client side authentication with Google. Any solution would need to be custom built, meaning that this is not achievable through the Azure SDK.

Resources