Skip to main content

Unoffical Python Interface to the Jodel API (based on iOS)

Project description

Jodel iOS API
=========

|Build Status| |Python Versions| |PyPI Version| |License|

Unofficial interface to the private API of the Jodel App. Not affiliated
with *The Jodel Venture GmbH*.

Special Thanks
--------------
@nborrmann (https://github.com/nborrmann/jodel_api) who developed the main core of this API. I forked its repo and adapted the code to follow iOS app behaviour. I should have not broken compatibility with its version BUT I removed 'legacy' accounts support.


Installation
------------

Using pip:

.. code::

pip install jodel_ios_api

or using setup.py:

.. code::

git clone https://github.com/marbink/jodel_ios_api.git
cd jodel_ios_api
python setup.py install


Usage
-----

Account Creation
~~~~~~~~~~~~~~~~

Calling the bare constructor creates a new account:

.. code:: python

>>> import jodel_ios_api
>>> lat, lng, city = 48.148434, 11.567867, "Munich"
>>> jodel_ios_api.JodelAccount.secret = 'SECRET_HERE'.encode('ascii')
>>> j = jodel_ios_api.JodelAccount(lat=lat, lng=lng, city=city)
Creating new account.

``get_account_data()`` returns all data associated with this account
(censored by me):

.. code:: python

>>> j.get_account_data()
{'access_token': 'xxx', 'expiration_date': 1472660000, 'refresh_token': 'xxx', 'distinct_id': 'xxx', 'device_uid': 'xxx'}

Save this data to reuse the account later on, feed it to the
JodelAccount() constructor to reinitiate the account. This constructor
issues one request to update the location of the account.

.. code:: python

>>> j = jodel_ios_api.JodelAccount(lat=lat, lng=lng, city=city, access_token='xxx', expiration_date='xxx',
refresh_token='xxx', distinct_id='xxx', device_uid='xxx', is_legacy=True)
(204, '')

Add ``update_location=False`` to suppress this behaviour. The
constructor will only instantiate an object, without making any remote
calls:

.. code:: python

>>> j = jodel_ios_api.JodelAccount(lat=lat, lng=lng, city=city, update_location=False, **account_data)

After ``expiration_date`` has passed, call ``refresh_access_tokens()``
to re-authenticate. If ``refresh_access_token`` fails, use
``refresh_all_tokens`` instead (this is akin to creating a new account,
but preserves the account's data (karma, etc)):

.. code:: python

>>> j.refresh_access_token()
(200, {'token_type': 'bearer', 'access_token': 'xxx', 'expires_in': 604800, 'expiration_date': xxx})
>>> j.refresh_all_tokens()
(200, {'expires_in': 604800, 'access_token': 'xxx', 'token_type': 'bearer', 'returning': True,
'refresh_token': 'xxx', 'expiration_date': 1472600000, 'distinct_id': 'xxx'})


API calls
~~~~~~~~~

All remote API calls return a tuple of HTTP status\_code and the
response (if possible a dict, parsed from the API response), but might
also be a string (error message).

The following API calls are supported (presented without their
respective responses):


.. code:: python

# API methods for reading posts:
>>> j.get_posts_recent(skip=0, limit=60, after=None, mine=False, hashtag=None, channel=None)
>>> j.get_posts_popular(skip=0, limit=60, after=None, mine=False, hashtag=None, channel=None)
>>> j.get_posts_discussed(skip=0, limit=60, after=None, mine=False, hashtag=None, channel=None)
>>> j.get_pictures_recent(skip=0, limit=60, after=None)
>>> j.get_pictures_popular(skip=0, limit=60, after=None)
>>> j.get_pictures_discussed(skip=0, limit=60, after=None)
>>> j.get_my_pinned_posts(skip=0, limit=60, after=None)
>>> j.get_my_replied_posts(skip=0, limit=60, after=None)
>>> j.get_my_voted_posts(skip=0, limit=60, after=None)
>>> j.post_search(message, skip=0, limit=60)

# API methods for interacting with single posts:
>>> j.create_post(message=None, imgpath=None, b64img=None, color=None, ancestor=None, channel="")
>>> j.get_post_details(post_id) # This endpoint has been deprecated. Use get_post_details_v3.
>>> # This api endpoint implements paging and returns at most 50 replies,
>>> # use the skip parameter to page through the thread:
>>> j.get_post_details_v3(post_id, skip=0)
>>> j.upvote(post_id)
>>> j.downvote(post_id)
>>> j.give_thanks(post_id)
>>> j.get_share_url(post_id)
>>> j.pin(post_id)
>>> j.unpin(post_id)
>>> j.enable_notifications(post_id)
>>> j.disable_notifications(post_id)
>>> j.delete_post(post_id) # Only works on your own posts ಠ_ಠ

# API methods for interacting with sticky posts:
>>> j.upvote_sticky_post(post_id)
>>> j.downvote_sticky_post(post_id)
>>> j.dismiss_sticky_post(post_id)

# API methods for interacting with notifications:
>>> j.get_notifications()
>>> j.get_notifications_new()
>>> j.notification_read(post_id=None, notification_id=None)

# API methods for interacting with channels:
>>> j.get_recommended_channels()
>>> j.get_channel_meta(channel)
>>> j.follow_channel(channel)
>>> j.unfollow_channel(channel)

# API methods for interacting with your user profile:
>>> j.set_location(lat, lng, city, country=None, name=None) # country and name appear to have no effect
>>> j.set_user_profile(user_type=None, gender=None, age=None)
>>> j.get_user_config()
>>> j.get_karma()
>>> j.get_captcha()
>>> j.submit_captcha(key, answer)


The parameters ``skip``,
``limit`` and ``after`` implement paging. While ``skip`` and ``limit``
are integers, ``after`` is a ``post_id`` parameter and will return all
jodels that follow that one. The former two paramters seem to be
deprecated in favor of the latter, however ``after`` doesn't work
on all ``/mine/`` endpoints (ie. ``mine=True`` or ``get_my_x_posts``).

The arguments ``mine`` (boolean), ``hashtag``, ``channel`` (both strings)
are exclusive. If ``mine`` evaluates to ``true``, the other two arguments
are discarded, if ``hashtag`` evaluates ``true`` , ``channel`` is
discarded.

``post_search()`` is a new endpoint (as of June 17) that isn't yet
available through the app. It returns all posts from your location
that contain a given string.

You can pass additional arguments (such as proxies and timeouts) to all
API calls through the ``**xargs`` argument that will be passed to the
``requests.request()`` function:

.. code:: python

>>> j.upvote(post_id, timeout=5, proxies={'https': '127.0.0.1:5000'})

For unimplemented endpoints, check `issue #22
<https://github.com/nborrmann/jodel_api/issues/22/>`_.


Error Codes
~~~~~~~~~~~

- **401 "Unauthorized"**: Your ``access_token`` is invalid. Either
you messed up, or it is outdated. You need to call
``refresh_access_token()`` or ``refresh_all_token()`` (check the
above section on account creation).
- **401 "Action not allowed"**: You are using a ``4.48`` account
with ``is_legacy=True``, but ``4.48`` accounts are not allowed
to downgrade.
- **403 "Access Denied"**: Your IP is banned accross endpoints,
just read-only endpoints still work. Effective for 24 hours.
- **429 "Too Many Requests"**: Your IP is rate-limited. Applies only
to one specific endpoint.
- **477 "Signed Request Expected"**: This library should handle request
signing. Make sure to upgrade to the latest version of ``jodel_ios_api``,
as the signing key changes every few weeks.
- **478 "Account not verified"**: Verify the account through GCM.
- **502 "Bad Gateway"**: Something went wrong server-side. This happens
pretty randomly. ``jodel_ios_api`` automatically retries two times when
it sees this error. If you encounter this status, the jodel servers
are probably having issues. Try again later.

Rate-Limits
~~~~~~~~~~~

The Jodel API appears to have the following (IP-based) rate-limits

- max of 200 new account registrations from one IP per half hour
- max of 200 votes per minute
- max of 100 captcha requests per minute

They also hand out 403 bans if you overdo it.


.. code:: python

python setup.py test

.. |Build Status| image:: https://travis-ci.org/marbink/jodel_ios_api.svg?branch=master
:target: https://travis-ci.org/marbink/jodel_ios_api
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/jodel_ios_api.svg
:target: https://pypi.python.org/pypi/jodel_ios_api/
.. |PyPI Version| image:: https://img.shields.io/pypi/v/jodel_ios_api.svg
:target: https://pypi.python.org/pypi/jodel_ios_api/
.. |License| image:: https://img.shields.io/pypi/l/jodel_ios_api.svg
:target: https://pypi.python.org/pypi/jodel_ios_api/


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jodel_ios_api-1.0.2.tar.gz (9.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

jodel_ios_api-1.0.2-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

jodel_ios_api-1.0.2-py2-none-any.whl (9.5 kB view details)

Uploaded Python 2

File details

Details for the file jodel_ios_api-1.0.2.tar.gz.

File metadata

  • Download URL: jodel_ios_api-1.0.2.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.3

File hashes

Hashes for jodel_ios_api-1.0.2.tar.gz
Algorithm Hash digest
SHA256 d710c191ecfd485168587b268f77f3e9a526ba232d7f6c53af6470bcca1c877c
MD5 c66e1849a3573aef10857e28901c04fb
BLAKE2b-256 f7027c036aa0955bce2960c4872d2d9dd71dea0eb1d64ec98609c6a6b208ccc4

See more details on using hashes here.

File details

Details for the file jodel_ios_api-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: jodel_ios_api-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.3

File hashes

Hashes for jodel_ios_api-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e5ec521a21a94ee5e8cdb0d65b5fb69d6bffb1cbcad644471e78d25bc7c7d2b8
MD5 e2d0600d09f5486da5599731821af1e1
BLAKE2b-256 dd9d2c535afbe7bcdb6d5b521441656140d5ebec6acf48118e66a8de0212f72d

See more details on using hashes here.

File details

Details for the file jodel_ios_api-1.0.2-py2-none-any.whl.

File metadata

  • Download URL: jodel_ios_api-1.0.2-py2-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 2
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/2.7.14

File hashes

Hashes for jodel_ios_api-1.0.2-py2-none-any.whl
Algorithm Hash digest
SHA256 14a7562c243684c0c2a0616ec33b96d0b72cb50627974eb3debc530d1bd3de7f
MD5 af4e58660566d0944b15aae1b5208e10
BLAKE2b-256 02b5548642bba737ea31348559ce0de417262fe443bc2e9d9beb4770325d0c05

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page