Library / SDK
tumblr/tumblr.js avatar
tumblr/tumblr.js

tumblr.js is the official JavaScript client for the Tumblr API v2

JavaScript client for the Tumblr API

666 stars121 forksJavaScriptApache-2.0

At a glance

What is it?
tumblr.js wraps the Tumblr API in promise-based methods for blogs, posts, likes, follows and tags, signs requests with OAuth, and can also issue arbitrary GET, POST and PUT requests against any API path. It is Apache-2.0 licensed, publishes typed declarations and ESM style exports from a CommonJS package, and the newest release is v5.0.1 from 2025-05-02 with the last push on 2026-10-08.
Who is it for?
Adopt tumblr.js if you are writing server-side JavaScript or TypeScript against the Tumblr API v2 and want named methods, typed options and OAuth signing handled for you. Do not adopt it for browser-side use, where the README says CORS rules make it a bad fit and points to JSONP endpoints instead, and note that a callback style still appears in the examples even though the callback form is called deprecated.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A named method per API endpoint, all returning promises

tumblr.js is the official JavaScript client library for the Tumblr API, and the design is one named method per API area rather than a generic request helper. The README groups them into user methods, blog methods, post methods, legacy post methods and tagged methods, and each method takes optional parameters as a JavaScript object.

The shape of the calls is consistent. A method that lists posts takes a blog name and an optional options object:

js
const response = await client.blogPosts('blogName', {
  type: 'photo',
  tag: ['multiple', 'tags', 'likethis'],
});

and because options are optional, the same call works without them:

js
const response = await client.blogPosts('blogName');

That last point is a small quality-of-life detail that saves a lot of noise in application code, since there is no need to pass an empty object when you want nothing.

The user and blog methods cover the everyday surface. The user group gets information about the authenticating user and their blogs, the dashboard, likes and followings, and it can follow and unfollow a blog and like and unlike a post. The blog group covers blog information, posts with optional filtering, the avatar URL, likes, followers, and the queue, drafts and submissions.

The return values are promises. The README says so plainly and adds that the callback form is considered deprecated and should not be used, which is worth knowing because the Example section further down the README still shows a callback style call. That example is older than the guidance around it.

Three levels of authentication, from API key to three-legged OAuth

Different API methods need different kinds of authentication, and the README is organised around that rather than around a single setup step.

The baseline is an API key, which requires registering an application. The OAuth Consumer Key is that API key, and most methods need at least this much.

Methods that require a fully signed request need OAuth tokens on top of the key, obtained by authenticating as a Tumblr user and allowing access to your application. The README gives a four step route through the OAuth applications page: visit it, click Explore API on the application you want to authorize, click Allow, which takes you to the API console, and then click Show keys, which reveals the credentials you can use to make signed requests. The README calls this the easy way to do it with your own account.

The third level is for applications you build for other people. That requires the three-legged OAuth flow, and the README links to the API help documentation for the details.

The distinction between an API key and a signed request is the one to keep straight when you are deciding which endpoints you can call. Reading public blog content generally needs the key, while anything acting as a user, such as liking a post or following a blog, needs the signed credentials and therefore a token pair.

The client constructor takes all four credential values, which is where those three levels show up in code.

Creating the client in Node.js

Installation is a single npm command:

bash
npm install --save tumblr.js

The client is created from a factory function that takes the credentials:

js
const tumblr = require('tumblr.js');
const client = tumblr.createClient({
  consumer_key: '<consumer key>',
  consumer_secret: '<consumer secret>',
  token: '<oauth token>',
  token_secret: '<oauth token secret>',
});

The README also offers a constructor form for people who prefer it, with new tumblr.Client and the same configuration.

The package metadata tells you what you are getting. The name is tumblr.js, the version is 5.0.1, and the package is CommonJS by type, with main pointing at the built library and an exports map that resolves types from a declaration file and the default entry to the compiled JavaScript. The dependencies are small and pointed: the oauth package and its types for signing, and form-data for multipart uploads, alongside the Node type definitions.

The engine requirement is Node 22 or newer with npm 10 or newer, which is a current baseline and a reminder that a v5 release of this client targets a modern Node rather than the versions the copyright line dates from.

The project describes itself as official, and that matters for one practical reason: it is maintained alongside the API it wraps, so a new endpoint or a change in the OAuth flow is more likely to be reflected here than in a community client.

The browser is explicitly out of scope

The README has a short section headed In the Browser, and it is a refusal rather than a guide. Due to CORS restrictions, it says, you are going to have a really hard time using this library in the browser. Although GET endpoints on the Tumblr API support JSONP, the library is not intended for in-browser use.

That is a useful thing to know before you plan an architecture around it, because the library is otherwise written in a way that looks browser friendly, with a CommonJS build and a browser-oriented promise API. The constraint is the API's, not the library's, since the Tumblr API does not send the cross-origin headers a browser needs for a scripted request.

The workaround the README points at is JSONP on GET endpoints, which is a different mechanism from using this library: a script tag whose response is evaluated as JavaScript. The library is not built for that.

So the deployment shape follows from the constraint. This client belongs in a server, a job runner or a build step, where the network origin does not matter and the consumer key and token secret can be kept out of the browser bundle. If you need to talk to Tumblr from a page, you need a server in the middle anyway, and that server is exactly where this client belongs.

