A Python Tumblr API v2 Client

Overview

PyTumblr

Build Status

Installation

Install via pip:

$ pip install pytumblr

Install from source:

$ git clone https://github.com/tumblr/pytumblr.git
$ cd pytumblr
$ python setup.py install

Usage

Create a client

A pytumblr.TumblrRestClient is the object you'll make all of your calls to the Tumblr API through. Creating one is this easy:

client = pytumblr.TumblrRestClient(
    '<consumer_key>',
    '<consumer_secret>',
    '<oauth_token>',
    '<oauth_secret>',
)

client.info() # Grabs the current user information

Two easy ways to get your credentials to are:

  1. The built-in interactive_console.py tool (if you already have a consumer key & secret)
  2. The Tumblr API console at https://api.tumblr.com/console
  3. Get sample login code at https://api.tumblr.com/console/calls/user/info

Supported Methods

User Methods

client.info() # get information about the authenticating user
client.dashboard() # get the dashboard for the authenticating user
client.likes() # get the likes for the authenticating user
client.following() # get the blogs followed by the authenticating user

client.follow('codingjester.tumblr.com') # follow a blog
client.unfollow('codingjester.tumblr.com') # unfollow a blog

client.like(id, reblogkey) # like a post
client.unlike(id, reblogkey) # unlike a post

Blog Methods

client.blog_info(blogName) # get information about a blog
client.posts(blogName, **params) # get posts for a blog
client.avatar(blogName) # get the avatar for a blog
client.blog_likes(blogName) # get the likes on a blog
client.followers(blogName) # get the followers of a blog
client.blog_following(blogName) # get the publicly exposed blogs that [blogName] follows
client.queue(blogName) # get the queue for a given blog
client.submission(blogName) # get the submissions for a given blog

Post Methods

Creating posts

PyTumblr lets you create all of the various types that Tumblr supports. When using these types there are a few defaults that are able to be used with any post type.

The default supported types are described below.

  • state - a string, the state of the post. Supported types are published, draft, queue, private
  • tags - a list, a list of strings that you want tagged on the post. eg: ["testing", "magic", "1"]
  • tweet - a string, the string of the customized tweet you want. eg: "Man I love my mega awesome post!"
  • date - a string, the customized GMT that you want
  • format - a string, the format that your post is in. Support types are html or markdown
  • slug - a string, the slug for the url of the post you want

We'll show examples throughout of these default examples while showcasing all the specific post types.

Creating a photo post

Creating a photo post supports a bunch of different options plus the described default options * caption - a string, the user supplied caption * link - a string, the "click-through" url for the photo * source - a string, the url for the photo you want to use (use this or the data parameter) * data - a list or string, a list of filepaths or a single file path for multipart file upload

#Creates a photo post using a source URL
client.create_photo(blogName, state="published", tags=["testing", "ok"],
                    source="https://68.media.tumblr.com/b965fbb2e501610a29d80ffb6fb3e1ad/tumblr_n55vdeTse11rn1906o1_500.jpg")

#Creates a photo post using a local filepath
client.create_photo(blogName, state="queue", tags=["testing", "ok"],
                    tweet="Woah this is an incredible sweet post [URL]",
                    data="/Users/johnb/path/to/my/image.jpg")

#Creates a photoset post using several local filepaths
client.create_photo(blogName, state="draft", tags=["jb is cool"], format="markdown",
                    data=["/Users/johnb/path/to/my/image.jpg", "/Users/johnb/Pictures/kittens.jpg"],
                    caption="## Mega sweet kittens")
Creating a text post

Creating a text post supports the same options as default and just a two other parameters * title - a string, the optional title for the post. Supports markdown or html * body - a string, the body of the of the post. Supports markdown or html

#Creating a text post
client.create_text(blogName, state="published", slug="testing-text-posts", title="Testing", body="testing1 2 3 4")
Creating a quote post

Creating a quote post supports the same options as default and two other parameter * quote - a string, the full text of the qote. Supports markdown or html * source - a string, the cited source. HTML supported

