A super awesome Twitter API client for Python.

Overview

birdy

birdy is a super awesome Twitter API client for Python in just a little under 400 LOC.

TL;DR

Features

Installation

The easiest and recommended way to install birdy is from PyPI

pip install birdy

Usage

Import client and initialize it:

from birdy.twitter import UserClient
client = UserClient(CONSUMER_KEY,
                    CONSUMER_SECRET,
                    ACCESS_TOKEN,
                    ACCESS_TOKEN_SECRET)

GET example (GET users/show):

response = client.api.users.show.get(screen_name='twitter')
response.data

POST example (POST statuses/update):

response = client.api.statuses.update.post(status='Hello @pybirdy!')

Dynamic URL example (POST statuses/destroy/:id):

response = client.api.statuses.destroy['240854986559455234'].post()

Streaming API example (Public Stream POST statuses/filter):

response = client.stream.statuses.filter.post(track='twitter')

for data in response.stream():
    print data

Supported Python version

birdy works with both python2 (2.7+) and python3 (3.4+).

Why another Python Twitter API client? Aren't there enough?

The concept behind birdy is so simple and awesome that it just had to be done, and the result is a super light weight and easy to use API client, that covers the whole Twitter REST API in just a little under 400 lines of code.

To achieve this, birdy relies on established, battle tested python libraries like requests and requests-ouathlib to do the heavy lifting, but more importantly it relies on Python's dynamic nature to automatically construct API calls (no individual wrapper functions for API resources needed). This allows birdy to cover all existing Twitter API resources and any future additions, without the need to update birdy itself.

Includes full support for both OAuth1 (user) and OAuth2 (application) authentication workflows.

Finally, birdy is simple and explicit by design, besides error handling and JSON decoding it doesn't process the returned data in any way, that is left for you to handle (who'd know better what to do with it).

OK, I'm sold, but how do I use it? How does this dynamic API construction work?

The easiest way to show you is by example. Lets say you want to query Twitter for @twitter user information. The Twitter API resource for this is GET users/show (Twitter docs).

First you will need to import a client, here we import UserClient (OAuth1) and than initialize it.

from birdy.twitter import UserClient
client = UserClient(CONSUMER_KEY,
                    CONSUMER_SECRET,
                    ACCESS_TOKEN,
                    ACCESS_TOKEN_SECRET)

To query the GET /users/show API resource and pass in the parameter screen_name='twitter' you do this.

resource = client.api.users.show
response = resource.get(screen_name='twitter')

What happens here is very simple, birdy translates the users.show part after client.api into the appropriate API resource path ('users/show'). Then when you call get() on the resource, birdy constructs a full resource URL, appends any parameters passed to get() to it and makes a GET request to that URL and returns the result.

Usually the above example would be shortened to just one line like this.

response = client.api.users.show.get(screen_name='twitter')

Making a post request is similar, if for example, you would like to post a status update, this is how to do it. The API resource is POST statuses/update (Twitter docs).

response = client.api.statuses.update.post(status='Hello @pybirdy!')

Like before the part after client.api gets converted to the correct path, only this time post() is called instead of get(), so birdy makes a POST request and pass parameters (and files) as part of the request body.

For cases when dynamic values are part of the API resource URL, like when deleting a tweet at POST statuses/destroy/:id (Twitter docs), birdy supports an alternative, dictionary lookup like, syntax. For example, deleting a tweet with id '240854986559455234' looks like this.

response = client.api.statuses.destroy['240854986559455234'].post()

By now it should be clear what happens above, birdy builds the API resource path and than makes a POST request, the only difference is that part of the API path is provided like a dictionary key lookup.

Actually any call can be written in this alternative syntax, use whichever you prefer. Both syntax forms can be freely combined as in the example above. Some more examples:

response = client.api['users/show'].get(screen_name='twitter')

response = client.api['users']['show'].get(screen_name='twitter')

response = client.api['statuses/destroy']['240854986559455234'].post()

Is Streaming API supported as well?

Sure, since version 0.2, birdy comes with full support for Streaming API out of the box. Access to the Streaming API is provided by a special StreamClient.