Posts, media uploads and the legacy post methods

Creating content is three methods. createPost takes a blog name and options, editPost takes a blog name, a post id and options, and deletePost takes a blog name and a post id:

js
await client.createPost(blogName, options);
js
await client.editPost(blogName, postId, options);
js
await client.deletePost(blogName, postId);

Media uploads are handled by providing a ReadStream as the block media, which is why the form-data dependency is in the manifest:

js
await client.createPost(blogName, {
  content: [
    {
      type: 'image',
      // Node's fs module, e.g. `import fs from 'node:fs';`
      media: fs.createReadStream(new URL('./image.jpg', import.meta.url)),
      alt_text: '…',
    },
  ],
});

The stream is created from a file URL with import.meta.url, which is the modern way to reference a file next to the module, and the alt_text field is set on the same content block. Streaming rather than buffering matters for large uploads.

Alongside these there are legacy post methods, listed as deprecated in the README: createLegacyPost, editLegacyPost and reblogPost. They remain in the client for endpoints that still exist in the API, but they are marked as the older generation of post types, and new code should prefer the createPost and editPost path.

Tag queries are also exposed, in a taggedPosts method that takes a tag and optional options.

Arbitrary requests cover what the named methods do not

The library does not stop at the methods it names. The Unsupported Methods section, which is a slightly odd label for the escape hatch it describes, gives you three generic calls for anything else:

js
// GET requests
client.getRequest(apiPath, params);

// POST requests
client.postRequest(apiPath, params);

// PUT requests
client.putRequest(apiPath, params);

This is the safety net for a new endpoint or a parameter the typed methods do not expose yet, and it means a client upgrade is rarely a blocker. The cost is that you lose the named method's options handling, so you have to know the API's query parameters yourself, and the response shape is whatever the API returns rather than a documented object.

Combining the two approaches is the normal pattern in practice. Use the named methods for the calls you make often, since they carry the option conventions, and fall back to the generic request for the occasional endpoint that has no wrapper yet.

The library also groups the tagged and post surfaces, and the README points at the API documentation for the available options for every method, with the API docs at tumblr.com/docs/api/v2 as the reference for the parameter lists.

Tooling, release cadence and the shape of the repository

The repository is a TypeScript project that publishes JavaScript. The lib/ directory holds the source, there are separate tsconfig files for the library, the tests and the build graph, and the published package is CommonJS with generated type declarations. A typedoc configuration drives the docs-site script, which is how the API reference at tumblr.github.io/tumblr.js is produced.

The scripts cover the usual ground with a few details worth noting. The test script runs mocha with a 100 millisecond timeout, which is a sign that the unit tests are expected to be fast and hermetic. Coverage is measured with nyc, and there are separate integration suites, one read-only and one write suite with a 6000 millisecond timeout for the slower round trips. The lint script targets lib, test and integration with an eslint flat config, and prettier handles formatting through autoformat and format-check scripts.

Release history is steady but not fast. The repository is not archived and the last push was on 2026-10-08, but the recent versions are v5.0.1 from 2025-05-02, v5.0.0 from 2024-02-22 and v4.0.1 from 2023-09-01. The copyright notice in the README still reads 2013-2019 Tumblr, Inc., which dates the original library rather than the current maintenance.

Licensing is Apache-2.0, which is a permissive licence with the usual obligations to preserve the notice and state changes. This is a description of the terms rather than legal advice.

The honest limitation to keep in view is that this is a client for one service, and it is only as good as that service's API. A wrapper for a proprietary API inherits its authentication friction, its rate limits and its endpoint drift, and the practical cost of using it is that you take a dependency on an npm package for something you could also call over HTTP directly. What you buy is the option handling, the OAuth plumbing and the typed method signatures.

Editorial conclusion

Adopt tumblr.js if you are writing server-side JavaScript or TypeScript against the Tumblr API v2 and want named methods, typed options and OAuth signing handled for you. Do not adopt it for browser-side use, where the README says CORS rules make it a bad fit and points to JSONP endpoints instead, and note that a callback style still appears in the examples even though the callback form is called deprecated. Verify first by calling one read method against your own blog with the consumer key alone, then check the OAuth token flow before you build a write path on top of it.

Frequently asked questions

How do I install and authenticate the Tumblr JavaScript client?

Run npm install --save tumblr.js, register an application to get an OAuth consumer key, and pass the credentials to tumblr.createClient with consumer_key, consumer_secret, token and token_secret. The README explains that the consumer key is the API key, and that fully signed requests need tokens obtained by authorizing your application.

Can tumblr.js be used in the browser?

The README says no. It states that CORS restrictions make browser use very hard, that although GET endpoints on the Tumblr API support JSONP, the library is not intended for in-browser use, and that the callback form of the request methods is considered deprecated in favour of promises.

How do I upload media to a Tumblr post with tumblr.js?

Pass a ReadStream as the block media when calling createPost, with a content entry of type image, the stream and an alt_text field. The example uses fs.createReadStream with a file URL built from import.meta.url, and the form-data package is a dependency for this path.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. tumblr/tumblr.js on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tumblr-tumblr-js.svg)](https://hysenlabs.com/projects/tumblr-tumblr-js)

Was this useful?