#Creating a quote post
client.create_quote(blogName, state="queue", quote="I am the Walrus", source="Ringo")
Creating a link post
  • title - a string, the title of post that you want. Supports HTML entities.
  • url - a string, the url that you want to create a link post for.
  • description - a string, the desciption of the link that you have
#Create a link post
client.create_link(blogName, title="I like to search things, you should too.", url="https://duckduckgo.com",
                   description="Search is pretty cool when a duck does it.")
Creating a chat post

Creating a chat post supports the same options as default and two other parameters * title - a string, the title of the chat post * conversation - a string, the text of the conversation/chat, with diablog labels (no html)

#Create a chat post
chat = """John: Testing can be fun!
Renee: Testing is tedious and so are you.
John: Aw.
"""
client.create_chat(blogName, title="Renee just doesn't understand.", conversation=chat, tags=["renee", "testing"])
Creating an audio post

Creating an audio post allows for all default options and a has 3 other parameters. The only thing to keep in mind while dealing with audio posts is to make sure that you use the external_url parameter or data. You cannot use both at the same time. * caption - a string, the caption for your post * external_url - a string, the url of the site that hosts the audio file * data - a string, the filepath of the audio file you want to upload to Tumblr

#Creating an audio file
client.create_audio(blogName, caption="Rock out.", data="/Users/johnb/Music/my/new/sweet/album.mp3")

#lets use soundcloud!
client.create_audio(blogName, caption="Mega rock out.", external_url="https://soundcloud.com/skrillex/sets/recess")
Creating a video post

Creating a video post allows for all default options and has three other options. Like the other post types, it has some restrictions. You cannot use the embed and data parameters at the same time. * caption - a string, the caption for your post * embed - a string, the HTML embed code for the video * data - a string, the path of the file you want to upload

#Creating an upload from YouTube
client.create_video(blogName, caption="Jon Snow. Mega ridiculous sword.",
                    embed="http://www.youtube.com/watch?v=40pUYLacrj4")

#Creating a video post from local file
client.create_video(blogName, caption="testing", data="/Users/johnb/testing/ok/blah.mov")
Editing a post

Updating a post requires you knowing what type a post you're updating. You'll be able to supply to the post any of the options given above for updates.

client.edit_post(blogName, id=post_id, type="text", title="Updated")
client.edit_post(blogName, id=post_id, type="photo", data="/Users/johnb/mega/awesome.jpg")
Reblogging a Post

Reblogging a post just requires knowing the post id and the reblog key, which is supplied in the JSON of any post object.

client.reblog(blogName, id=125356, reblog_key="reblog_key")
Deleting a post

Deleting just requires that you own the post and have the post id

client.delete_post(blogName, 123456) # Deletes your post :(

A note on tags: When passing tags, as params, please pass them as a list (not a comma-separated string):

client.create_text(blogName, tags=['hello', 'world'], ...)
Getting notes for a post

In order to get the notes for a post, you need to have the post id and the blog that it is on.

data = client.notes(blogName, id='123456')

The results include a timestamp you can use to make future calls.

data = client.notes(blogName, id='123456', before_timestamp=data["_links"]["next"]["query_params"]["before_timestamp"])

Tagged Methods

# get posts with a given tag
client.tagged(tag, **params)

Using the interactive console

This client comes with a nice interactive console to run you through the OAuth process, grab your tokens (and store them for future use).

You'll need pyyaml installed to run it, but then it's just:

$ python interactive-console.py

and away you go! Tokens are stored in ~/.tumblr and are also shared by other Tumblr API clients like the Ruby client.

Running tests

The tests (and coverage reports) are run with nose, like this:

python setup.py test

Copyright and license

Copyright 2013 Tumblr, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations.