StreamClient can't be used to obtain access tokens, but you can use UserClient to get them.

To work with the Streaming API, first import the client and initialize it.

from birdy.twitter import StreamClient
client = StreamClient(CONSUMER_KEY,
                    CONSUMER_SECRET,
                    ACCESS_TOKEN,
                    ACCESS_TOKEN_SECRET)

To access resources on the Public stream, like POST statuses/filter (Twitter docs)

resource = client.stream.statuses.filter.post(track='twitter')

For User stream resource GET user (Twitter docs)

resource = client.userstream.user.get()

And for Site stream resource GET site (Twitter docs)

resource = client.sitestream.site.get()

To access the data in the stream you iterate over resource.stream() like this

for data in resource.stream():
   print data

Great, what about authorization? How do I get my access tokens?

birdy supports both OAuth1 and OAuth2 authentication workflows by providing two different clients, a UserClient and AppClient respectively. While requests to API resources, like in above examples are the same in both clients, the workflow for obtaining access tokens is slightly different.

Before you get started, you will need to register your application with Twitter, to obtain your application's CONSUMER_KEY and CONSUMER_SECRET.

OAuth1 workflow for user authenticated requests (UserClient)

Step 1: Creating a client instance

First you need to import the UserClient and create an instance with your apps CONSUMER_KEY and CONSUMER_SECRET.

from birdy.twitter import UserClient

CONSUMER_KEY = 'YOUR_APPS_CONSUMER_KEY'
CONSUMER_SECRET = 'YOUR_APPS_CONSUMER_SECRET'
CALLBACK_URL = 'https://127.0.0.1:8000/callback'

client = UserClient(CONSUMER_KEY, CONSUMER_SECRET)

Step 2: Get request token and authorization URL

Pass callback_url only if you have a Web app, Desktop and Mobile apps do not require it.

Next you need to fetch request token from Twitter. If you are building a Sign-in with Twitter type application it's done like this.

token = client.get_signin_token(CALLBACK_URL)

Otherwise like this.

token = client.get_authorize_token(CALLBACK_URL)

Save token.oauth_token and token.oauth_token_secret for later user, as this are not the final token and secret.

ACCESS_TOKEN = token.oauth_token
ACCESS_TOKEN_SECRET = token.oauth_token_secret

Direct the user to Twitter authorization url obtained from token.auth_url.

Step 3: OAuth verification

If you have a Desktop or Mobile app, OAUTH_VERIFIER is the PIN code, you can skip the part about extraction.

After authorizing your application on Twitter, the user will be redirected back to the callback_url provided during client initialization in Step 1.

You will need to extract the OAUTH_VERIFIER from the URL. Most web frameworks provide an easy way of doing this or you can parse the URL yourself using urlparse module (if that is your thing).

Django and Flask examples:

#Django
OAUTH_VERIFIER = request.GET['oauth_verifier']

#Flash
OAUTH_VERIFIER = request.args.get('oauth_verifier')

Once you have the OAUTH_VERIFIER you can use it to obtain the final access token and secret. To do that you will need to create a new instance of UserClient, this time also passing in ACCESS_TOKEN and ACCESS_TOKEN_SECRET obtained in Step 2 and then fetch the tokens.

client = UserClient(CONSUMER_KEY, CONSUMER_SECRET,
                    ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

token = client.get_access_token(OAUTH_VERIFIER)

Now that you have the final access token and secret you can save token.oauth_token and token.oauth_token_secret to the database for later use, also you can use the client to start making API request immediately. For example, you can retrieve the users home timeline like this.

response = client.api.statuses.home_timeline.get()
response.data

That's it you have successfully authorized the user, retrieved the tokens and can now make API calls on their behalf.

OAuth2 workflow for app authenticated requests (AppClient)

Step 1: Creating a client instance

For OAuth2 you will be using the AppClient, so first you need to import it and create an instance with your apps CONSUMER_KEY and CONSUMER_SECRET.

from birdy.twitter import AppClient

CONSUMER_KEY = 'YOUR_APPS_CONSUMER_KEY'
CONSUMER_SECRET = 'YOUR_APPS_CONSUMER_SECRET'

client = AppClient(CONSUMER_KEY, CONSUMER_SECRET)

Step 2: Getting the access token

OAuth2 workflow is much simpler compared to OAuth1, to obtain the access token you simply do this.

access_token = client.get_access_token()

That's it, you can start using the client immediately to make API request on behalf of the app. It's recommended you save the access_token for later use. You initialize the client with a saved token like this.

client = AppClient(CONSUMER_KEY, CONSUMER_SECRET, SAVED_ACCESS_TOKEN)

Keep in mind that OAuth2 authenticated requests are read-only and not all API resources are available. Check Twitter docs for more information.

Any other useful features I should know about?

Of course, birdy comes with some handy features, to ease your development, right out of the box. Lets take a look at some of the goodies.

Automatic JSON decoding

JSON data returned by the REST and Streaming API is automatically decoded to native Python objects, no extra coding necessary, start using the data right away.

JSONObject

When decoding JSON data, objects are, instead of a regular Python dictionary, converted to a JSONObject, which is dictionary subclass with attribute style access in addition to regular dictionary lookup style, for convenience. The following code produces the same result

followers_count = response.data['followers_count']

followers_count = response.data.followers_count

ApiResponse

Calls to REST API resources return a ApiResponse, which in addition to returned data, also gives you access to response headers (useful for checking rate limits) and resource URL.

response.data           # decoded JSON data
response.resource_url   # resource URL
response.headers        # dictionary containing response HTTP headers

StreamResponse

StreamResponse is returned when calling Streaming API resources and provides the stream() method which returns an iterator used to receive JSON decoded streaming data. Like ApiResponse it also gives you access to response headers and resource URL.

response.stream()       # a generator method used to iterate over the stream

for data in response.stream():
    print data 

Informative exceptions

There are 4 types of exceptions in birdy all subclasses of base BirdyException (which is never directly raised).

  • TwitterClientError raised for connection and access token retrieval errors
  • TwitterApiError raised when Twitter returns an error
  • TwitterAuthError raised when authentication fails, TwitterApiError subclass
  • TwitterRateLimitError raised when rate limit for resource is reached, TwitterApiError subclass

TwitterApiError and TwitterClientError instances (exepct for access token retrieval errors) provide a informative error description which includes the resource URL and request method used (very handy when tracking errors in logs), also available is the following:

exception.request_method    # HTTP method used to make the request (GET or POST)
exception.resource_url      # URL of the API resource called
exception.status_code       # HTTP status code returned by Twitter
exception.error_code        # error code returned by Twitter
exception.headers           # dictionary containing response HTTP headers

Customize and extend through subclassing

birdy was built with subclassing in mind, if you wish to change the way it works, all you have to do is subclass one of the clients and override some methods and you are good to go.

Subclassing a client and then using the subclass instance in your codeis actually the recommended way of using birdy.

For example, if you don't wish to use JSONObject you have to override get_json_object_hook() method.

from birdy.twitter import UserClient

class MyClient(UserClient):
    @staticmethod
    def get_json_object_hook(data):
        return data

client = MyClient(...)
response = client.api.users.show.get(screen_name='twitter')

Or maybe, if you want global error handling for common errors, just override handle_response() method.

class MyClient(UserClient):
    def handle_response(self, method, response):
        try:
            response = super(MyClient, self).handle_response(method, response)
        except TwitterApiError, e:
            ...
            # Your error handling code
            ...
        return response

Another use of subclassing is configuration of requests.Session instance (docs) used to make HTTP requests, to configure it, you override the configure_oauth_session() method.

class MyClient(UserClient):
    def configure_oauth_session(self, session):
        session = super(MyClient, self).configure_oauth_session(session)
        session.proxies = {'http': 'foo.bar:3128'}
    return session

Do you accept contributions and feature requests?

Yes, both contributions (including feedback) and feature requests are welcome, the proper way in both cases is to first open an issue on GitHub and we will take if from there.

Keep in mind that I work on this project on my free time, so I might not be able to respond right way.

Credits

birdy would not exists if not for the excellent requests and requests-oauthlib libraries and the wonderful Python programing language.

Question, comments, ...

If you need to contact me, you can find me on Twitter (@sect2k).

Comments
  • Streaming API behind

    Streaming API behind

    It seems that the stream() is 1 post 'behind', is this supposed to be the case?

    example: response = client.stream.statuses.filter.post(track='@someperson') for data in response.stream(): print data

    if you now create a tweet on twitter: @someperson testing 1

    nothing will print if you now create a second tweet on twitter: @someperson testing 2

    the data for the first tweet will now print out (@someperson testing 1)

    if you create a third tweet: @someperson testing 3

    the data for the second tweet will now print out (@someperson testing2)

    opened by ghost 15
  • __repr__ override in JSONObject makes JSONObject non-dictlike

    __repr__ override in JSONObject makes JSONObject non-dictlike

    JSONObject is not handled correctly by things like the pretty printer because of the repr override. This override seems unnecessary to me.

    My general preference would be not to use JSONObject at all, but just to keep things simple by having the response handler return plain dictionaries. Barring that, it would be nice if the JSONObject was pretty printable by not overriding the repr.

    opened by scott2b 9
  • Streaming User returns no data

    Streaming User returns no data

    Trying out the following and I'm getting no data returned from stream(). It justs blocks indefinitely.

    I'm using Python 3.5.2 on Windows 10.

    client = StreamClient(CONSUMER_KEY,
                        CONSUMER_SECRET,
                        ACCESS_TOKEN,
                        ACCESS_TOKEN_SECRET)
    
    resource = client.userstream.user.get()
    print(resource)
    for data in resource.stream():
       print(data)
    
    opened by woakesd 2
  • streaming api does not return

    streaming api does not return

    Call to streaming api gets stuck and doesn't ever return. While others work.

    from birdy.twitter import UserClient
    
    CONSUMER_KEY = 'xxxx'
    CONSUMER_SECRET = 'xxxx'
    ACCESS_TOKEN = 'xxxxxxx'
    ACCESS_TOKEN_SECRET = 'xxxx'
    
    client = UserClient(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
    response = client.stream.statuses.filter.post(track='python')
    
    print "doesn't reach here"
    
    for data in response.stream():
        print data
    
    opened by bhanuvrat 2
  • POST https://api.twitter.com/1.1/friendships/create.json error

    POST https://api.twitter.com/1.1/friendships/create.json error

    I'm attempting to follow other users from the UserClient but i'm getting: birdy.twitter.TwitterAuthError: An unknown error has occured processing your request

    I'm attempting to do this with:

    u = user.api.friendships.create.post(screen_name='twitter',follow=True)

    What am I doing wrong?

    opened by sventhondyke 2
  • requests releasing streaming connections to the pool, causing multiple reconnects

    requests releasing streaming connections to the pool, causing multiple reconnects

    Account was cutoff and authentication disabled by Twitter trying to use the StreamClient.

    Birdy successful creates a single instance of the StreamClient, and I get the response via this: response = client.stream.statuses.filter.post(follow=users)

    and then iterate the response:

    for r in response.stream():
        ...
    

    So, I would expect a single HTTP connection to happen. But, looking through my logs, I see a lot of these:

    INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1)

    It seems to me that there should not be a bunch of new HTTP connections. But looking at the requests docs, here: http://docs.python-requests.org/en/latest/user/advanced/ it looks like it is expected behavior to release a streaming connection back to the pool once the data is read off of the stream. So what I see is a handful of responses (tweets) followed by a reconnect, infinitely until Twitter cuts me off.

    I don't see a way to tell requests to hold onto the connection rather than release it to the pool, so I'm not sure what is to be done about this.

    opened by scott2b 2
  • How to search tweets?

    How to search tweets?

    May I know how to search tweets using the following API method

    https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets.html

    opened by drhanlau 1
  • Public stream does not respond with any data

    Public stream does not respond with any data

    I'm trying a simple example for streaming API but I don't see anything but a blank screen and no output. client = UserClient(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

    response = client.stream.statuses.filter.post(track='love')

    for data in response.stream(): print(data)

    opened by DipanshuJuneja 1
  • Updated setup.py for python2 install error

    Updated setup.py for python2 install error

    Collecting birdy Downloading birdy-0.3.1.tar.gz Complete output from command python setup.py egg_info: warning: pypandoc module not found, could not convert Markdown to RST Traceback (most recent call last): File "", line 1, in File "/private/tmp/pip-build-tEUbg6/birdy/setup.py", line 41, in long_description = read_md('README.md'), File "/private/tmp/pip-build-tEUbg6/birdy/setup.py", line 14, in read_md return open(f, 'r', encoding='utf-8').read() TypeError: 'encoding' is an invalid keyword argument for this function

    opened by shashfrankenstien 1
  • Added on the fly README conversion for PyPI

    Added on the fly README conversion for PyPI

    Nice snippet I use with success in my projects, that converts Markdown written README to PyPI's rst format. Just remember to have pypandoc installed when building new package version.

    opened by pawelad 1
  • Add supported Python version to README and setupy.py

    Add supported Python version to README and setupy.py

    Does birdy support Python 3? I looked through README and setup.py and couldn't find any answer, other then Next release (0.3) will focus on Python 3 support which created more questions (what release is this? it's not in GitHub releases, let's go to PyPI, etc.)

    No matter the answer though (which I still don't 100% know ; ), it should be stated somewhere, ideally both in README and in setupy.py classifiers.

    opened by pawelad 1
  • docs: Fix a few typos

    docs: Fix a few typos

    There are small typos in:

    • README.md

    Fixes:

    • Should read except rather than exepct.
    • Should read oauthlib rather than ouathlib.

    Semi-automated pull request generated by https://github.com/timgates42/meticulous/blob/master/docs/NOTE.md

    opened by timgates42 0
  • Need help for /1.1/users/lookup request

    Need help for /1.1/users/lookup request

    I am trying to do request on users/lookup route of Twitter API using params of Email/Phone. In the result I am getting error Could not authenticate you. in response

    While using the same credentials when i do request for screen_name, user_id lookup I am getting perfect Response. Not only that all other api requests like home_timeline, followers_ids, friends_ids, etc are working fine with same credentials but on that specific request, I am getting this error. My Request is:

    import requests
    
    url = "https://api.twitter.com/1.1/users/lookup.json?map=true&phone=1234567890"
    
    payload = {}
    
    headers = {
        'Authorization': 'OAuth realm="http%3A%2F%2Fapi.twitter.com",oauth_consumer_key="<consumer_key>",oauth_token="<outh_token>",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1595318479",oauth_nonce="<nonce>",oauth_version="1.0",oauth_signature="<sig>"'
    }
    
    response = requests.request("GET", url, headers=headers, data = payload)
    print(response.text.encode('utf8'))
    

    In Response I am getting this Error:

    {
        "errors": [
            {
                "code": 32,
                "message": "Could not authenticate you."
            }
        ]
    }
    

    What are the reasons and suggestions on this ? Will appreciate the positive feedback.

    opened by msohaibali 0
  • Update README.md

    Update README.md

    Fixed some typos. Made some sections read easier by breaking up long sentences into shorter sentences. Made some sentences single lines rather than multiple lines.

    opened by edjw 0
  • Example of post request with JSON parameters?

    Example of post request with JSON parameters?

    In the POST example for a static update, the payload consists of simple key-value pairs:

    status='Hello @pybirdy!'

    I've successfully created a direct message using the deprecated direct_messages/new endpoint:

    response = self.client.api.direct_messages.new.post(screen_name='cherdt', text='hello there')

    I'm unclear how to accomplish the same using the direct_messages/events/new endpoint (https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/new-event).

    I've tried creating a string representing the JSON, but I've been getting the following error:

    birdy.twitter.TwitterApiError: Unable to decode JSON response.

    Here's an example request I tried that produced the error:

    response = self.client.api.direct_messages.events.new.post(event='{"type": "message_create", "message_create": {"target": {"recipient_id": "6039192"}, "message_data": {"text": "hi there"}}}')

    opened by cherdt 2
Owner
Inueni
Inueni
Actively maintained, pure Python wrapper for the Twitter API. Supports both normal and streaming Twitter APIs.

Twython Twython is a Python library providing an easy way to access Twitter data. Supports Python 3. It's been battle tested by companies, educational

Ryan McGrath 1.9k Jan 2, 2023
twitter bot tha uses tweepy library class to connect to TWITTER API

TWITTER-BOT-tweepy- twitter bot that uses tweepy library class to connect to TWITTER API replies to mentions automatically and follows the tweet.autho

Muziwandile Nkomo 2 Jan 8, 2022
Twitter-bot - A Simple Twitter bot with python

twitterbot To use this bot, You will require API Key and Access Key. Signup at h

Bentil Shadrack 8 Nov 18, 2022
Quickly and efficiently delete your entire tweet history with the help of your Twitter archive without worrying about the pointless 3200 tweet limit imposed by Twitter.

Twitter Nuke Quickly and efficiently delete your entire tweet history with the help of your Twitter archive without worrying about the puny and pointl

Mayur Bhoi 73 Dec 12, 2022
Twitter bot that finds new friends in Twitter.

PythonTwitterBot Twitter Bot Thats Find New Friends pip install textblob pip install tweepy pip install googletrans check requirements.txt file Env

IbukiYoshida 4 Aug 11, 2021
A twitter multi-tool for OSINT on twitter accounts.

>TwitterCheckr A twitter multi-tool for OSINT on twitter accounts. Infomation TwitterCheckr also known as TCheckr is multi-tool for OSINT on twitter a

IRIS 16 Dec 23, 2022
Twitter-redesign - Twitter Redesign With Django

Twitter Redesign A project that tests Django and React knowledge through a twitt

Mark Jumba 1 Jun 1, 2022
A Python API wrapper for the Twitter API!

PyTweet PyTweet is an api wrapper made for twitter using twitter's api version 2! Installation Windows py3 -m pip install PyTweet Linux python -m pip

TheFarGG 1 Nov 19, 2022
wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3) for Python

wyscoutapi wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3). Usage Install with pip install wyscoutapi. To connect to the Wys

Ben Torvaney 11 Nov 22, 2022
Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Harun Mbaabu Mwenda 46 Sep 1, 2022
Dns-Client-Server - Dns Client Server For Python

Dns-client-server DNS Server: supporting all types of queries and replies. Shoul

Nishant Badgujar 1 Feb 15, 2022
Raphtory-client - The python client for the Raphtory project

Raphtory Client This is the python client for the Raphtory project Install via p

Raphtory 5 Apr 28, 2022
A Python wrapper around the Twitter API.

Python Twitter A Python wrapper around the Twitter API. By the Python-Twitter Developers Introduction This library provides a pure Python interface fo

Mike Taylor 3.4k Jan 1, 2023
A Python wrapper around the Twitter API.

Python Twitter A Python wrapper around the Twitter API. By the Python-Twitter Developers Introduction This library provides a pure Python interface fo

Mike Taylor 3.4k Jan 1, 2023
Python Twitter API

Python Twitter Tools The Minimalist Twitter API for Python is a Python API for Twitter, everyone's favorite Web 2.0 Facebook-style status updater for

Mike Verdone 2.9k Jan 3, 2023
Python Twitter API

Python Twitter Tools The Minimalist Twitter API for Python is a Python API for Twitter, everyone's favorite Web 2.0 Facebook-style status updater for

null 2.9k Dec 29, 2022
Python script using Twitter API to change user banner to see 100DaysOfCode process.

100DaysOfCode - Automatic Banners ??‍?? Adds a number to your twitter banner indicating the number of days you have in the #100DaysOfCode challenge Se

Ingrid Echeverri 10 Jul 6, 2022
Python script to harvest tweets with the Twitter API V2 Academic Research Product Track

Tweet harvester Python script to scrape, collect, and/or harvest tweets with the Twitter API V2 Academic Research Product Track. Important note: In or

Thomas Frissen 2 Nov 11, 2021
Drcom-pt-client - Drcom Pt version client with refresh timer

drcom-pt-client Drcom Pt version client with refresh timer Dr.com Pt版本客户端 可用于网页认

null 4 Nov 16, 2022