Comments
  • oauth issue

    oauth issue

    Hello,

    I received this error when i launched

    python interactive_console.py

    I did give correct "consumer key" and "consumer secret" when prompted

    I am using latest python and installed required modules it requested using python setup.py install Python 2.7.6

    Appreciate your help...

    Traceback (most recent call last): File "interactive_console.py", line 65, in tokens = new_oauth(yaml_path) File "interactive_console.py", line 32, in new_oauth print '\nPlease go here and authorize:\n%s?oauth_token=%s' % (authorize_url, request_token['oauth_token'][0]) KeyError: 'oauth_token'

    opened by srinivasuk 50
  • Unauthorized Error - Get The Number of Followers That A Blog Has

    Unauthorized Error - Get The Number of Followers That A Blog Has

    This is the JSON response, when I use this library to get the followers of a blog.

    {'meta': {'status': 401, 'msg': 'Not Authorized'}, 'response': []}

    This is what I use to call it: #client is declared elsewhere tumblr_followers = json.loads(json.dumps(client.followers('good')))

    print tumblr_followers

    I am able to use the library for getting Blog Posts, I was just wondering how I could get the total number of followers of a blog, since apparently I am not authorized to get this info? Thanks.

    Francois


    After reading some documentation, it turns out you need OAuth, I'm still relatively new to Python, how do I make an OAuth request to Tumblr? I have the OAuth Consumer Key/Secret/API Key/Secret.

    Thanks for the help in advance.

    opened by peoplecallmefrancois 12
  • Python 2/3 support for PyTumblr.

    Python 2/3 support for PyTumblr.

    I have updated the PyTumblr code to be both backwards compatible with Python 2 and forwards compatible with Python 3.

    Some notes about what I've done:

    • Since the oauth2 library isn't Python 3 compatible, I've ripped it out and replaced it with requests-oauthlib, which has cleaned up the multipart posting quite a bit.
    • I've used the future library to be the compatibility layer for both Python 2 and Python 3, hence I've added it as a requirement.
    • I have done some manual testing in both Python 2 and 3 to ensure that the major functionality is still working the way we'd expect, including file uploads.
    • It seems like HTTPretty is giving us back values as Python bytestrings, so I had to update the tests to reflect that. If this is a concern, let me know.
    • There's some issues with running the tests under 3.4 due to how HTTPretty does its mocking of sockets (see this issue for more details), so I've only added 3.3 to be run on Travis. The client itself runs fine under Python 3.4.
    • There's also a few minor PEP8 fixes that I've tossed in as well.

    I'm reading over the CLA right now. I'll try to get it sent in on Tuesday if you're are interested in ever trying to merge this. I'm happy to make updates based on any code review feedback you may have.

    opened by dianakhuang 11
  • ImportError: No module named 'helpers'

    ImportError: No module named 'helpers'

    opened by coilysiren 10
  • Add photoset_layout as an option to create_photo

    Add photoset_layout as an option to create_photo

    Even though this is not a documented feature of the Tumblr API, it works well, and this attribute is actually provided by their Posts endpoint (http://www.tumblr.com/docs/en/api/v2#posts)

    Allowing this photoset_layout option as a valid attribute to create an image post would be a nice addition since it gives full control over how photos are laid out in the template.

    opened by taylanpince 8
  • Reblogging with a photo

    Reblogging with a photo

    I've attempted to use pytumblr to reblog a post with an image however I cannot seem to do this properly.

    Noticing the reblog method doesn't accept kwargs caption, link, source, and data I added those to the accepted options in init.py. Then naturally I called reblog like: client.reblog(your_blog_name,id=i,reblog_key=key,type='photo',source=ima)

    however this results in empty reblogs that do not have the photo uploaded. additionally, I have tried manually calling client.request.post() as such: url = "/v2/blog/" + your_blog_name + ".tumblr.com/post/reblog" kwargs = {"type":"photo", "source":ima, "id":i, "reblog_key":key} print client.request.post(url, kwargs)

    with the same result.

    How would I reblog with a photo added?

    opened by PolarisScientia 8
  • client.create_photo

    client.create_photo

    I've just discovered by trial and error that when using client.create_photo method the data parameter (path to photo) must be str (not unicode). However the caption can be unicode. You should specify when you say "string" what type of string too (str or unicode). Thank you.

    opened by Danielez 7
  • Can't use type parameter in posts() function

    Can't use type parameter in posts() function

    The field type is listed as an optional argument for posts() function but the validate_params() function disallows type and raises an exception upon its inclusion, making it impossible to call with the type parameter.

    i.e., this code:

    args = {'type': 'text', 'limit':2} posts = client.posts(urls[0], **args)

    returns this error:

    C:\Users-Removed-\AppData\Local\Enthought\Canopy\User\lib\site-packages\pytumblr-0.0.5-py2.7.egg\pytumblr\helpers.pyc in validate_params(valid_options, params) 25 if disallowed_fields: 26 field_strings = ",".join(disallowed_fields) ---> 27 raise Exception("%s are not allowed fields" % field_strings) 28 29 def validate_blogname(fn):

    Exception: type are not allowed fields

    opened by Chaobunny 7
  • Installation added to readme file

    Installation added to readme file

    As an API v2 user, I encountered with this problem, I needed to install it but since there wasn't clear information about if it is available in pip I tried and saw that it is available, but with adding these tutorial lines it would be easier for the new users. It also includes the downloading and installing it via setup.py version of the installation.

    Need to be added:

    • [ ] Features
    • [ ] Dependencies
    opened by omergulen 6
  • help me

    help me

    hello sorry i'm french i can not run I have an error on the test I have my tumblr key, but I do not understand all

    EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

    ERROR: test_blogInfo (main.TumblrRestClientTest)

    Traceback (most recent call last): File "/home/pi/pytumblr/pytumblr-master/tests/test_pytumblr.py", line 16, in setUp with open('tests/tumblr_credentials.json', 'r') as f: IOError: [Errno 2] No such file or directory: 'tests/tumblr_credentials.json'

    =========-------------------------------------------------- Ran 33 tests in 0.335s

    FAILED (errors=33)

    opened by mika7700 6
  • 401 unauthorized errors despite correct credentials

    401 unauthorized errors despite correct credentials

    ( Note: could be having the same problem as #34 )

    I've copied all the credentials directly from the API explore console (where all calls complete successfully), and provided them to the Pytumblr client. However, I still receive 401 errors for any requests to the API. I have confirmed my configuration with someone at Tumblr, as well.

    opened by jakemmarsh 6
  • Neue Post Format, post type filters, and content

    Neue Post Format, post type filters, and content

    I'm attempting to get all image posts associated with a list blogs (just personal archive reasons), and I'm running into an issue with the Neue Post Format / NPF. If I'm understanding the documentation correctly, NPF should return what's effectively a JSON layout of the post, and NPF posts can be identified by having is_blocks_post_format as True.

    The post I'm having issues with is post id 186412013514. It's marked as a text type post, NPF, and contains a single image.

    The following is the line to find the post as generated by the API console:

    client.posts('vagelio', type='photo', limit=1, offset=40, reblog_info=True)
    
    And the response...
    {
      "meta": {
        "status": 200,
        "msg": "OK"
      },
      "response": {
        "blog": {
          "ask": true,
          "ask_anon": true,
          "ask_page_title": "Ask me anything",
          "asks_allow_media": true,
          "avatar": [
            {
              "width": 512,
              "height": 512,
              "url": ["https://64.media.tumblr.com/avatar_712957997181_512.png"](https://64.media.tumblr.com/avatar_712957997181_512.png)
            },
            {
              "width": 128,
              "height": 128,
              "url": ["https://64.media.tumblr.com/avatar_712957997181_128.png"](https://64.media.tumblr.com/avatar_712957997181_128.png)
            },
            {
              "width": 96,
              "height": 96,
              "url": ["https://64.media.tumblr.com/avatar_712957997181_96.png"](https://64.media.tumblr.com/avatar_712957997181_96.png)
            },
            {
              "width": 64,
              "height": 64,
              "url": ["https://64.media.tumblr.com/avatar_712957997181_64.png"](https://64.media.tumblr.com/avatar_712957997181_64.png)
            }
          ],
          "can_chat": false,
          "can_subscribe": false,
          "description": "Freelancer illustrator\nContact: [email protected]",
          "is_nsfw": false,
          "name": "vagelio",
          "posts": 154,
          "share_likes": false,
          "subscribed": false,
          "theme": {
            "header_full_width": 938,
            "header_full_height": 1364,
            "header_focus_width": 879,
            "header_focus_height": 495,
            "avatar_shape": "square",
            "background_color": "#DCDED4",
            "body_font": "Helvetica Neue",
            "header_bounds": "177,879,672,0",
            "header_image": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s938x1364/93da9b234204a90b33359be4d6a450f39df2da3f.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s938x1364/93da9b234204a90b33359be4d6a450f39df2da3f.jpg),
            "header_image_focused": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072_c0,12977,93710,49267/9163dcae16537f1de57a42dde0f6e85172e62015.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072_c0,12977,93710,49267/9163dcae16537f1de57a42dde0f6e85172e62015.jpg),
            "header_image_poster": "",
            "header_image_scaled": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072/ebce2594714645366b4aa030b5750c66d2ea76df.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072/ebce2594714645366b4aa030b5750c66d2ea76df.jpg),
            "header_stretch": true,
            "link_color": "#567080",
            "show_avatar": true,
            "show_description": true,
            "show_header_image": true,
            "show_title": true,
            "title_color": "#2E2E2E",
            "title_font": "Sans Serif",
            "title_font_weight": "bold"
          },
          "title": "Vagelio",
          "total_posts": 154,
          "updated": 1669230150,
          "url": ["https://vagelio.tumblr.com/"](https://vagelio.tumblr.com/),
          "uuid": "t:vmv8w8qw3xgtMosxZINm4Q"
        },
        "posts": [
          {
            "type": "text",
            "is_blocks_post_format": true,
            "blog_name": "vagelio",
            "blog": {
              "name": "vagelio",
              "title": "Vagelio",
              "description": "Freelancer illustrator\nContact: [email protected]",
              "url": ["https://vagelio.tumblr.com/"](https://vagelio.tumblr.com/),
              "uuid": "t:vmv8w8qw3xgtMosxZINm4Q",
              "updated": 1669230150,
              "tumblrmart_accessories": {}
            },
            "id": 186412013514,
            "id_string": "186412013514",
            "post_url": ["https://vagelio.tumblr.com/post/186412013514/argam-tiefling-rogue-part-of-a-larger-commission"](https://vagelio.tumblr.com/post/186412013514/argam-tiefling-rogue-part-of-a-larger-commission),
            "slug": "argam-tiefling-rogue-part-of-a-larger-commission",
            "date": "2019-07-20 01:32:58 GMT",
            "timestamp": 1563586378,
            "state": "published",
            "format": "html",
            "reblog_key": "xwQgX7xU",
            "tags": [
              "dungeons and dragons",
              "rpg",
              "illustration",
              "character concept",
              "dnd",
              "commission",
              "fantasy",
              "dnd character",
              "character art",
              "5e",
              "sketch",
              "sketchbook",
              "dnd 5e",
              "tiefling",
              "Rogue",
              "portrait",
              "commisionwork",
              "vagelio kaliva",
              "Vagelio"
            ],
            "short_url": ["https://tmblr.co/Z5xoYx2jd1PVA"](https://tmblr.co/Z5xoYx2jd1PVA),
            "summary": "Argam, tiefling rogue. Part of a larger commission for a kickstarter module.\nFind me on\nTumblr:...",
            "should_open_in_legacy": false,
            "recommended_source": null,
            "recommended_color": null,
            "note_count": 36,
            "title": "",
            "body": "

    Argam, tiefling rogue. Part of a larger commission for a kickstarter module.

    Find me on

    Tumblr: http://vagelio.tumblr.com/

    Facebook: https://www.facebook.com/vageliokali

    Instagram: https://www.instagram.com/vageliokal/

    Deviantart: http://vagelio.deviantart.com/

    ", "reblog": { "comment": "

    Argam, tiefling rogue. Part of a larger commission for a kickstarter module.

    Find me on

    Tumblr: http://vagelio.tumblr.com/

    Facebook: https://www.facebook.com/vageliokali

    Instagram: https://www.instagram.com/vageliokal/

    Deviantart: http://vagelio.deviantart.com/

    ", "tree_html": "" }, "trail": [ { "blog": { "name": "vagelio", "active": true, "theme": { "header_full_width": 938, "header_full_height": 1364, "header_focus_width": 879, "header_focus_height": 495, "avatar_shape": "square", "background_color": "#DCDED4", "body_font": "Helvetica Neue", "header_bounds": "177,879,672,0", "header_image": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s938x1364/93da9b234204a90b33359be4d6a450f39df2da3f.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s938x1364/93da9b234204a90b33359be4d6a450f39df2da3f.jpg), "header_image_focused": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072_c0,12977,93710,49267/9163dcae16537f1de57a42dde0f6e85172e62015.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072_c0,12977,93710,49267/9163dcae16537f1de57a42dde0f6e85172e62015.jpg), "header_image_poster": "", "header_image_scaled": ["https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072/ebce2594714645366b4aa030b5750c66d2ea76df.jpg"](https://64.media.tumblr.com/bc8d9592a2e59aa531c1732d67074363/60aceee2e7f46bf5-9d/s2048x3072/ebce2594714645366b4aa030b5750c66d2ea76df.jpg), "header_stretch": true, "link_color": "#567080", "show_avatar": true, "show_description": true, "show_header_image": true, "show_title": true, "title_color": "#2E2E2E", "title_font": "Sans Serif", "title_font_weight": "bold" }, "share_likes": false, "share_following": false, "can_be_followed": true }, "post": { "id": "186412013514" }, "content_raw": "

    Argam, tiefling rogue. Part of a larger commission for a kickstarter module.

    Find me on

    Tumblr: http://vagelio.tumblr.com/

    Facebook: https://www.facebook.com/vageliokali

    Instagram: https://www.instagram.com/vageliokal/

    Deviantart: http://vagelio.deviantart.com/

    ", "content": "

    \"image\"

    Argam, tiefling rogue. Part of a larger commission for a kickstarter module.

    Find me on

    Tumblr: http://vagelio.tumblr.com/

    Facebook: https://www.facebook.com/vageliokali

    Instagram: https://www.instagram.com/vageliokal/

    Deviantart: http://vagelio.deviantart.com/

    ", "is_current_item": true, "is_root_item": true } ], "can_like": false, "interactability_reblog": "everyone", "can_reblog": false, "can_send_in_message": true, "can_reply": false, "display_avatar": true } ], "total_posts": 149, "_links": { "next": { "href": "/v2/blog/vagelio.tumblr.com/posts/photo?type=photo&limit=1&offset=40&reblog_info=true&tumblelog=vagelio.tumblr.com&page_number=VH-0kfNfc0AU9JcdaiZtcKHeFS0_5P3WZdEnabkhmd0zbGM4dW1hN1JKbXdZelpabWZONlU4Y1lNd0xMdWxZR3JFSGpTNWpmUDdZelRxc2RVRjZOOUNabEMybUlQNiszUkVhZ2QyNURCMTNoMHY0RENmbWRIUitKQVpFdFJ3cDhWL2xLOXJLbkhkWm42YVE3SzJtTHJadzNYWkdZQW8rdThFTmUwSm81OEZ2NzZXMTE1K3pBSmVWbWVOVWc4Z3h2T0JZcDIrRFRiQnREMGg5TUhBQjk5QWtYSDZpbk1ITWt0N3EyQWsyeGt5clR5OFVrYXFSTmhzNG1WNC96cHZRdHlMRTJzM1FpUzhTNjNFK01iTjY1L1Z0RDZMU3M4RnhXRk9BSWRGNkhKM3EvN2g1S2g4R1RoYXdrekJ1KzdKT2ZHd0gwZklWOGQ2bzE3ajYvTHdIOW1PRTFJQ2tOWTdkbFlOUXJmU3JqTm5sY21OV1JnUU5LblltRDgzbjFzS2Z2S1h5eHJhRkZyTmtXNzg1RUNYVVk5bS9wR2lhYnBRN2E4ZEFMczVlUzhOZkFQdXB1K2V3elZWZz09", "method": "GET", "query_params": { "type": "photo", "limit": "1", "offset": "40", "reblog_info": "true", "tumblelog": "vagelio.tumblr.com", "page_number": "VH-0kfNfc0AU9JcdaiZtcKHeFS0_5P3WZdEnabkhmd0zbGM4dW1hN1JKbXdZelpabWZONlU4Y1lNd0xMdWxZR3JFSGpTNWpmUDdZelRxc2RVRjZOOUNabEMybUlQNiszUkVhZ2QyNURCMTNoMHY0RENmbWRIUitKQVpFdFJ3cDhWL2xLOXJLbkhkWm42YVE3SzJtTHJadzNYWkdZQW8rdThFTmUwSm81OEZ2NzZXMTE1K3pBSmVWbWVOVWc4Z3h2T0JZcDIrRFRiQnREMGg5TUhBQjk5QWtYSDZpbk1ITWt0N3EyQWsyeGt5clR5OFVrYXFSTmhzNG1WNC96cHZRdHlMRTJzM1FpUzhTNjNFK01iTjY1L1Z0RDZMU3M4RnhXRk9BSWRGNkhKM3EvN2g1S2g4R1RoYXdrekJ1KzdKT2ZHd0gwZklWOGQ2bzE3ajYvTHdIOW1PRTFJQ2tOWTdkbFlOUXJmU3JqTm5sY21OV1JnUU5LblltRDgzbjFzS2Z2S1h5eHJhRkZyTmtXNzg1RUNYVVk5bS9wR2lhYnBRN2E4ZEFMczVlUzhOZkFQdXB1K2V3elZWZz09" } } } } }

    Firstly, this post probably shouldn't be in the response I'm getting because I'm filtering for only photo type files and it's marked as text.

    Secondly, and more importantly, the content block seems to be raw HTML. I thought it was just a fluke until I opened up another random photos-like post to the same result. Adding filter=... to the function call either leaves it unchanged or cuts all of the non-text content from the post.

    My understanding is that it should look more like this:

    (Low-effort creation based on the NPF API examples.)
    {
        "content": [
            {
                "type": "image",
                "media": [
                    {
                        "type": "image/jpeg",
                        "url": "https://64.media.tumblr.com/ab6859e23877a7d3a47a12cac73f529a/0267aa057059a2a1-a2/s500x750/1758d513b10010d980864d10f7e75d72c40ee21a.jpg",
                        "width": 1280,
                        "height": 1073
                    },
                    {
                        "type": "image/jpeg",
                        "url": "https://64.media.tumblr.com/ab6859e23877a7d3a47a12cac73f529a/0267aa057059a2a1-a2/s500x750/1758d513b10010d980864d10f7e75d72c40ee21a.jpg",
                        "width": 540,
                        "height": 400
                    },
                    {
                        "type": "image/jpeg",
                        "url": "https://64.media.tumblr.com/ab6859e23877a7d3a47a12cac73f529a/0267aa057059a2a1-a2/s500x750/1758d513b10010d980864d10f7e75d72c40ee21a.jpg",
                        "width": 250,
                        "height": 150
                    }
                ],
            },
            {
                "type": "text",
                "text": "The person's social information would be here."
            },
        ],
    }
    

    Am I misunderstanding the API?

    (Oh, also the npf=true flag doesn't seem to work for the client.posts() function.)

    opened by ldv8434 1
  • Upload v0.1.1 to pypi?

    Upload v0.1.1 to pypi?

    I note there was a version bump to v0.1.1 back in March, however the latest version on PyPi is still 0.1.0 - would it be possible to get 0.1.1 released and uploaded?

    opened by snail-coupe 0
  • API returns older posts than requested

    API returns older posts than requested

    Hi, when using the tagged method with the current date's timestamp int(datetime.datetime.now().timestamp()), for certain tags (e.g., fashion), the API returns posts starting from the year 2015 while for others start at the current date (2022-07-20). This is probably more of an issue with the server rather than the API, however, it is still an issue.

    opened by daubaris 0
Releases(0.1.1)
Owner
Tumblr
Tumblr
PepeSniper is an open-source Discord Nitro auto claimer/redeemer made in python.

PepeSniper is an open-source Discord Nitro auto claimer made in python. It sure as hell is not the fastest sniper out there but it gets the job done in a timely and stable manner. It also supports ho

Unknown User 1 Dec 22, 2021
A Discord Server Cloner Which Can Clone Any Discord Server In Just Few Minutes

A Discord Server Cloner Which Can Clone Any Discord Server In Just Few Minutes.

samet 4 Jul 23, 2022
Get notifications in your Discord server of any software releases from Apple.

Apple Releases Get notifications in your Discord server of any software releases from Apple. Running To locally host your own instance, create a Disco

adam 17 Oct 22, 2022
A simple telegram bot that resolves video urls using yt-dlp

URL to Video Telegram Bot A simple telegram bot that resolves video urls using yt-dlp Copyright (C) 2021 Vítor Vasconcellos This program is free softw

Vítor 1 Nov 18, 2021
Fairstructure - Structure your data in a FAIR way using google sheets or TSVs

Fairstructure - Structure your data in a FAIR way using google sheets or TSVs. These are then converted to LinkML, and from there other formats

Linked data Modeling Language 23 Dec 01, 2022
Set of classes and tools to communicate with a Noso wallet using NosoP

NosoPy Set of classes and tools to communicate with a Noso wallet using NosoP(Noso Protocol). The data that can be retrieved consist of: Node informat

Noso Project 1 Jan 10, 2022
twtxt is a decentralised, minimalist microblogging service for hackers.

twtxt twtxt is a decentralised, minimalist microblogging service for hackers. So you want to get some thoughts out on the internet in a convenient and

buckket 1.8k Jan 09, 2023
A Multi-Tool with 30+Options.

A Multi-Tool with 30+Options.

Mervin404 15 Apr 12, 2022
A Discord bot made by QwertyIsCoding

QwertyBot QwertyBot A Discord bot made by QwertyIsCoding Explore the docs » View Demo . Report Bug . Request Feature About The Project This Discord bo

4 Oct 08, 2022
Pythonic event-processing library based on decorators

Process Events In Style This library aims to simplify the common pattern of event processing. It simplifies the process of filtering, dispatching and

Nicolas Marier 3 Sep 01, 2022
The text based version of my App Blocker that I planning on converting to GUI soon.

App-Blocker The text based version of my App Blocker that I planning on converting to GUI soon. Currently I am just uploading the appblocker.py file,

Harsh Raj 0 Sep 13, 2022
Backend.AI Client Library for Python

Backend.AI Client The official API client library for Backend.AI Usage (KeyPair mode) You should set the access key and secret key as environment vari

Lablup 10 Feb 10, 2022
Discord spam bots with multiple account support and more

Discord spam bots with multiple account support and more. PLEASE READ EVERYTHING BEFORE WRITING AN ISSUE!! Server Messages Text Image Dm Messages Text

Mr. Nobody 6 Sep 14, 2022
Example of a discord bot in Python

discordbot.py Example of a discord bot in Python Requirements Python 3.8 or higher Discord Bot Setting Up Clone this repo or download the files Rename

Debert Jamie 1 Oct 23, 2021
Unlimited Filter Bot

Unlimited Filter Bot The Orginel Owner Of This Repo Is ㅤㅤㅤㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤ An advanced Filter Bot with nearly unlimitted filters! Features Nearly unlimite

4 Aug 02, 2022
A GitHub Action that automatically reports your Advent of Code progress in a table in your README

Advent README Stars This action adds and maintains a stars report in your README based on your Advent of Code progress. Example Table 2021 Results Day

Kevin Duff 36 Dec 30, 2022
Бот для скачивания треков с Deezer используя ISRC и UPC коды

deez_robot Запуск Установите необходимые библиотеки pip install -r requirements.txt Создайте файл config.py и поместите туда токен бота и ARL-токен De

Max 4 Jul 31, 2022
Generate direct m3u playlist for all the channels subscribed in the Tata Sky portal

Tata Sky IPTV Script generator A script to generate the m3u playlist containing direct streamable file (.mpd or MPEG-DASH or DASH) based on the channe

Gaurav Thakkar 250 Jan 01, 2023
An enhanced discord.py, based off of the now-archived discord.py project

enhanced-discord.py A modern, maintained, easy to use, feature-rich, and async ready API wrapper for Discord written in Python. The Future of enhanced

Devision 2 Dec 21, 2022